前言
做上位机界面开发的朋友应该都清楚,从零搭建一套功能完整的可视化工具界面有多折腾——拖控件、绑数据、调通信、写脚本,每个环节都得自己撸一遍。
一套面向工业监控、设备管理、数据可视化场景的桌面端画面设计系统。这篇文章就聊聊它的整体思路、具体功能和我们实际落地时的考量。
项目介绍
项目本质上是一个所见即所得的画面设计器加运行时引擎,你可以把它理解成"工业级PPT + 脚本环境 + 通信网关"的混合体。它不像传统组态软件那样笨重,也不像通用UI框架那样需要写大量前端代码。
核心定位是:让实施人员通过拖拽配置就能完成大部分界面工作,遇到复杂逻辑时又能用C#脚本灵活兜底。
整个工程基于.NET 8开发,渲染层用SkiaSharp保证跨平台一致性,脚本引擎走Roslyn动态编译,通信层支持Modbus、OPC UA、MQTT等主流协议。
项目功能
按实际使用流程把功能拆成八个模块,下面这张表可以快速看清全貌:
项目特点
配置优先,脚本兜底:大部分交互逻辑通过数据绑定和表达式就能搞定,实在复杂的再上C#脚本,不至于把简单事情搞复杂。
插件化第一:从控件到通信驱动全部走插件加载,第三方集成时不用改主程序代码。
跨平台发布:一套工程既能出Windows版本也能出Linux版本,界面渲染保持一致。
调试体验友好:变量监控、脚本断点、通信报文收发都在一个环境里完成,不用来回切工具。
资源管理严谨:引用溯源和未使用标记功能避免工程膨胀,多人协作时减少冲突。
项目技术
项目代码(核心片段)
变量绑定与表达式求值的简化实现:
// 变量绑定核心:属性变化时同步更新绑定的变量publicclassBindingExpression{privatereadonlystring _expression;privatereadonly Dictionary<string, IVariable> _variables;publicobjectEvaluate() {// 解析表达式中的变量占位符,如 [温度]var parsed = _expression.Replace("[", "").Replace("]", "");if (_variables.TryGetValue(parsed, outvarvar)) {// 如果是简单变量直接返回值returnvar.GetValue(); }// 如果是复杂表达式,调用内置函数库解析return ExpressionParser.Parse(_expression, _variables); }}// 控件属性绑定示例:将按钮文本绑定到变量 "设备状态"var binding = new BindingExpression("[设备状态]", variableManager);button.TextBinding = binding;// 变量变化时自动刷新控件variableManager.OnVariableChanged += (name) => {if (name == "设备状态") button.Text = binding.Evaluate().ToString();};插件加载机制的简化版本:
[Export(typeof(IControlPlugin))]publicclassGaugePlugin : IControlPlugin{publicstring DisplayName => "仪表盘";publicvoidRender(SKCanvas canvas, ControlProps props) {// 使用SkiaSharp绘制刻度、指针和数值 DrawGauge(canvas, props.Value, props.Min, props.Max); }public List<PropertyDescriptor> GetProperties() {// 返回属性定义,自动映射到属性面板returnnew List<PropertyDescriptor> {new FloatProperty("MinValue", 0),new FloatProperty("MaxValue", 100),new IntProperty("TickCount", 10) }; }}项目效果
通信稳定性方面,在Modbus TCP轮询100个寄存器、100ms间隔的场景下,CPU占用控制在5%以内,内存稳定在120MB左右。脚本热重载功能在现场调试时非常救命——修逻辑不用停运行中的画面,直接改完保存就能生效。



预览和发布的一致性做得比较到位,编辑时看到什么样,发布的exe跑起来就是什么样,没出现过"编辑时好好的,打包后变形"的情况。资源管理里的引用溯源功能帮我们清理了好几次历史遗留的未用图片,工程体积从800多MB降到了200多MB。

项目源码

总结
这套系统的初衷很简单——不想每次做项目都重复造界面和通信的轮子。从实际效果来看,配置驱动加脚本兜底的模式确实能兼顾效率和灵活性。技术选型上.NET生态的跨平台能力和Roslyn的脚本编译能力是两大支柱,SkiaSharp保证了一致的渲染表现。当然,不足也很明显:复杂动画的性能还有优化空间,表达式引擎目前不支持自定义函数扩展,这些是下一版本要补的短板。
如果大家是做设备监控、产线看板、数据中台前端这类工作,这套思路或许能给你一些参考。工具终究是工具,关键还是用对场景,我们不追求大而全,只求在工业界面这块领域里做得顺手、踏实。
关键词
画面设计器、数据绑定、C#脚本引擎、Roslyn编译、工业组态、Modbus通信、OPC UA、SkiaSharp渲染、插件化架构、变量监控、跨平台发布、表达式求值、热重载、MEF插件、Avalonia UI
作者:小码编匠
.NET 8 + Cursor 编写的工业 Web SCADA 系统
.NET 8 + WPF + Material Design 开发的工业传感器实时数据监控系统
.NET 8 + WPF 开发的工业设备日志 AI 分析工具
.NET 工业上位机20篇实战精选(SCADA/Modbus/视觉/运动控制)
WPF 搭建的现代化工业级数据采集与监控终端 (SCADA/HMI) 系统
WPF + Modbus RTU 一套 SCADA监控系统的实现
工业上位机开发没头绪?这个 WPF 模板把 SCADA 和大屏都给整明白了
WPF + ONNX Runtime 实现 YOLOv5 目标检测桌面应用
WPF 一款轻量级多服务器远程管理工具,本地 JSON 存储,开箱即用
C# 视觉检测平台(支持海康工业相机+OpenCV 算子流程编排+串口通信)
C# 实现 SCADA + 看板 + MES 接口,一套能落地的工业方案
.NET 8 工业自动化流程编辑器,不用写代码,拖拽生成PLC数据流
.NET 10 实现工业调试必备的通信工具(支持串口/Modbus/TCP/OPC UA)
觉得有收获?不妨分享让更多人受益
关注「DotNet技术匠」,共同提升技术实力




夜雨聆风