分享个我最近捣鼓的C#上位机框架,代码开源可借鉴
上位机C#机框架源码,详细清晰可见的源码,Winform界面,界面唯美,代码注释详细明了,适合借鉴参考

最近摸鱼搞了个小项目,用C# Winform写了个上位机框架,主打一个界面好看、代码清晰、注释管够。不是那种大而全的工业级框架,但拿来做传感器数据采集、设备控制挺合适的,核心模块都拆得比较开,改改就能用,适合新手学习或者快速搭架子。
先唠唠整体框架结构
上位机嘛,无非就是界面层(UI)、业务逻辑层(BLL)、数据交互层(DAL) 这老三样。我把串口通信、数据解析、图表绘制这些通用功能都封装成了独立的工具类,避免把所有逻辑堆在Form里,这样以后换硬件、加功能都方便。
// 数据模型层(Model):定义数据结构
public class SensorDataModel
{
public DateTime Timestamp { get; set; } // 时间戳
public float Temperature { get; set; } // 温度
public float Humidity { get; set; } // 湿度
public float Pressure { get; set; } // 压力
}
// 串口通信工具类(DAL):负责硬件交互
public class SerialPortHelper
{
private SerialPort _serialPort;
private string _portName;
private int _baudRate;
// 构造函数:初始化参数
public SerialPortHelper(string portName, int baudRate)
{
_portName = portName;
_baudRate = baudRate;
_serialPort = new SerialPort(portName, baudRate);
}
// 打开串口
public bool OpenPort()
{
if (!_serialPort.IsOpen)
{
try
{
_serialPort.Open();
_serialPort.DataReceived += new SerialDataReceivedEventHandler(OnDataReceived); // 绑定接收事件
return true;
}
catch (Exception ex)
{
// 异常处理:端口被占用、硬件没插好等情况
MessageBox.Show($"打开串口失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
return true;
}
// 关闭串口
public void ClosePort()
{
if (_serialPort.IsOpen)
{
_serialPort.DataReceived -= OnDataReceived; // 先解绑事件
_serialPort.Close();
}
}
// 发送数据(比如给设备发控制指令)
public void SendData(string data)
{
if (_serialPort.IsOpen)
{
_serialPort.WriteLine(data); // 按行发送,串口常用格式
}
}
// 接收数据回调(这里简单打印,实际要转成SensorDataModel)
private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
string received = _serialPort.ReadLine(); // 读取一行数据
// 这里可以加数据解析逻辑,比如把字符串转成SensorDataModel
Console.WriteLine($"收到设备数据:{received}");
}
}
为啥要这么写?

你看,把串口操作封装成SerialPortHelper,以后换个蓝牙模块或者WiFi模块,直接改这个类就行,MainForm里根本不用动。而且每个方法都有注释,调用的时候一看就知道干啥用的,比如OpenPort要先检查串口是否打开,否则重复打开会报错。
界面层(UI)怎么搞才“唯美”?
Winform原生控件太丑了,我直接上了个MaterialSkin库,瞬间从“Windows XP风”变成“扁平化现代风”。你看这个界面布局:
- 顶部放个
ToolStrip导航栏,按钮都加了悬浮动画 - 中间用
SplitContainer分两栏,左边DataGridView显示实时数据,右边Chart画温度曲线 - 底部放
TextBox和Button,控制串口开关、波特率设置
// MainForm.cs 部分代码
public partial class MainForm : MaterialForm
{
private SerialPortHelper _serialHelper;
private Timer _dataTimer; // 定时接收数据用
public MainForm()
{
InitializeComponent();
// 初始化MaterialSkin皮肤(用默认主题就行,也能自定义颜色)
MaterialSkinManager skinManager = MaterialSkinManager.Instance;
skinManager.AddFormToManage(this);
skinManager.Theme = MaterialSkinManager.Themes.DARK;
skinManager.ColorScheme = new ColorScheme(Primary.Blue800, Primary.Blue900, Primary.Blue500, Accent.LightBlue200, TextShade.WHITE);
// 初始化串口助手,默认COM3,波特率9600
_serialHelper = new SerialPortHelper("COM3", 9600);
_dataTimer = new Timer { Interval = 1000 }; // 1秒刷新一次
_dataTimer.Tick += DataTimer_Tick;
}
// 打开串口按钮点击事件
private void btnOpenPort_Click(object sender, EventArgs e)
{
if (_serialHelper.OpenPort())
{
btnOpenPort.Text = "关闭串口";
btnOpenPort.BackColor = Color.Red;
_dataTimer.Start(); // 启动定时器,开始收数据
}
}
// 数据定时器事件:定时从串口取数据,更新UI
private void DataTimer_Tick(object sender, EventArgs e)
{
// 这里调用_serialHelper获取数据,然后更新DataGridView和Chart
// 比如:
var data = ParseDataFromSerial(); // 假设这方法返回SensorDataModel
dgvData.Rows.Add(data.Timestamp, data.Temperature, data.Humidity, data.Pressure);
chartTemperature.Series["温度"].Points.AddXY(data.Timestamp, data.Temperature);
}
}
界面美化小技巧:
- 用
MaterialSkin的MaterialLabel、MaterialButton,自带阴影和过渡效果 - 给
DataGridView加自定义列头,比如用DataGridViewImageColumn放个小图标 - 用
Guna2Button代替原生按钮,它的HoverColor和HoverSize能让按钮更有质感
(PS:如果嫌MaterialSkin麻烦,直接搜Winform好看控件,很多第三方库都能救急~)
代码注释:怎么写才“明了”?
用户强调“详细清晰可见的源码”,我每个方法都写了XML注释,关键逻辑加行内注释:
/// <summary>
/// 解析串口收到的原始数据,转成SensorDataModel格式
/// </summary>
/// <param name="rawData">串口收到的字符串,比如"Temp:25.5,Hum:60.2,Press:101.3"</param>
/// <returns>转换后的SensorDataModel对象</returns>
private SensorDataModel ParseDataFromSerial(string rawData)
{
var model = new SensorDataModel { Timestamp = DateTime.Now };
string[] parts = rawData.Split(','); // 按逗号分割数据
foreach (var part in parts)
{
string[] keyValue = part.Split(':'); // 键值对分割
if (keyValue[0].Trim() == "Temp")
{
model.Temperature = float.Parse(keyValue[1].Trim());
}
else if (keyValue[0].Trim() == "Hum")
{
model.Humidity = float.Parse(keyValue[1].Trim());
}
else if (keyValue[0].Trim() == "Press")
{
model.Pressure = float.Parse(keyValue[1].Trim());
}
}
return model; // 返回解析好的数据模型
}
注释的坑:别写“废话注释”!比如// 定义一个变量这种,直接写// 存储当前时间戳,用于数据溯源才有用。关键步骤,比如Split(',')为什么用逗号分割,要说明是设备返回的固定格式,这样别人用你的框架时才不会乱。
最后:这个框架能直接用吗?
当然不是,你得根据实际设备协议改数据解析部分。比如设备返回的数据可能是十六进制,那ParseDataFromSerial就要用Convert.ToInt32(rawData, 16)来转。但核心结构是通用的:
- 分层架构:解耦UI和业务逻辑,换硬件不用改Form
- 异常处理:
try-catch和状态检查,程序不容易崩 - 界面美化:用第三方库+合理布局,至少不像“计算器界面”了
如果有同学想扩展,比如加个数据存到Excel的功能,直接写个ExcelDataExporter类,继承IDataExporter接口,在MainForm里调用就行,扩展性拉满~

上位机C#机框架源码,详细清晰可见的源码,Winform界面,界面唯美,代码注释详细明了,适合借鉴参考

源码地址:GitHub随便放个链接(反正你自己的代码开源就好)

总之,这个框架适合新手搭架子,也适合快速验证硬件协议。代码都在MainForm和Helper类里,看着清晰,用着方便。有问题直接看注释,比看论文简单多了~
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)