C#上位机与欧姆龙PLC的通信10----开发专用的通讯工具软件(WPF版)

发布时间:2024年01月05日

1、介绍

上节开发了一个winform版的通讯测试工具,这节再搞个wpf版的,wpf是什么?请自行百度,也可以看前面的博客,WPF真入门教程,wpf的界面效果是比winform漂亮,因为wpf使用了web项目中的css样式来美化界面,在这个例子中用到wpf的控件,资源样式,命令绑定等,采用的是mvvm的渲染模式,界面如图:

前面的winform界面:

?2、开工干

2.1 创建wpf项目

2.2 创建目录及PLC模型类对象

?2.3 创建自定义的消息弹窗

MsgBoxWindow.xaml代码

<Window x:Class="OmronFinsWPFApp.MsgBoxWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:OmronFinsWPFApp"
        mc:Ignorable="d"
                Title="消息框" Height="200" Width="420" WindowStartupLocation="CenterScreen" BorderThickness="1,0,1,1" ResizeMode="NoResize" WindowStyle="None" AllowsTransparency="True" Background="Transparent">
    <Window.Resources>
        <Style TargetType="Button" x:Key="msgbtnStyle">
            <Setter Property="Width" Value="60"/>
            <Setter Property="Height" Value="25"/>
            <Setter Property="Margin" Value="10,0,15,0"/>
        </Style>
    </Window.Resources>
    <Grid Background="Transparent" MouseLeftButtonDown="Grid_MouseLeftButtonDown" >
        <Border BorderBrush="#FFA9AEB1"  BorderThickness="2" CornerRadius="10" Margin="5">
            <Border.Effect>
                <DropShadowEffect BlurRadius="10" Color="#FFB8BBC8" Direction="300" ShadowDepth="2"/>
            </Border.Effect>
            <Border.Background>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#FFFBFBFB" Offset="0.16"/>
                    <GradientStop Color="#FFB4D8E2" Offset="0.986"/>
                </LinearGradientBrush>
            </Border.Background>
            <Grid x:Name="grid" Margin="5">
                <Grid.RowDefinitions>
                    <RowDefinition Height="30"/>
                    <RowDefinition/>
                    <RowDefinition Height="50" />
                </Grid.RowDefinitions>
                <Grid Grid.Row="0" Background="Transparent">
                    <Border  Background="Transparent"  >
                        <Grid>
                            <TextBlock Text="{Binding MessageTitle}" HorizontalAlignment="Left"  Height="25" Width="200" VerticalAlignment="Center" Margin="5,0" />
                            <Image Source="imgs/errbtn.jpg" HorizontalAlignment="Right" Margin="0,3,3,2"  MouseLeftButtonDown="Image_MouseLeftButtonDown"/>
                        </Grid>
                    </Border>
                </Grid>
                <!--显示图片和文本-->
                <StackPanel Grid.Row="1" VerticalAlignment="Center" Orientation="Horizontal">
                    <Image Source="{Binding ImagePath}" Width="30" Height="30" Margin="40,20,20,20"/>
                    <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" TextWrapping="WrapWithOverflow" Width="280" TextAlignment="Left"
                       Text="{Binding MessageBoxText}" FontSize="12"/>
                </StackPanel>
                <!--Button Margin(坐上右下)-->
                <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
                    <Button Content="确 定" Style="{StaticResource msgbtnStyle}" x:Name="OkButton"   IsDefault="True"
                    Visibility="{Binding OkButtonVisibility,Mode=OneWay}" Click="OkButton_Click"/>
                    <Button Content="是" Style="{StaticResource msgbtnStyle}"  x:Name="YesButton"  
                    Visibility="{Binding YesButtonVisibility,Mode=OneWay}" Click="YesButton_Click"/>
                    <Button Content="否" Style="{StaticResource msgbtnStyle}"  x:Name="NoButton" 
                    Visibility="{Binding NoButtonVisibility,Mode=OneWay}" Click="NoButton_Click"/>
                    <Button Content="取消" Style="{StaticResource msgbtnStyle}"  x:Name="CancelButton" 
                    Visibility="{Binding CancelButtonVisibility}" Click="CancelButton_Click"/>
                </StackPanel>
            </Grid>
        </Border>
    </Grid>
