跳到主要内容

第一个 WPF 应用

这一篇带你做一个最小 WPF 上位机窗口:输入文本,点击发送,虚拟通道回显,界面显示通道状态。

先运行示例

在仓库的 code 目录执行:

dotnet run --project samples/Zeus.Samples.Wpf.QuickStart

窗口打开后:

  1. 状态应显示 Open
  2. 输入框默认是 PING
  3. 点击“发送”。
  4. 回显区域应显示 PING

如果能看到回显,说明 Zeus 宿主、虚拟通道和 WPF Dispatcher 封送都正常。

你的项目需要哪些包

dotnet add package Zeus.Communications --prerelease
dotnet add package Zeus.Presentation.Wpf --prerelease

项目文件需要是 Windows 桌面目标:

<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>

XAML 界面

<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Zeus WPF QuickStart"
Height="260"
Width="520">
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<TextBlock Grid.Row="0" Grid.Column="0" Text="发送" />
<TextBox x:Name="InputBox" Grid.Row="0" Grid.Column="1" Text="PING" />

<Button Grid.Row="1" Grid.Column="1" Content="发送" Click="OnSendClick" />

<TextBlock Grid.Row="2" Grid.Column="0" Text="状态" />
<TextBlock x:Name="StateText" Grid.Row="2" Grid.Column="1" />

<TextBlock Grid.Row="3" Grid.Column="0" Text="回显" />
<TextBlock x:Name="EchoText" Grid.Row="3" Grid.Column="1" />
</Grid>
</Window>

后台代码

using System.Text;
using System.Windows;
using Zeus;

public partial class MainWindow : Window
{
private readonly IChannel _meter;

public MainWindow()
{
InitializeComponent();

var attachment = this.AttachZeus(builder => builder.AddVirtualChannel("meter"));
_meter = attachment.Host.Channels.Get("meter");
_meter.BindState(StateText);
_meter.BindTo(EchoText);
}

private async void OnSendClick(object sender, RoutedEventArgs e)
{
await _meter.WriteAsync(Encoding.UTF8.GetBytes(InputBox.Text));
}
}

这段代码怎么理解

代码作用
this.AttachZeus(...)把宿主挂到 WPF 窗口生命周期上,窗口加载时启动,关闭时释放
AddVirtualChannel("meter")声明一条不用硬件的虚拟通道
Channels.Get("meter")取出通道对象
BindState(StateText)把通道状态写入 TextBlock
BindTo(EchoText)收到数据后把文本写入 TextBlock
WriteAsync(...)点击按钮时发送输入框内容

为什么不用自己 Dispatcher.Invoke

通道收到数据时,事件可能来自 IO 线程。WPF 控件只能在 Dispatcher 线程更新。

BindToBindState 已经封装了 Dispatcher 切换,所以你不需要在每次接收数据时手写 Dispatcher.Invoke

想用 XAML 数据绑定怎么办

如果你不想直接绑定到 TextBlock,可以用:

DataContext = _meter.AsBindingSource(this);

常用属性包括:

属性含义
LastText最近一次收到的数据,按文本显示
LastHex最近一次收到的数据,按十六进制显示
StateText通道状态文本
ReceivedCount已收到的次数

换成真实串口

先只改这一行:

var attachment = this.AttachZeus(builder => builder.AddSerialPort("meter", "COM3", 9600));

界面绑定和发送代码不变。如果串口打不开,先回到虚拟通道确认界面逻辑,再检查端口和设备参数。

下一步:了解 WPF 适配器,或继续看 串口