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模式一样的,这就实现了界面与数据的分离。
打字不易,截图不易,代码不易,准备不易,原创不易,多多点赞收藏,江湖有你,共同致富。