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. 运行和测试
- 编译运行:在Visual Studio中按F5运行程序。
- 连接设备:将串口设备(如Arduino)连接到电脑,确保设备发送数据以换行符结束。
- 测试功能 :
- 选择串口号和波特率,点击"打开串口"。
- 接收到的数据将显示在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串口通信程序。根据实际需求,您可以扩展功能,如添加数据发送或更多配置选项。