</Window>

?MsgBoxWindow.xaml.cs代码(这是页面MsgBoxWindow.xaml的后台逻辑代码)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace OmronFinsWPFApp
{
    /// <summary>
    /// MsgBoxWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MsgBoxWindow : Window
    {
        public MsgBoxWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            //所有按钮不显示()
            OkButtonVisibility = Visibility.Collapsed;
            CancelButtonVisibility = Visibility.Collapsed;
            YesButtonVisibility = Visibility.Collapsed;
            NoButtonVisibility = Visibility.Collapsed;
            Result = CustomMessageBoxResult.None;
        }

        /// <summary>
        /// 显示按钮类型
        /// </summary>
        public enum CustomMessageBoxButton
        {
            OK = 1,
            OKCancel = 2,
            YesNo = 3,
            YesNoCancel = 4
        }

        /// <summary>
        /// 消息框返回值
        /// </summary>
        public enum CustomMessageBoxResult
        {
            None = 0,//用户直接关闭消息框
            OK = 1,//用户点击了确定按钮
            Cancel = 2,//用户点击了取消按钮
            Yes = 3,//用户点击了是按钮
            No = 4//用户点击了否按钮
        }

        /// <summary>
        /// 图标类型
        /// </summary>
        public enum CustomMessageBoxIcon
        {
            None = 0,
            Error = 1,
            Question = 2,
            Infomation = 3
        }

        #region 页面属性定义
        /// <summary>
        /// 消息文本
        /// </summary>
        public string MessageBoxText { get; set; }
        /// <summary>
        /// 消息框标题
        /// </summary>
        public string MessageTitle { get; set; }
        /// <summary>
        /// 图标路径
        /// </summary>
        public string ImagePath { get; set; }
        /// <summary>
        /// 显示确定
        /// </summary>
        public Visibility OkButtonVisibility { get; set; }
        /// <summary>
        /// 显示取消
        /// </summary>
        public Visibility CancelButtonVisibility { get; set; }
        /// <summary>
        /// 显示是
        /// </summary>
        public Visibility YesButtonVisibility { get; set; }
        /// <summary>
        /// 显示否
        /// </summary>
        public Visibility NoButtonVisibility { get; set; }
        /// <summary>
        /// 消息框返回值
        /// </summary>
        public CustomMessageBoxResult Result { get; set; }
        #endregion

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            Result = CustomMessageBoxResult.Cancel;
            this.Close();
        }

        private void NoButton_Click(object sender, RoutedEventArgs e)
        {
            Result = CustomMessageBoxResult.No;
            this.Close();
        }

        private void YesButton_Click(object sender, RoutedEventArgs e)
        {
            Result = CustomMessageBoxResult.Yes;
            this.Close();
        }

        private void OkButton_Click(object sender, RoutedEventArgs e)
        {
            Result = CustomMessageBoxResult.OK;
            this.Close();
        }

        private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// 消息框拖动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.DragMove();
        }
        /// <summary>
        /// 显示消息框
        /// </summary>
        /// <param name="msgText"></param>
        /// <param name="title"></param>
        /// <param name="msgBtn"></param>
        /// <param name="msgIcon"></param>
        /// <returns></returns>
        public static CustomMessageBoxResult Show(string msgText, string title, CustomMessageBoxButton msgBtn, CustomMessageBoxIcon msgIcon)
        {
            MsgBoxWindow msg = new MsgBoxWindow();
            msg.Topmost = true;
            msg.MessageBoxText = msgText;
            msg.MessageTitle = title;
            //消息框按钮显示
            switch (msgBtn)
            {
                case CustomMessageBoxButton.OK:
                    msg.OkButtonVisibility = Visibility.Visible;
                    break;
                case CustomMessageBoxButton.OKCancel:
                    msg.OkButtonVisibility = Visibility.Visible;
                    msg.CancelButtonVisibility = Visibility.Visible;
                    break;
                case CustomMessageBoxButton.YesNo:
                    msg.YesButtonVisibility = Visibility.Visible;
                    msg.NoButtonVisibility = Visibility.Visible;
                    break;
                case CustomMessageBoxButton.YesNoCancel:
                    msg.YesButtonVisibility = Visibility.Visible;
                    msg.NoButtonVisibility = Visibility.Visible;
                    msg.CancelButtonVisibility = Visibility.Visible;
                    break;
                default:
                    msg.OkButtonVisibility = Visibility.Visible;
                    break;
            }
            switch (msgIcon)
            {
                case CustomMessageBoxIcon.Infomation:
                    msg.ImagePath = @"imgs/success.jpg";
                    break;
                case CustomMessageBoxIcon.Error:
                    msg.ImagePath = @"imgs/error.jpg";
                    break;
                case CustomMessageBoxIcon.Question:
                    msg.ImagePath = @"imgs/question.jpg";
                    break;
            }
            msg.ShowDialog();
            return msg.Result;
        } 
    }
}

