第一个 WinForms 应用
这一篇带你做一个最小 WinForms 上位机窗口:输入文本,点击发送,虚拟通道回显,界面显示通道状态。
先运行示例
在仓库的 code 目录执行:
dotnet run --project samples/Zeus.Samples.WinForms.QuickStart
窗口打开后:
- 状态应显示
Open。 - 输入框默认是
PING。 - 点击“发送”。
- 回显区域应显示
PING。
这说明 Zeus 已经把宿主生命周期、通道收发和 UI 线程切换串起来了。
你的项目需要哪些包
dotnet add package Zeus.Communications --prerelease
dotnet add package Zeus.Presentation.WinForms --prerelease
项目文件需要是 Windows 桌面目标:
<TargetFramework>net8.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
最小窗口代码
using System.Text;
using Zeus;
public sealed class MainForm : Form
{
private readonly TextBox _input = new() { Text = "PING", Dock = DockStyle.Fill };
private readonly Button _send = new() { Text = "发送", Dock = DockStyle.Fill };
private readonly Label _state = new() { Dock = DockStyle.Fill };
private readonly Label _echo = new() { Dock = DockStyle.Fill };
private readonly IChannel _meter;
public MainForm()
{
Text = "Zeus WinForms QuickStart";
var layout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 4 };
layout.Controls.Add(new Label { Text = "发送" }, 0, 0);
layout.Controls.Add(_input, 1, 0);
layout.Controls.Add(_send, 1, 1);
layout.Controls.Add(new Label { Text = "状态" }, 0, 2);
layout.Controls.Add(_state, 1, 2);
layout.Controls.Add(new Label { Text = "回显" }, 0, 3);
layout.Controls.Add(_echo, 1, 3);
Controls.Add(layout);
var attachment = this.AttachZeus(builder => builder.AddVirtualChannel("meter"));
_meter = attachment.Host.Channels.Get("meter");
_meter.BindState(_state);
_meter.BindTo(_echo);
_send.Click += async (_, _) =>
{
await _meter.WriteAsync(Encoding.UTF8.GetBytes(_input.Text));
};
}
}
实际示例为了更好看和更稳,会多写一些布局、异常弹窗和窗口尺寸设置。核心逻辑就是上面几行。
这段代码怎么理解
| 代码 | 作用 |
|---|---|
this.AttachZeus(...) | 把 Zeus 宿主挂到窗体生命周期上:窗口加载时启动,关闭时释放 |
builder.AddVirtualChannel("meter") | 声明一条虚拟通道,先不用真实硬件 |
attachment.Host.Channels.Get("meter") | 取出刚才声明的通道 |
_meter.BindState(_state) | 把通道状态显示到 Label,例如 Open / Closed |
_meter.BindTo(_echo) | 收到数据后,把文本显示到 Label |
_meter.WriteAsync(...) | 点击按钮时向通道写入字节 |
为什么不用自己 Invoke
串口和 TCP 的接收事件通常不在 UI 线程上。WinForms 直接在后台线程改控件会抛跨线程异常。
BindTo 和 BindState 已经通过 Zeus 的 WinForms 适配器做了线程封送,所以你不需要在每个 DataReceived 里手写 Invoke。
换成真实串口
先只改这一行:
var attachment = this.AttachZeus(builder => builder.AddSerialPort("meter", "COM3", 9600));
其它绑定代码不变。如果串口打不开,先确认:端口名、波特率、串口调试助手是否占用端口。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
状态一直不是 Open | 宿主启动失败或通道打开失败 | 看弹窗和控制台日志 |
| 点击发送报“当前为 Created” | 窗口还没完成 Load 或宿主未启动 | 等窗口加载完成后再发,或看状态 Label |
| 绑定后界面没刷新 | 没用 Zeus 的绑定扩展,或控件传错 | 确认调用了 BindTo / BindState |
| 换真实串口后无数据 | 程序结构没问题,问题可能在接线或参数 | 先回到虚拟通道确认界面逻辑,再查硬件 |
下一步:了解 WinForms 适配器,或直接看 串口。