使用WPF编写一个读取串口的程序

WPF串口通信教程

本教程将指导您使用WPF(Windows Presentation Foundation)创建一个完整的串口通信程序。我们将逐步完成以下步骤:创建界面、建立串口通信、实时更新接收数据、停止通信。示例代码基于.NET框架的System.IO.Ports命名空间,使用C#语言编写。请确保在Visual Studio中创建WPF应用程序项目(选择.NET Framework 4.5或更高版本)。

1. 创建界面

首先,设计WPF界面,包括串口选择、数据显示和操作按钮。打开MainWindow.xaml文件,添加以下XAML代码:

复制代码
<Window x:Class="SerialPortApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="串口通信程序" Height="350" Width="500">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        
        <!-- 串口选择区域 -->
        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="10">
            <TextBlock Text="选择串口:" VerticalAlignment="Center" Margin="0,0,10,0"/>
            <ComboBox x:Name="comboBoxPorts" Width="100" VerticalAlignment="Center"/>
            <TextBlock Text="波特率:" VerticalAlignment="Center" Margin="10,0,10,0"/>
            <ComboBox x:Name="comboBoxBaudRate" Width="80" VerticalAlignment="Center">
                <ComboBoxItem Content="9600" IsSelected="True"/>
                <ComboBoxItem Content="115200"/>
            </ComboBox>
        </StackPanel>
        
        <!-- 数据显示区域 -->
        <TextBox x:Name="textBoxData" Grid.Row="1" Margin="10" Height="200" 
                 VerticalScrollBarVisibility="Auto" IsReadOnly="True" TextWrapping="Wrap"/>
        
        <!-- 操作按钮 -->
        <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">
            <Button x:Name="buttonOpen" Content="打开串口" Width="80" Click="ButtonOpen_Click"/>
            <Button x:Name="buttonClose" Content="关闭串口" Width="80" Margin="10,0,0,0" Click="ButtonClose_Click" IsEnabled="False"/>
        </StackPanel>
    </Grid>
</Window>

此界面包括:

  • 一个ComboBox用于选择串口号(如COM1)。
  • 一个ComboBox用于选择波特率(如9600)。
  • 一个TextBox用于显示接收到的数据。
  • 两个Button:一个用于打开串口,另一个用于关闭串口。
2. 建立通信

接下来,在后台代码(MainWindow.xaml.cs)中实现串口通信逻辑。首先,添加必要的命名空间和变量声明:

复制代码
using System;
using System.IO.Ports;
using System.Windows;
using System.Windows.Threading;

public partial class MainWindow : Window
{
    private SerialPort serialPort; // 串口对象
    private bool isPortOpen = false; // 串口状态标志

    public MainWindow()
    {
        InitializeComponent();
        LoadPorts(); // 初始化时加载可用串口
    }

    // 加载可用串口
    private void LoadPorts()
    {
        comboBoxPorts.Items.Clear();
        string[] ports = SerialPort.GetPortNames();
        foreach (string port in ports)
        {
            comboBoxPorts.Items.Add(port);
        }
        if (ports.Length > 0)
        {
            comboBoxPorts.SelectedIndex = 0;
        }
    }
}
3. 打开串口和更新数据

实现打开串口按钮的点击事件。打开串口后,订阅DataReceived事件以处理接收数据。注意:串口事件在非UI线程触发,需要使用Dispatcher更新UI。

复制代码
// 打开串口按钮点击事件
private void ButtonOpen_Click(object sender, RoutedEventArgs e)
{
    if (comboBoxPorts.SelectedItem == null)
    {
        MessageBox.Show("请选择串口!");
        return;
    }

    string portName = comboBoxPorts.SelectedItem.ToString();
    int baudRate = int.Parse((comboBoxBaudRate.SelectedItem as ComboBoxItem)?.Content.ToString() ?? "9600");

    try
    {
        serialPort = new SerialPort(portName, baudRate);
        serialPort.DataReceived += SerialPort_DataReceived; // 订阅数据接收事件
        serialPort.Open();
        isPortOpen = true;
        buttonOpen.IsEnabled = false;
        buttonClose.IsEnabled = true;
        textBoxData.AppendText($"串口 {portName} 已打开,波特率 {baudRate}。\n");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"打开串口失败: {ex.Message}");
    }
}

// 数据接收事件处理
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    // 读取数据
    string data = serialPort.ReadLine(); // 假设数据以行结束(如换行符)
    
    // 使用Dispatcher更新UI,确保线程安全
    Dispatcher.Invoke(() =>
    {
        textBoxData.AppendText($"{DateTime.Now:HH:mm:ss} 接收: {data}\n");
        textBoxData.ScrollToEnd(); // 自动滚动到最新内容
    });
}
4. 停止通信

实现关闭串口按钮的点击事件。关闭串口时,取消订阅事件并释放资源。

