WPF MVVM 实现简单串口助手
程序员文章站
2024-02-02 11:17:34
...
WPF MVVM 实现简单串口助手
本例使用WPF MVVM实现了一个简单的串口助手,写这个的目的主要是熟悉WPF中基于数据绑定的模式。当然,实现过程中参考了一些大神的代码,在此感谢;此程序界面没做任何优化,因为不是本例的重点。因为不想添加额外的DLL文件,所以没有音乐MVVM Light,自己实现的MVVM框架;效果如下:
主窗体xaml代码:
<Window x:Class="WPF_SP.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:WPF_SP"
xmlns:cv="clr-namespace:WPF_SP.Converts"
mc:Ignorable="d"
Title="WPF MVVM SerialPort" Height="460" Width="700" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.Resources>
<cv:ComSwitchConvert x:Key="ComSwitchConvert"/>
<cv:ColorConverters x:Key="ColorConverters"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=" *"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Height="26" Margin="20 0 0 0">
<TextBlock Text="串口号:" VerticalAlignment="Center" />
<ComboBox Width="100" ItemsSource="{Binding CParameter.Port}" SelectedItem="{Binding CuParameter.Port}" IsEnabled="{Binding CuParameter.EnableSelect}"/>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" Height="26" Margin="20 0 0 0">
<TextBlock Text="波特率:" VerticalAlignment="Center" />
<ComboBox Width="100" ItemsSource="{Binding CParameter.BaudRate}" SelectedItem="{Binding CuParameter.BaudRate}" IsEnabled="{Binding CuParameter.EnableSelect}"/>
</StackPanel>
<StackPanel Grid.Row="2" Orientation="Horizontal" Height="26" Margin="20 0 0 0">
<TextBlock Text="数据位:" VerticalAlignment="Center" />
<ComboBox Width="100" ItemsSource="{Binding CParameter.DataBit}" SelectedItem="{Binding CuParameter.DataBit}" IsEnabled="{Binding CuParameter.EnableSelect}"/>
</StackPanel>
<StackPanel Grid.Row="3" Orientation="Horizontal" Height="26" Margin="20 0 0 0">
<TextBlock Text="校验位:" VerticalAlignment="Center" />
<ComboBox Width="100" ItemsSource="{Binding CParameter.Parity}" SelectedItem="{Binding CuParameter.Parity}" IsEnabled="{Binding CuParameter.EnableSelect}"/>
</StackPanel>
<StackPanel Grid.Row="4" Orientation="Horizontal" Height="26" Margin="20 0 0 0">
<TextBlock Text="停止位:" VerticalAlignment="Center" />
<ComboBox Width="100" ItemsSource="{Binding CParameter.StopBit}" SelectedItem="{Binding CuParameter.StopBit}" IsEnabled="{Binding CuParameter.EnableSelect}"/>
</StackPanel>
<StackPanel Grid.Row="5" Orientation="Horizontal" Height="26" Margin="20 0 0 0">
<TextBlock Text="状 态:" VerticalAlignment="Center" />
<Rectangle Width="40" Height="20" Margin="30 0 0 0" Fill="{Binding CuParameter.IsOpen,Converter={StaticResource ColorConverters}}"/>
</StackPanel>
<StackPanel Grid.Row="6" Orientation="Horizontal" Height="26" Margin="20 0 0 0">
<Button Width="100" Content="{Binding CuParameter.IsOpen,Converter={StaticResource ComSwitchConvert}}" Margin="20 0 0 0" Command="{Binding OpenCom}"/>
</StackPanel>
<StackPanel Grid.Row="7" Orientation="Vertical">
<CheckBox Content="十六进制接收" VerticalAlignment="Center" Margin="40 10 0 0" IsChecked="{Binding CuParameter.ReceiveFormat16}"/>
<Button Content="清空接收" Width="100" HorizontalAlignment="Left" Margin="40 10 0 0" Command="{Binding ClearReceiveDatas}"/>
</StackPanel>
<StackPanel Grid.Row="8" Orientation="Vertical">
<CheckBox Content="十六进制发送" VerticalAlignment="Center" Margin="40 10 0 0" IsChecked="{Binding CuParameter.SendFormat16}"/>
<Button Content="清空发送" Width="100" HorizontalAlignment="Left" Margin="40 10 0 0" Command="{Binding ClearSendDatas}"/>
</StackPanel>
<StackPanel Grid.Row="9" Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="TX: " Margin="40 0 0 0"/>
<TextBlock Width="20" Text="{Binding CuParameter.SendCount}"/>
<TextBlock Text="RX: " Margin="40 0 0 0"/>
<TextBlock Width="20" Text="{Binding CuParameter.ReceiveCount}"/>
</StackPanel>
</Grid>
<!--右侧接收发送数据栏-->
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="280"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" VerticalAlignment="Center">
<TextBlock Text="数据接收区:"/>
</Grid>
<Grid Grid.Row="1">
<TextBox Margin="0 0 10 10" Text="{Binding CuParameter.DataReceiveInfo}" TextWrapping="Wrap"></TextBox>
</Grid>
<Grid Grid.Row="2" VerticalAlignment="Center">
<TextBlock Text="数据发送区:"/>
</Grid>
<StackPanel Grid.Row="3" VerticalAlignment="Center" Orientation="Horizontal">
<TextBox Width="380" Height="60" Margin="0 0 10 10" Text="{Binding CuParameter.SendData}" TextWrapping="Wrap"/>
<Button Content="发送" HorizontalAlignment="Right" Width="60" Height="60" Margin="0 0 10 10" Command="{Binding SendCom}"/>
</StackPanel>
</Grid>
</Grid>
</Window>
主窗体后台代码:
namespace WPF_SP
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
}
串口的初始化参数类ComParameter:
class ComParameter : NotificationObject
{
//端口号
private ObservableCollection<string> port = new ObservableCollection<string>() { "COM1", "COM2", "COM3","COM4","COM5","COM6","COM7","COM8" };
public ObservableCollection<string> Port
{
get { return port; }
set
{
port = value;
this.RaisePropertyChanged("Port");
}
}
//波特率
private ObservableCollection<int> baudRate = new ObservableCollection<int>() { 110, 300, 600, 1200, 2400, 4800, 9600, 14400,19200,38400 };
public ObservableCollection<int> BaudRate
{
get { return baudRate; }
set
{
baudRate = value;
this.RaisePropertyChanged("BaudRate");
}
}
//数据位
private ObservableCollection<int> dataBit = new ObservableCollection<int>() { 6, 7, 8 };
public ObservableCollection<int> DataBit
{
get { return dataBit; }
set
{
dataBit = value;
this.RaisePropertyChanged("DataBit");
}
}
//校验位
private ObservableCollection<string> parity = new ObservableCollection<string>() { "无", "偶校验", "奇校验" };
public ObservableCollection<string> Parity
{
get { return parity; }
set
{
parity = value;
this.RaisePropertyChanged("Parity");
}
}
//停止位
private ObservableCollection<string> stopBit = new ObservableCollection<string>() { "1", "1.5", "2" };
public ObservableCollection<string> StopBit
{
get { return stopBit; }
set
{
stopBit = value;
this.RaisePropertyChanged("StopBit");
}
}
}
串口参数与事件的实体类CurrentParameter:
class CurrentParameter : NotificationObject
{
public CurrentParameter()
{
}
#region 初始串口参数
//当前串口号
private string port;
public string Port
{
get { return port; }
set
{
port = value;
this.RaisePropertyChanged("Port");
}
}
//当前波特率
private int baudRate;
public int BaudRate
{
get { return baudRate; }
set
{
baudRate = value;
this.RaisePropertyChanged("BaudRate");
}
}
//当前数据位
private int dataBit;
public int DataBit
{
get { return dataBit; }
set
{
dataBit = value;
this.RaisePropertyChanged("DataBit");
}
}
//当前校验位
private string parity;
public string Parity
{
get { return parity; }
set
{
parity = value;
this.RaisePropertyChanged("Parity");
}
}
//当前停止位
private string stopBit;
public string StopBit
{
get { return stopBit; }
set
{
stopBit = value;
this.RaisePropertyChanged("StopBit");
}
}
#endregion
private SerialPort serialPort;
public SerialPort SerialPort
{
get { return serialPort; }
set { serialPort = value; this.RaisePropertyChanged("SerialPort"); }
}
private string dataReceiveInfo;
private string sendData;
private bool isOpen;
private bool receiveFormat16 = false;
private bool sendFormat16 = false;
private bool enableSelect = true;
private int sendCount;
private int receiveCount;
public int SendCount
{
get { return sendCount; }
set { sendCount = value; this.RaisePropertyChanged("SendCount"); }
}
/// <summary>
/// 接收数量
/// </summary>
public int ReceiveCount
{
get { return receiveCount; }
set { receiveCount = value; this.RaisePropertyChanged("ReceiveCount"); }
}
/// <summary>
/// 接收区16进制
/// </summary>
public bool ReceiveFormat16
{
get { return receiveFormat16; }
set { receiveFormat16 = value; this.RaisePropertyChanged("ReceiveFormat16"); }
}
public bool EnableSelect
{
get { return enableSelect; }
set { enableSelect = value; this.RaisePropertyChanged("EnableSelect"); }
}
/// <summary>
/// 接收区数据
/// </summary>
public string DataReceiveInfo
{
get { return dataReceiveInfo; }
set { dataReceiveInfo = value; this.RaisePropertyChanged("DataReceiveInfo"); }
}
/// <summary>
/// 发送数据
/// </summary>
public string SendData
{
get
{
return sendData;
}
set { sendData = value; this.RaisePropertyChanged("SendData"); }
}
/// <summary>
/// 发送区16进制
/// </summary>
public bool SendFormat16
{
get { return sendFormat16; }
set { sendFormat16 = value; this.RaisePropertyChanged("SendFormat16"); }
}
/// <summary>
/// 开关
/// </summary>
public bool IsOpen
{
get { return isOpen; }
set { isOpen = value; this.RaisePropertyChanged("IsOpen"); }
}
public bool Open()
{
if (serialPort != null && serialPort.IsOpen)
{
return Close();
}
try
{
serialPort = new SerialPort();
serialPort.BaudRate = this.BaudRate;
serialPort.DataBits = this.DataBit;
if (this.StopBit == "1")
serialPort.StopBits = System.IO.Ports.StopBits.One;
else if (this.StopBit == "1.5")
serialPort.StopBits = System.IO.Ports.StopBits.OnePointFive;
else
serialPort.StopBits = System.IO.Ports.StopBits.Two;
if (this.Parity == "无")
serialPort.Parity = System.IO.Ports.Parity.None;
else if (this.Parity == "偶校验")
serialPort.Parity = System.IO.Ports.Parity.Even;
else
serialPort.Parity = System.IO.Ports.Parity.Odd;
serialPort.PortName = this.Port;
serialPort.RtsEnable = true;
serialPort.DataReceived += SerialPort_DataReceived;
serialPort.Open();
EnableSelect = false;
if (serialPort.IsOpen)
{
return IsOpen = true;
}
else
{
return IsOpen = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return IsOpen = false;
}
/// <summary>
/// 关闭串口
/// </summary>
/// <returns></returns>
public bool Close()
{
try
{
EnableSelect = true;
if (serialPort.IsOpen)
{
serialPort.Close();
return IsOpen = serialPort.IsOpen;
}
else
{
return IsOpen = serialPort.IsOpen;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return IsOpen = false;
}
}
/// <summary>
/// 发送数据
/// </summary>
public void Send()
{
if (SerialPort != null)
{
if (SendFormat16)
{
byte[] bytes = CRC.StringToHexByte(SendData);
this.SerialPort.Write(bytes, 0, bytes.Length);
SendCount += bytes.Length; //不做增量
}
else
{
this.SerialPort.Write(SendData);
SendCount += SendData.Length;
}
}
else
MessageBox.Show("串口未打开!");
}
/// <summary>
/// 清空接收区
/// </summary>
public void Clear()
{
this.DataReceiveInfo = string.Empty;
}
/// <summary>
/// 清空发送区和缓存区
/// </summary>
public void ClearText()
{
this.SendData = string.Empty;
this.SendCount = 0;
this.ReceiveCount = 0;
}
private bool Listening = false;
private bool Closing = false;
private StringBuilder builder = new StringBuilder();
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (Closing) return;
try
{
Listening = true;
int n = SerialPort.BytesToRead;
byte[] buf = new byte[n];
ReceiveCount += n;
SerialPort.Read(buf, 0, n);
builder.Clear();
if (ReceiveFormat16)
builder.Append(Encoding.UTF8.GetString(buf));
else
{
foreach (byte b in buf)
{
builder.Append(b.ToString("X2") + " ");
}
}
DataReceiveInfo += builder.ToString();
}
finally
{
Listening = false;
}
}
}
对应的类MainWindowViewModel:
class MainWindowViewModel : NotificationObject
{
public MainWindowViewModel()
{
CParameter = new ComParameter();
CuParameter = new CurrentParameter();
CuParameter.Port = CParameter.Port[0];
CuParameter.BaudRate = CParameter.BaudRate[6];
CuParameter.DataBit = CParameter.DataBit[2];
CuParameter.Parity = CParameter.Parity[0];
CuParameter.StopBit = CParameter.StopBit[0];
this.ClearSendDatas = new DelegateCommand();
this.ClearSendDatas.ExecuteAction = new Action<object>(this.ClearSend);
this.ClearReceiveDatas = new DelegateCommand();
this.ClearReceiveDatas.ExecuteAction = new Action<object>(this.ClearReceive);
this.OpenCom = new DelegateCommand();
this.OpenCom.ExecuteAction = new Action<object>(this.ToOpen);
this.SendCom = new DelegateCommand();
this.SendCom.ExecuteAction = new Action<object>(this.ToSend);
}
private CurrentParameter cuParameter;
public CurrentParameter CuParameter
{
get { return cuParameter; }
set
{
cuParameter = value;
this.RaisePropertyChanged("CuParameter");
}
}
//获取串口参数下拉列表
private ComParameter cParameter;
public ComParameter CParameter
{
get { return cParameter; }
set
{
cParameter = value;
this.RaisePropertyChanged("CParameter");
}
}
#region 清空发送和接收区
//清空发送区
public DelegateCommand ClearSendDatas { get; set; }
private void ClearSend(object parameter)
{
CuParameter.SendData = string.Empty;
}
//清空接收区
public DelegateCommand ClearReceiveDatas { get; set; }
private void ClearReceive(object parameter)
{
CuParameter.DataReceiveInfo = string.Empty;
}
#endregion
//打开串口
public DelegateCommand OpenCom { get; set; }
private void ToOpen(object parameter)
{
this.CuParameter.Open();
}
//发送数据
public DelegateCommand SendCom { get; set; }
private void ToSend(object parameter)
{
this.CuParameter.Send();
}
}
还有两个转换器类:实现打开串口和关闭串口的转换,串口状态的颜色变化:
public class ComSwitchConvert : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && bool.TryParse(value.ToString(), out bool result))
{
if (result)
{
return "关闭串口";
}
}
return "打开串口";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class ColorConverters : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && bool.TryParse(value.ToString(), out bool result))
{
if (result)
{
return new SolidColorBrush((Color)System.Windows.Media.ColorConverter.ConvertFromString("#008000"));
}
}
return new SolidColorBrush((Color)System.Windows.Media.ColorConverter.ConvertFromString("#808080"));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
还有两个类,一个响应数据实时变化和一个与命令相关:
class NotificationObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
class DelegateCommand : ICommand
{
public bool CanExecute(object parameter)
{
if (CanExecuteFunc == null)
return true;
return this.CanExecuteFunc(parameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (ExecuteAction == null)
{
return;
}
this.ExecuteAction(parameter);
}
public Action<object> ExecuteAction { get; set; }
public Func<object, bool> CanExecuteFunc { get; set; }
}
代码下载地址:
https://download.csdn.net/download/qq_43024228/12461723
上一篇: PHP 强制性文件下载功能的函数代码(任意文件格式)
下一篇: 进度条函数封装