2.4 添加之前的通讯库dll文件,并引用到项目中

2.5 添加目录及命令类RelayCommand

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace OmronFinsWPFApp.Common
{
    /// <summary>
    /// 命令实现类
    /// </summary>
    public class RelayCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;
        /// <summary>
        /// 要执行的操作
        /// </summary>
        private Action<object> executeActions;
        /// <summary>
        /// 是否可以执行的委托
        /// </summary>
        private Func<object, bool> canExecuteFunc;
        /// <summary>
        /// 构造函数 无参构造
        /// </summary>
        public RelayCommand() { }
        /// <summary>
        /// 通过执行的委托构造
        /// </summary>
        /// <param name="execute"></param>
        public RelayCommand(Action<object> execute) : this(execute, null)
        {

        }
        /// <summary>
        /// 通过执行的操作与是否可执行的委托
        /// </summary>
        /// <param name="execute">要执行的操作</param>
        /// <param name="canExecute">是否可以被执行</param>
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
        {
            this.executeActions = execute;
            this.canExecuteFunc = canExecute;
        }

        /// <summary>
        /// 命令是否可以执行
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            if (canExecuteFunc != null)
                return this.canExecuteFunc(parameter);
            else
                return true;
        }

        /// <summary>
        /// 要执行的操作
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            if (executeActions == null)
                return;
            this.executeActions(parameter);
        }

        /// <summary>
        /// 执行CanExecuteChanged事件
        /// </summary>
        public void OnCanExecuteChanged()
        {
            this.CanExecuteChanged?.Invoke(this, new EventArgs());
        }
    }
}

2.6 添加视图模型类对象

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using static OmronFinsWPFApp.MsgBoxWindow;

namespace OmronFinsWPFApp.ViewModel
{
    /// <summary>
    /// 视图模型基类
    /// </summary>
    public class ViewModelBase : INotifyPropertyChanged
    {
        /// <summary>
        /// 属性值发生更改时触发
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;



        /// <summary>
        /// 执行更改
        /// C#5.0中的新特性CallerMemberName
        /// </summary>
        /// <param name="propertyName"></param>
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }


        /// <summary>
        /// 成功消息提示
        /// </summary>
        /// <param name="message"></param>
        /// <param name="title"></param>
        /// <param name="buttons"></param>
        public void ShowMessage(string message, string title = "提示", CustomMessageBoxButton buttons = CustomMessageBoxButton.OK)
        {
            Show(message, title, buttons, CustomMessageBoxIcon.Infomation);
        }

        /// <summary>
        /// 错误消息框
        /// </summary>
        /// <param name="message"></param>
        /// <param name="title"></param>
        /// <param name="buttons"></param>
        public void ShowError(string message, string title = "错误", CustomMessageBoxButton buttons = CustomMessageBoxButton.OK)
        {
            Show(message, title, buttons, CustomMessageBoxIcon.Error);
        }

        /// <summary>
        /// 询问消息框
        /// </summary>
        /// <param name="message"></param>
        /// <param name="title"></param>
        /// <param name="buttons"></param>
        /// <returns></returns>
        public CustomMessageBoxResult ShowQuestion(string message, string title = "询问", CustomMessageBoxButton buttons = CustomMessageBoxButton.OKCancel)
        {
            return Show(message, title, buttons, CustomMessageBoxIcon.Question);
        }



    }
}

?

