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

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模式一样的,这就实现了界面与数据的分离。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/294241.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

jenkins忘记密码后的操作

1、先停止 jenkins 服务 systemctl stop jenkins 关闭Jenkins服务 或者杀掉进程 ps -ef | grep jenkins &#xff5c;awk {print $2} | grep -v "grep" | xargs kill -9 2、找到 config.xml 文件 find /root -name config.xml3、备份config.xml文件 cp /root/.jen…

AI论文指南|ChatGPT助力论文论据搜集!【建议收藏】

点击下方▼▼▼▼链接直达AIPaperPass &#xff01; AIPaperPass - AI论文写作指导平台 公众号原文▼▼▼▼&#xff1a; AI论文指南|ChatGPT助力论文论据搜集&#xff01;【建议收藏】 上一篇文章&#xff0c;小编为宝子们分享了ChatGPT在论文论点提炼方面的内容以及操作流…

Java中的IO与NIO篇----第四篇

系列文章目录 文章目录 系列文章目录前言一、NIO 的非阻塞二、Channel三、Buffer四、Selector前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、NIO 的非阻塞 I…

安装PyTorch及环境配置(应用于Python上的YOLO)

这个基本都是Bilibili网站里面叫“小手丫子”up的视频教程&#xff0c;此前自己需要装了好几次又卸载了好几次&#xff0c;现在根据视频教学整理出来自己所理解的文档。 注意事项 1.安装的pycharm版本和anaconda版本无要求。 2.运行pycharm尽量以管理员身份运行。 3.Cuda是独…

论文阅读_InP-Based_Generic_Foundry_Platform_for_Photonic_Integrated_Circuits

InP-Based_Generic_Foundry_Platform_for_Photonic_Integrated_Circuits 时间&#xff1a;2018年 作者&#xff1a;Luc M. Augustin, Member, IEEE, Rui Santos, Erik den Haan, Steven Kleijn, Peter J. A. Thijs, Sylwester Latkowski, Senior Member, IEEE, Dan Zhao, Wei…

【基础篇】十四、GC算法

文章目录 1、实现思路2、SWT3、GC算法4、标记清除算法Mark Sweep GC5、复制算法Copying GC5、标记整理算法Mark Compact GC6、分代算法Generational GC7、分代的整体流程8、为什么分代GC把堆内存分为年轻代和老年代&#xff1f;&#x1f4d5; 1、实现思路 Java实现垃圾回收的步…

除了AIGC和大模型,2023年这个赛道也很拥挤

大数据产业创新服务媒体 ——聚焦数据 改变商业 转眼间&#xff0c;2023年即将里过去。回首往事&#xff0c;这个疫情后的第一个年份还是超乎不少人预期&#xff0c;这其中最明显的、最引人关注的科技变革&#xff0c;就是人工智能。自从年初ChatGPT爆火以来&#xff0c;这个话…

Unity SRP 管线【第五讲:自定义烘培光照】

文章目录 一、自定义烘培光照1. 烘培光照贴图2. 获取光照贴图3. 获取物体在光照贴图上的UV坐标4. 采样光照贴图 二、自定义光照探针三、 Light Probe Proxy Volumes&#xff08;LPPV&#xff09;四、Meta Pass五、 自发光烘培 一、自定义烘培光照 细节内容详见catlikecoding.c…

iPay88 支付网关Gateway-User Scan(用户扫码模式 - PHP实现)

背景 近期因为合作客户有马来西亚的业务&#xff0c;需要对接 【iPay88 支付】 通过阅读官方文档&#xff0c;发现一头雾水 相对之前接触的支付文档&#xff0c;个人觉得 iPay88 是最凌乱的 注意&#xff0c;注册平台账号后&#xff0c;会邮件发送几个开发文档附件&#xff08;…

TikTok革新游戏规则:解读短视频对社交媒体的影响

在社交媒体的巨浪中&#xff0c;TikTok以其独特的短视频形式和强大的创意社区&#xff0c;重新定义了游戏规则。这个以15秒视频为核心的平台&#xff0c;不仅让用户获得了表达自我的新方式&#xff0c;更深刻地影响了社交媒体的演进。本文将深入解读TikTok对社交媒体的影响&…

