WPF MVVM 实现简单串口助手

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

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现ListBox的拖动排序,需要使用WPFMVVM的一些技巧。下面是一些大致的步骤: 1. 在ViewModel中定义一个ObservableCollection,用于绑定ListBox的ItemsSource属性。 2. 在ListBox的ItemTemplate中,绑定ListBoxItem的PreviewMouseDown事件和PreviewMouseMove事件。这两个事件分别用于开始拖动和移动拖动项。 3. 在PreviewMouseDown事件中,使用VisualTreeHelper找到ListBoxItem的父ListBox,并将拖动项从ObservableCollection中移除。 4. 在PreviewMouseMove事件中,使用DragDrop.DoDragDrop方法启动拖动操作,并将拖动项的索引和DataObject传递给该方法。 5. 在ViewModel中,定义一个DragOver命令和一个Drop命令。DragOver命令用于处理拖动项经过其他项时的逻辑,Drop命令用于处理拖动项放下时的逻辑。这两个命令的参数都包含了拖动项的索引和目标项的索引。 6. 在ListBox的ItemContainerStyle中,绑定ListBoxItem的DragOver事件和Drop事件到ViewModel中的DragOver命令和Drop命令。 7. 在ViewModel的Drop命令中,将拖动项插入到ObservableCollection中目标项的位置,并更新所有项的排序。 这些步骤只是大致的概述,具体实现时还需要考虑一些细节,例如拖动项和其他项的样式、拖动项和其他项的交互效果等等。如果您需要更详细的实现细节,请参考以下链接: https://www.c-sharpcorner.com/UploadFile/raj1979/drag-and-drop-items-in-listbox-in-wpf-mvvm/ https://www.codeproject.com/Articles/30905/Drag-and-Drop-Items-in-WPF-ListView-Control https://www.wpf-tutorial.com/listview-control/listview-drag-drop-sorting-mvvm/

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值