using Omron.Communimcation.Fins.Omron;
using OmronFinsWPFApp.Common;
using OmronFinsWPFApp.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace OmronFinsWPFApp.ViewModel
{
    /// <summary>
    /// 视图模型
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        /// <summary>
        /// finstcp对象
        /// </summary>
        FinsTcp finsTcp; 
        

        PLCMemoryModel readPLCModel = SetInitModel();
        /// <summary>
        /// 读取PLC
        /// </summary>
        public PLCMemoryModel ReadPLCModel
        {
            get { return readPLCModel; }
            set
            {
                readPLCModel = ReadPLCModel;
                OnPropertyChanged();//属性通知
            }
        }

        PLCMemoryModel writePLCModel = SetInitModel();
        /// <summary>
        /// 写入PLC
        /// </summary>
        public PLCMemoryModel WritePLCModel
        {
            get { return writePLCModel; }
            set
            {
                writePLCModel = WritePLCModel;
                OnPropertyChanged();
            }
        }
        /// <summary>
        /// 初始化页面参数
        /// </summary>
        /// <returns></returns>
        private static PLCMemoryModel SetInitModel()
        {
            PLCMemoryModel model = new PLCMemoryModel();
            model.Area = "DM";
            model.DataType = "ushort";
            model.Address = "";
            model.Count = "1";
            return model;
        }

        private string hostName = "192.168.1.4";
        /// <summary>
        /// PLC地址
        /// </summary>
        public string HostName
        {
            get
            {
                return hostName;
            }
            set
            {
                hostName = value;
            }
        }

        private string hostPort = "7788";
        /// <summary>
        /// PLC端口
        /// </summary>
        public string HostPort
        {
            get { return hostPort; }
            set
            {
                hostPort = value;
            }
        }


        /// <summary>
        /// 存储区下拉框数据源
        /// </summary>
        public List<string> CboCustTypes
        {
            get
            {
                return new List<string>() { "DM", "H", "W", "CIO" };
            }
        }

        /// <summary>
        /// 数据类型
        /// </summary>
        public List<string> CboCustDatas
        {
            get
            {
                return new List<string>() { "ushort", "short", "float", "bool" };
            }
        } 

        private string readWords = "";
        /// <summary>
        /// 读数结果
        /// </summary>
        public string ReadWords
        {
            get { return readWords; }
            set
            {
                readWords = value;
                OnPropertyChanged();
            }
        }
        private string writeWords = "";
        /// <summary>
        /// 写入数据
        /// </summary>
        public string WriteWords
        {
            get { return writeWords; }
            set
            {
                writeWords = value;
                OnPropertyChanged();
            }
        }

        private string connectWords = "当前未连接";
        /// <summary>
        /// 连接状态
        /// </summary>
        public string ConnectWords
        {
            get { return connectWords; }
            set
            {
                connectWords = value;
                OnPropertyChanged();
            }
        }
         

        /// </summary>
        /// <summary>
        /// 登录按钮的命令处理
        /// </summary>
        public ICommand LoginCommand
        {
            get
            {
                return new RelayCommand(o =>
                {

                    var name = HostName;
                    var port = HostPort;
                    finsTcp = new FinsTcp(name, Convert.ToInt32(port), 10, 04);// 创建连接 
                    var result = finsTcp.Connect();// 开始连接PLC
                    if (!result.IsSuccessed)
                    {
                        ShowError(result.Message, "错误");
                        return;
                    }
                    ShowMessage("PLC连接成功", "提示");
                    ConnectWords = "PLC连接成功";
                });
            }
        }

        /// </summary>
        /// <summary>
        /// 读取按钮的命令处理
        /// </summary>
        public ICommand ReadCommand
        {
            get
            {
                return new RelayCommand(o =>
                {
                    var d = ReadPLCModel; 
                    //内存地址
                    string plcAddr = ReadPLCModel.Area + ReadPLCModel.Address;
                    //读取数量
                    ushort readCount = ushort.Parse(ReadPLCModel.Count);
                    //数据类型
                    string dataType = ReadPLCModel.DataType;
                    switch (dataType)
                    {
                        case "ushort":
                            var datas = finsTcp.Read<ushort>(plcAddr, readCount);
                            if (!datas.IsSuccessed)
                            {
                                ShowMessage(datas.Message, "提示");
                                return;
                            }
                            ReadWords = string.Join(",", datas.Datas);
                            break;
                        case "short":
                            var datas2 = finsTcp.Read<short>(plcAddr, readCount);
                            if (!datas2.IsSuccessed)
                            {
                                ShowMessage(datas2.Message, "提示");
                                return;
                            }
                            ReadWords = string.Join(",", datas2.Datas);
                            break;
                        case "float":
                            var datas3 = finsTcp.Read<float>(plcAddr, readCount);
                            if (!datas3.IsSuccessed)
                            {
                                ShowMessage(datas3.Message, "提示");
                                return;
                            }
                            ReadWords = string.Join(",", datas3.Datas);
                            break;
                        case "bool":
                            var datas4 = finsTcp.Read<bool>(plcAddr, readCount);
                            if (!datas4.IsSuccessed)
                            {
                                ShowMessage(datas4.Message, "提示");
                                return;
                            }
                            ReadWords = string.Join(",", datas4.Datas);
                            break;
                    }
                });
            }
        }


        /// </summary>
        /// <summary>
        /// 写入按钮的命令处理
        /// </summary>
        public ICommand WriteCommand
        {
            get
            {
                return new RelayCommand(o =>
                {
                    var d = WritePLCModel;
                    //内存地址
                    string plcAddr = WritePLCModel.Area + WritePLCModel.Address;
                    //写入数量 
                    ushort writeCount = ushort.Parse(WritePLCModel.Count);
                    //数据类型
                    string dataType = WritePLCModel.DataType;
                    //实际数量
                    string objWriteVals = WriteWords;
                    ushort objWCount = (ushort)objWriteVals.Split(',').Length;
                    //实际数量与要求数量不一致,不允许操作
                    if (writeCount != objWCount)
                    {
                        ShowError("写入值的数量不正确!");
                        return;
                    }
                    List<string> vals = objWriteVals.Split(',').ToList();
                    switch (dataType)
                    {
                        case "ushort":
                            //实际数值转换成list集合ushort类型
                            List<ushort> objushort = new List<ushort>();
                            vals.ForEach((x) =>
                            {
                                objushort.Add(ushort.Parse(x));
                            });
                            var finish1 = finsTcp.Write(objushort, plcAddr);
                            if (finish1.IsSuccessed)
                            {
                                ShowMessage(finish1.Message, "提示");
                            }
                            break;
                        case "short":
                            //实际数值转换成list集合 short类型
                            List<short> objshort = new List<short>();
                            vals.ForEach((x) =>
                            {
                                objshort.Add(short.Parse(x));
                            });
                            var finish2 = finsTcp.Write(objshort, plcAddr);
                            if (finish2.IsSuccessed)
                            {
                                ShowMessage(finish2.Message, "提示");
                            }
                            break;
                        case "float":
                            //实际数值转换成list集合 float
                            List<float> objfloat = new List<float>();
                            vals.ForEach((x) =>
                            {
                                objfloat.Add(float.Parse(x));
                            });
                            var finish3 = finsTcp.Write(objfloat, plcAddr);
                            if (finish3.IsSuccessed)
                            {
                                ShowMessage(finish3.Message, "提示");
                            }
                            break;
                        case "bool":
                            //实际数值转换成list集合bool
                            List<bool> objbool = new List<bool>();
                            vals.ForEach((x) =>
                            {
                                if (x == "1")
                                {
                                    objbool.Add(true);
                                }
                                else
                                {
                                    objbool.Add(false);
                                }
                            });
                            var finish4 = finsTcp.Write(objbool, plcAddr);
                            if (finish4.IsSuccessed)
                            {
                                ShowMessage(finish4.Message, "提示");
                            }
                            break;
                    }
                });
            }
        }

    }
}

