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

news2025/1/14 12:17:12

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用qtcreator创建qml项目(图解)

接下来就一直点继续&#xff0c;最后完成项目&#xff0c;如下图。 下面对项目进行一些基本的描述 &#xff08;1&#xff09;qt项目文件使用pro后缀&#xff0c;是qt项目的配置文件&#xff0c;它用于指定项目的各种参数&#xff0c;包括源文件、头文件、库依赖、编译选项等&a…

全网唯一值得推荐的C/C++框架和库

全网唯一值得推荐的C/C框架和库 C程序员开发指南 ​ 关注我&#xff0c;天天分享C/C开发技术干货&#xff01; ​关注他 30 人赞同了该文章 ​ 目录 收起 标准库 C通用框架和库 人工智能 异步事件循环 音频 生态学 压缩 并发性 容器 数据库 调试 游戏引擎 图…

C++程序设计兼谈对象模型(侯捷)笔记

C程序设计兼谈对象模型&#xff08;侯捷) 这是C面向对象程序设计的续集笔记&#xff0c;仅供个人学习使用。如有侵权&#xff0c;请联系删除。 主要内容&#xff1a;涉及到模板中的类模板、函数模板、成员模板以及模板模板参数&#xff0c;后面包含对象模型中虚函数调用&…

【合阳新起点公益】“关爱留守儿童 守护牙齿健康”牙膏发放活动

为了关爱儿童的口腔健康&#xff0c;帮助他们改善生活状况&#xff0c;养成良好的刷牙习惯。合阳县未成年人保护中心、合阳县新起点公益服务中心组织链接到汕头市惠泽人志愿服务中心&#xff0c;为孩子们申请到一批儿童爱心牙膏套盒&#xff0c;分别于2023年12月22日、12月30日…

基于seatunnel实现mysql同步clickhouse验证

场景&#xff1a; 需求想要实现mysql同步到clickhouse&#xff0c;seatunnel部署见前面文档linux环境seatunnel安装运行-CSDN博客。 官方说明文档 Clickhouse | Apache SeaTunnel mysql同步配置 server-id1 log_bin/var/lib/mysql/bin.log binlog_formatROW #binlog-do-db 具…

必看!干得不能再干了,Abaqus各版本安装指南

Abaqus下载链接 https://pan.baidu.com/s/1PHWhlM4cPINUqy2XYRHrhQ?pwd0531 ​1.鼠标右击【Abaqus2023(64bit)】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;【解压到 Abaqus.2023(64bit)】。 2.打开解压后的文件夹&#xff0c;鼠标右击【jdk-17…

2024年MySQL学习指南(四),探索MySQL数据库,掌握未来数据管理趋势

文章目录 前言9. 约束的概念10. 约束的分类11. 非空约束12. 唯一约束13. 主键约束14. 默认约束15. 外键约束16. 约束的案例练习 前言 接上篇&#xff1a; 2024年MySQL学习指南&#xff08;一&#xff09; 2024年MySQL学习指南&#xff08;二&#xff09; 2024年MySQL学习指…

影响机器人行业爆炸式发展的原因有哪些(OpenPilot)

机器人是现在和未来最有发展前景的行业&#xff0c;这一点是毋庸置疑的。 先看一段21年的视频&#xff1a; 外置式L2级别自动驾驶辅助套件 Openpilot 深度体验分析 火热 作为热门专业的从业人员&#xff0c;能够感受到行业的关注热度和实际热度之间的差异。 原因 有很多&am…

操作系统(期末复习知识点)

第1章 操作系统引论 操作系统的目标和作用 操作系统的目标 在计算机系统上配置OS&#xff0c;其主要目的是实现&#xff1a;方便性、有效性、可扩充性、开放性。 操作系统的作用 可以从人机交互、资源管理及资源抽象等不同方面来分析OS在计算机系统中所起到的作用。 OS 作为…

怎么测试电源负载瞬态响应?具体步骤是什么?

负载瞬态响应(Load Transient Response)指的是电路或系统在负载发生瞬态变化时,输出电压或电流出现的瞬时变化的情况。 电源负载瞬态响应测试是评估电源在负载变化时性能表现的一种重要方法。在实际应用中&#xff0c;电源负载的变化是不可避免的&#xff0c;因此准确评估电源的…