OpenSSL——升级

OpenSSL&#xff08;Open Secure Sockets Layer&#xff09;是一个开源的软件库&#xff0c;提供了SSL和TLS协议的实现&#xff0c;用于加密通信。它广泛用于安全连接&#xff0c;例如在网站上通过HTTPS协议进行安全的数据传输. 但是从openssl申请道德证书是不安全的。对于网站…

打破闲鱼商品搜索瓶颈!云端电商API接口让你畅享商品详情关键词搜索的便利!

在闲鱼卖家的日常经营中&#xff0c;商品搜索是一个至关重要的环节。但是&#xff0c;由于闲鱼平台商品信息繁杂&#xff0c;卖家往往很难通过手动搜索的方式找到符合自己需求的商品。为了解决这一问题&#xff0c;云端电商API接口应运而生。联讯数据将详细介绍云端电商API接口…

第一届能源电子产业创新大赛太阳能光伏赛道决赛及颁奖仪式在宜宾成功举办

在工业和信息化部电子信息司指导下&#xff0c;由工业和信息化部产业发展促进中心和宜宾市人民政府主办&#xff0c;宜宾市经济和信息化局、宜宾高新技术产业园区管理委员会承办的第一届能源电子产业创新大赛太阳能光伏赛道决赛及颁奖仪式于2024年1月3日-5日在宜宾市成功举办。…

亲授秘籍!独立站推广神级攻略,不信你试试?

在电商市场竞争激烈的今天&#xff0c;对于跨境电商卖家而言&#xff0c;有效推广独立站已经成为了一项关键任务。为了提升品牌知名度和吸引更多的潜在客户&#xff0c;我们需要不断探索新的推广方式和策略。在本文中&#xff0c;龙哥将分享一些成功的推广经验&#xff0c;以下…

java 6种深拷贝集合方式及其性能差异对比

文章目录 ArrayList 构造方法拷贝运行1000次耗时 1ms for循环拷贝运行1000次耗时 14ms Stream流 collect实现拷贝运行1000次耗时 54ms Stream流spring的BeanUtils实现拷贝运行1000次耗时 2468 ms Hutool工具实现拷贝Hutool 5.7.13版本运行1000次耗时 64674 msHutool 5.8.24版本…

芯片金线包封胶的使用注意事项是什么?

芯片金线包封胶的使用注意事项是什么?金线包封胶是一种高性能、高粘度的密封胶&#xff0c;广泛应用于电子、电器、汽车等领域。它具有良好的防水、防潮、防震等性能&#xff0c;能够保护产品内部零件不受环境影响&#xff0c;提高产品的使用寿命。然而&#xff0c;在使用金线…

记一次RabbitMQ服务器异常断电之后,服务重启异常的处理过程

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 问题描述&#xff1a; 机房突然停电&#xff0c;rabbitmq的主机异常断电&#xff0c;集群服务全部需要重启。但是在执行service…

热泵,预计到2025年市场规模将达到650亿美元

热泵是一种利用空气、水、地热等低品位能源&#xff0c;转化为高品位热能&#xff0c;提供供暖、热水和冷却等服务的热力设备。近年来&#xff0c;热泵市场逐步发展壮大&#xff0c;成为新能源市场的重点发展领域。全球市场分析&#xff1a; 近年来&#xff0c;全球绿色环保理念…

QT上位机开发(动态库dll的开发)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 有的时候&#xff0c;我们不想把所有的代码都放在一个exe里面&#xff0c;这个时候我们就需要引入dll动态库的概念。在windows平台上面&#xff0c…

接口测试测什么?一个简单问题把我难住了!

那么设计测试用例时我们主要考虑如下几个方面&#xff1a; 01、功能测试 接口的功能是否正确实现了 接口是否按照设计文档中来实现 &#xff08;比如username参数写为了user&#xff0c;那么这就不符合&#xff0c;因为接口文档在整个开发中都需要使用&#xff0c;所以接口实…