欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WPF MVVM 实现简单串口助手

程序员文章站 2024-02-02 11:17:34
...

WPF MVVM 实现简单串口助手

本例使用WPF MVVM实现了一个简单的串口助手,写这个的目的主要是熟悉WPF中基于数据绑定的模式。当然,实现过程中参考了一些大神的代码,在此感谢;此程序界面没做任何优化,因为不是本例的重点。因为不想添加额外的DLL文件,所以没有音乐MVVM Light,自己实现的MVVM框架;效果如下:
WPF MVVM 实现简单串口助手
WPF 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

相关标签: WPF