2.7 主界面样式

首先添加资源文件

?

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <!--定义通用的按钮样式-->
    <Style TargetType="{x:Type Button}" x:Key="btnBaseStyle">
        <Setter Property="Height" Value="22"/>
        <Setter Property="Width" Value="60"/>
        <Setter Property="FontFamily" Value="微软雅黑"/>
        <Setter Property="Margin" Value="3,0"/>
        <Setter Property="FontSize" Value="16"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="Foreground"  Value="Blue"/>
    </Style>

    <!--TextBox默认样式-->
    <Style TargetType="{x:Type TextBox}" x:Key="txtTextBoxStyle">
        <Setter Property="Width" Value="150"/>
        <Setter Property="Height" Value="25"/>
        <Setter Property="BorderBrush" Value="#FF105190"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Margin" Value="2,0"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Background">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="White" Offset="0"/>
                    <GradientStop Color="#FFE4E4E4" Offset="1"/>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
    </Style>

    <!--TextBlock默认样式-->
    <Style TargetType="{x:Type TextBlock}" x:Key="txtTextBlockStyle">
        <Setter Property="Margin" Value="1"/>
        <Setter Property="Height" Value="15"/>
    </Style>

    <!--页面下拉框样式-->
    <LinearGradientBrush x:Key="ComboBox.Static.Background" EndPoint="0,1" StartPoint="0,0">
        <GradientStop Color="White" Offset="0"/>
        <GradientStop Color="#FFE4E4E4" Offset="1"/>
    </LinearGradientBrush>
    <SolidColorBrush x:Key="ComboBox.Static.Border" Color="#FF105190"/>

    <!--combox默认样式-->
    <Style x:Key="cboStyle" TargetType="{x:Type ComboBox}">
        <Setter Property="Background" Value="{StaticResource ComboBox.Static.Background}"/>
        <Setter Property="BorderBrush" Value="{StaticResource ComboBox.Static.Border}"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
        <Setter Property="Width" Value="150"/>
        <Setter Property="Height" Value="25"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
        <Setter Property="Padding" Value="6,3,5,3"/>
        <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
        <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
        <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    </Style>