复制代码
// 关闭串口按钮点击事件
private void ButtonClose_Click(object sender, RoutedEventArgs e)
{
    if (isPortOpen && serialPort != null)
    {
        try
        {
            serialPort.DataReceived -= SerialPort_DataReceived; // 取消订阅事件
            serialPort.Close();
            isPortOpen = false;
            buttonOpen.IsEnabled = true;
            buttonClose.IsEnabled = false;
            textBoxData.AppendText("串口已关闭。\n");
        }
        catch (Exception ex)
        {
            MessageBox.Show($"关闭串口失败: {ex.Message}");
        }
    }
}
5. 完整代码示例

以下是MainWindow.xaml.cs的完整代码:

复制代码
using System;
using System.IO.Ports;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;

namespace SerialPortApp
{
    public partial class MainWindow : Window
    {
        private SerialPort serialPort;
        private bool isPortOpen = false;

        public MainWindow()
        {
            InitializeComponent();
            LoadPorts();
        }

        private void LoadPorts()
        {
            comboBoxPorts.Items.Clear();
            string[] ports = SerialPort.GetPortNames();
            foreach (string port in ports)
            {
                comboBoxPorts.Items.Add(port);
            }
            if (ports.Length > 0)
            {
                comboBoxPorts.SelectedIndex = 0;
            }
        }

        private void ButtonOpen_Click(object sender, RoutedEventArgs e)
        {
            if (comboBoxPorts.SelectedItem == null)
            {
                MessageBox.Show("请选择串口!");
                return;
            }

            string portName = comboBoxPorts.SelectedItem.ToString();
            int baudRate = int.Parse((comboBoxBaudRate.SelectedItem as ComboBoxItem)?.Content.ToString() ?? "9600");

            try
            {
                serialPort = new SerialPort(portName, baudRate);
                serialPort.DataReceived += SerialPort_DataReceived;
                serialPort.Open();
                isPortOpen = true;
                buttonOpen.IsEnabled = false;
                buttonClose.IsEnabled = true;
                textBoxData.AppendText($"串口 {portName} 已打开,波特率 {baudRate}。\n");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"打开串口失败: {ex.Message}");
            }
        }

        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            string data = serialPort.ReadLine();
            Dispatcher.Invoke(() =>
            {
                textBoxData.AppendText($"{DateTime.Now:HH:mm:ss} 接收: {data}\n");
                textBoxData.ScrollToEnd();
            });
        }

        private void ButtonClose_Click(object sender, RoutedEventArgs e)
        {
            if (isPortOpen && serialPort != null)
            {
                try
                {
                    serialPort.DataReceived -= SerialPort_DataReceived;
                    serialPort.Close();
                    isPortOpen = false;
                    buttonOpen.IsEnabled = true;
                    buttonClose.IsEnabled = false;
                    textBoxData.AppendText("串口已关闭。\n");
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"关闭串口失败: {ex.Message}");
                }
            }
        }
    }
}
6. 运行和测试
  1. 编译运行:在Visual Studio中按F5运行程序。
  2. 连接设备:将串口设备(如Arduino)连接到电脑,确保设备发送数据以换行符结束。
  3. 测试功能
    • 选择串口号和波特率,点击"打开串口"。
    • 接收到的数据将显示在TextBox中。
    • 点击"关闭串口"停止通信。
注意事项
  • 线程安全 :串口DataReceived事件在后台线程触发,必须使用Dispatcher.Invoke更新UI,避免跨线程异常。

  • 错误处理:添加更多try-catch块以处理潜在异常(如串口被占用)。

  • 数据格式 :本示例使用ReadLine()方法,假设数据以换行符结束。如果数据格式不同,可改用ReadExisting()或自定义解析。

  • 资源释放 :在窗口关闭时,确保关闭串口。可重写OnClosing方法:

    复制代码
    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {
        if (isPortOpen && serialPort != null)
        {
            serialPort.Close();
        }
        base.OnClosing(e);
    }

通过本教程,您已实现了一个完整的WPF串口通信程序。根据实际需求,您可以扩展功能,如添加数据发送或更多配置选项。

相关推荐
关关长语1 天前
HandyControl 3.5.x 版本 ListViewItem不显示问题
windows·wpf
Macbethad1 天前
工业设备维护程序技术方案
wpf
Macbethad1 天前
工业设备配方管理系统技术方案
wpf
喵叔哟1 天前
7.日志系统深入
wpf
清风徐来Groot1 天前
WPF布局之Grid
wpf
清风徐来Groot1 天前
WPF布局之WrapPanel
wpf
Macbethad1 天前
WPF工业设备工艺配方流程程序技术方案
wpf
清风徐来Groot1 天前
WPF布局之UniformGrid
wpf
清风徐来Groot1 天前
WPF布局之StackPanel
wpf
500842 天前
鸿蒙 Flutter 权限管理进阶:动态权限、权限组、兼容处理与用户引导
flutter·华为·架构·wpf·开源鸿蒙