面试必问究极重点之HashMap的底层原理

1.底层数据结构 JDK版本不同的数据结构 1.7 数组 链表 1.8 数组 &#xff08;链表 | 红黑树&#xff09; 2.添加数据put 在添加一个值的时候&#xff0c;首先会计算他的hash码&#xff0c;然后进行二次hash&#xff0c;在对当前长度取模得到在底层数组中的索引位置当取模完…

用Spring Boot 3.2虚拟线程搭建静态文件服务器有多快?

Spring Boot 3.2 于 2023 年 11 月大张旗鼓地发布&#xff0c;标志着 Java 开发领域的一个关键时刻。这一突破性的版本引入了一系列革命性的功能&#xff0c;包括&#xff1a; 虚拟线程&#xff1a;利用 Project Loom 的虚拟线程释放可扩展性&#xff0c;从而减少资源消耗并增…

【HBase】——优化

1 RowKey设计 重要&#xff1a;一条数据的唯一标识就是 rowkey&#xff0c;那么这条数据存储于哪个分区&#xff0c;取决于 rowkey 处于 哪个一个预分区的区间内&#xff0c;设计 rowkey的主要目的 &#xff0c;就是让数据均匀的分布于所有的 region 中&#xff0c;在一定程度…

海昌海洋公园用泛微千里聆RPA智能采集、识别、分析网络数据,助力优化运营服务

海昌海洋公园控股有限公司&#xff08;以下简称“海昌海洋公园”&#xff09;是中国知名的主题公园和配套商用物业开发及运营商。经过近二十年发展&#xff0c;凭借行业优秀的极地海洋动物保育技术&#xff0c;公司将其业务模式逐步推广到核心城市&#xff0c;展开了海昌海洋公…

2024中国管业十大品牌——皮尔特管业

2024中国管业十大品牌——皮尔特管业 2024年度中国管业十大品牌评选活动圆满举办。来自江苏的皮尔特管道&#xff0c;再次成功入围2024中国管业十大品牌。皮尔特管业凭借多年积累的市场口碑&#xff0c;再次入围也是实至名归。 苏州皮尔特管业科技有限公司创建于2001年&#x…

矩阵翻转180度是什么意思,请举例

问题描述&#xff1a;矩阵翻转180度是什么意思&#xff0c;请举例 问题解答&#xff1a; 矩阵的180度翻转是指将矩阵绕中心水平和垂直翻转。这个操作类似于将图像或矩阵上下颠倒&#xff0c;然后左右颠倒。翻转后的矩阵在水平和垂直方向上保持了原始矩阵的对称性。 让我们以…

【性能测试】老鸟总结,性能测试到底该如何做,一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、前期准备 性能…

2023年第2季社区Task挑战赛贡献者榜单

基于FISCO BCOS及Weldentity&#xff0c;实现SSO单点登录服务&#xff1b;提供食品溯源、电商运费险7天退保、电子病历等智能合约库业务场景案例&#xff1b;基于FISCO BCOS更新游戏体验&#xff1b;体验并分析解读最新发布的分布式数据协作管理解决方案DDCMS&#xff0c;提供相…

Eviews 11安装包下载及安装教程

Eviews 11下载链接&#xff1a;https://docs.qq.com/doc/DUmRGdXVUeVBSU1lK 1.选中下载好的安装包&#xff0c;右键解压到“Eviews 11”文件夹 2.选中"Setup.exe"鼠标右键选择以管理员身份运行 3.点击“Next” 4.勾选“I Accept...”&#xff0c;点击“Next” 5.选择…

提升效率必备:用关键词替换法重命名文件夹技巧

在日常生活和工作中&#xff0c;经常要处理大量的文件夹&#xff0c;进行归类、整理和重命名。但是手动一个个重命名文件夹既费时又费力。为了提高效率&#xff0c;可以采用关键词替换法来批量重命名文件夹。现在讲解云炫文件管理器如何用关键词替换命名文件夹的具体步骤。 首先…