</ResourceDictionary>

?使用资源文件?

2.8 主界面布局

?

<Window x:Class="OmronFinsWPFApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:OmronFinsWPFApp.ViewModel"
        mc:Ignorable="d"   WindowStartupLocation="CenterScreen"
        FontSize="13" FontFamily="Microsoft YaHei" FontWeight="ExtraLight" Title="欧姆龙PLC通讯工具" Height="430" Width="860" Name="loginWin">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid Background="Honeydew" ShowGridLines="true">
        <Grid.RowDefinitions>
            <RowDefinition Height="60"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <!--第一行标题-->
        <TextBlock Grid.Row="0" Text="C#利用Fins协议实现欧姆龙PLC的数据读写" FontSize="22" VerticalAlignment="Center" Margin="20,0" FontWeight="Bold" />
        <!--第二行信息-->
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="200"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions> 

            <StackPanel Grid.Column="0" Margin="20">
                <TextBlock Text="PLC地址"/>
                <TextBox   Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10" Text="{Binding HostName}"  /> 
                <TextBlock Text="端口号" Margin="0,10,0,0"/>
                <TextBox  Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10" Text="{Binding HostPort}"  /> 
                <Button Content="连接" Margin="0,30,0,0" Height="30"   Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=loginWin}"  />
                <Button Content="断开" Margin="0,10" Height="30" />
                <TextBlock  Text="{Binding ConnectWords,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"  Margin="0,10,0,0"  Foreground="Blue"/>
            </StackPanel>

            <Grid Grid.Column="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="60"/>
                    <RowDefinition Height="80"/>
                    <RowDefinition Height="30"/>
                    <RowDefinition Height="60"/>
                    <RowDefinition Height="80"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="10">
                    <TextBlock     Text="存储区:"  Style="{StaticResource  txtTextBlockStyle}" ></TextBlock>
                    <ComboBox  ItemsSource="{Binding CboCustTypes}" SelectedValue="{Binding  ReadPLCModel.Area}"  Width="70"  Style="{StaticResource cboStyle}"></ComboBox>
                    <TextBlock   Text="数据类型:"  Style="{StaticResource  txtTextBlockStyle}" ></TextBlock>
                    <ComboBox     ItemsSource="{Binding CboCustDatas}" SelectedValue="{Binding ReadPLCModel.DataType}"  Width="70" Style="{StaticResource cboStyle}"></ComboBox>
                    <TextBlock   Text="地址:" Style="{StaticResource  txtTextBlockStyle}" ></TextBlock>
                    <TextBox    Text="{Binding ReadPLCModel.Address}"  Width="100"  Style="{StaticResource  txtTextBoxStyle}" />
                    <TextBlock   Text="数量:" Style="{StaticResource  txtTextBlockStyle}" ></TextBlock>
                    <TextBox    Text="{Binding ReadPLCModel.Count}"  Width="60" Style="{StaticResource  txtTextBoxStyle}"  />
                    <Button Content="读取" Height="30" Width="100"  Command="{Binding ReadCommand}" CommandParameter="{Binding ElementName=loginWin}" Style="{StaticResource btnBaseStyle}"  />
                </StackPanel>

                <StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"  Margin="10">
                    <TextBlock Text="读取结果(多个数据之间用逗号隔开)"  Style="{StaticResource  txtTextBlockStyle}" Margin="0 5 0 9"></TextBlock>
                    <TextBox   Text="{Binding ReadWords,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"  Width="600" Style="{StaticResource  txtTextBoxStyle}"    />
                </StackPanel>

                <StackPanel Orientation="Horizontal" Margin="5" Grid.Row="2"  Grid.Column="0" Grid.ColumnSpan="2" Background="#4490AC" Width="600">
                </StackPanel>

                <StackPanel Orientation="Horizontal" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2"  Margin="10">
                    <TextBlock     Text="存储区:" Style="{StaticResource  txtTextBlockStyle}" ></TextBlock>
                    <ComboBox    ItemsSource="{Binding CboCustTypes}" SelectedValue="{Binding WritePLCModel.Area }"  Width="70"  Style="{StaticResource cboStyle}"></ComboBox>
                    <TextBlock   Text="数据类型:" Style="{StaticResource  txtTextBlockStyle}" ></TextBlock>
                    <ComboBox     ItemsSource="{Binding CboCustDatas}" SelectedValue="{Binding WritePLCModel.DataType }"  Width="70"  Style="{StaticResource cboStyle}"></ComboBox>
                    <TextBlock   Text="地址:" Style="{StaticResource  txtTextBlockStyle}" ></TextBlock>
                    <TextBox   Text="{Binding  WritePLCModel.Address }"  Width="100"  Style="{StaticResource  txtTextBoxStyle}"  />
                    <TextBlock   Text="数量:" Style="{StaticResource  txtTextBlockStyle}" ></TextBlock>
                    <TextBox    Text="{Binding WritePLCModel.Count}"  Width="60"   Style="{StaticResource  txtTextBoxStyle}"  />
                    <Button Content="写入" Height="30" Width="100"   Command="{Binding WriteCommand}" CommandParameter="{Binding ElementName=loginWin}" Style="{StaticResource btnBaseStyle}" />
                </StackPanel>

                <StackPanel Orientation="Vertical" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2"  Margin="10">
                    <TextBlock   Text="写入数据(多个数据之间用逗号隔开)" Style="{StaticResource  txtTextBlockStyle}" Margin="0 5 0 9" ></TextBlock>
                    <TextBox    Text="{Binding WriteWords ,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"  Width="600" Style="{StaticResource  txtTextBoxStyle}"  />
                </StackPanel>

            </Grid>
        </Grid>
    </Grid>
</Window>

运行起来效果

3、测试软件

3.1 测试PLC连接

3.2 读取dm区100的short类型

3.3?读取h区100的ushort类型

3.4?读取w区100的float类型

3.5?读取cio区100的bool类型

3.6 写入DM区110的short类型

?

?

3.7 写入H区110的ushort类型

3.8 写入W区110的float类型

3.9 写入CIO区110的bool类型

?

全部通讯报文?

4、小结

本例子用到WPF的MVVM模式,不是winform中的按钮点击事件,是后台绑定viewmodel,用数据驱动页面控件,跟vue中的mvvm渲染数据方式一样,跟微信小程序的MVVM模式一样的,这就实现了界面与数据的分离。

打字不易,截图不易,代码不易,准备不易,原创不易,多多点赞收藏,江湖有你,共同致富。

文章来源:https://blog.csdn.net/hqwest/article/details/135409097
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。