在工业4.0的浪潮下,越来越多的C#开发者需要构建工业监控系统。但很多人在项目中遇到这样的困扰:界面逻辑和业务逻辑耦合严重,代码维护困难,测试覆盖率低。今天,我将通过一个完整的工业设备监控系统案例,带你掌握MVP架构模式的精髓,让你的C#项目结构更清晰,代码更易维护!
本文将解决三个核心问题:如何设计清晰的MVP架构、如何实现实时数据更新、如何处理异步操作中的异常。不过这个模式在Winform中我应用极少,我记得的也就是以前写过一个通讯工具。
本文将解决三个核心问题:如何设计清晰的MVP架构、如何实现实时数据更新、如何处理异步操作中的异常。不过这个模式在Winform中我应用极少,我记得的也就是以前写过一个通讯工具。
在传统的WinForms开发中,我们经常会遇到以下问题:
MVP模式正是解决这些问题的利器!
MVP模式将应用程序分为三个核心组件:

C#public class Equipment : INotifyPropertyChanged
{
private string _id;
private string _name;
private EquipmentStatus _status;
private double _temperature;
private double _pressure;
private double _speed;
private DateTime _lastUpdated;
public string Id
{
get => _id;
set
{
_id = value;
OnPropertyChanged(nameof(Id));
}
}
// 温度属性 - 支持数据绑定
public double Temperature
{
get => _temperature;
set
{
_temperature = value;
OnPropertyChanged(nameof(Temperature));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public enum EquipmentStatus
{
Stopped, // 停止
Running, // 运行中
Warning, // 警告
Error, // 故障
Maintenance // 维护中
}
💡 关键亮点:
INotifyPropertyChanged接口,支持数据绑定C#public interface IMainView
{
// 属性绑定
string SelectedEquipmentId { get; }
Equipment CurrentEquipment { get; set; }
List<Equipment> EquipmentList { set; }
// 用户交互事件
event EventHandler ViewLoaded;
event EventHandler<string> EquipmentSelected;
event EventHandler StartEquipmentClicked;
event EventHandler StopEquipmentClicked;
// 界面操作方法
void ShowMessage(string message, string title = "信息");
void ShowError(string message, string title = "错误");
void UpdateEquipmentStatus(Equipment equipment);
}
⚡ 设计亮点:
C#using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AppMVPIndustrialMonitorSystem.Models;
using AppMVPIndustrialMonitorSystem.Views;
namespace AppMVPIndustrialMonitorSystem.Presenters
{
public class MainPresenter
{
private readonly IMainView _view;
private readonly IEquipmentService _equipmentService;
public MainPresenter(IMainView view, IEquipmentService equipmentService)
{
_view = view ?? throw new ArgumentNullException(nameof(view));
_equipmentService = equipmentService ?? throw new ArgumentNullException(nameof(equipmentService));
SubscribeToEvents();
}
private void SubscribeToEvents()
{
_view.ViewLoaded += OnViewLoaded;
_view.EquipmentSelected += OnEquipmentSelected;
_view.StartEquipmentClicked += OnStartEquipmentClicked;
_view.StopEquipmentClicked += OnStopEquipmentClicked;
_view.RefreshDataClicked += OnRefreshDataClicked;
_view.UpdateParametersClicked += OnUpdateParametersClicked;
_equipmentService.EquipmentStatusChanged += OnEquipmentStatusChanged;
}
private async void OnViewLoaded(object sender, EventArgs e)
{
try
{
await LoadEquipmentListAsync();
}
catch (Exception ex)
{
_view.ShowError($"加载设备列表失败:{ex.Message}");
}
}
private async void OnEquipmentSelected(object sender, string equipmentId)
{
try
{
if (string.IsNullOrEmpty(equipmentId))
{
_view.CurrentEquipment = null;
return;
}
var equipment = await _equipmentService.GetEquipmentByIdAsync(equipmentId);
_view.CurrentEquipment = equipment;
// 加载生产数据
await LoadProductionDataAsync(equipmentId);
}
catch (Exception ex)
{
_view.ShowError($"加载设备详情失败:{ex.Message}");
}
}
private async void OnStartEquipmentClicked(object sender, EventArgs e)
{
try
{
var equipmentId = _view.SelectedEquipmentId;
if (string.IsNullOrEmpty(equipmentId))
{
_view.ShowMessage("请先选择设备");
return;
}
var success = await _equipmentService.StartEquipmentAsync(equipmentId);
if (success)
{
_view.ShowMessage("设备启动成功");
}
else
{
_view.ShowError("设备启动失败");
}
}
catch (Exception ex)
{
_view.ShowError($"启动设备失败:{ex.Message}");
}
}
private async void OnStopEquipmentClicked(object sender, EventArgs e)
{
try
{
var equipmentId = _view.SelectedEquipmentId;
if (string.IsNullOrEmpty(equipmentId))
{
_view.ShowMessage("请先选择设备");
return;
}
var success = await _equipmentService.StopEquipmentAsync(equipmentId);
if (success)
{
_view.ShowMessage("设备停止成功");
}
else
{
_view.ShowError("设备停止失败");
}
}
catch (Exception ex)
{
_view.ShowError($"停止设备失败:{ex.Message}");
}
}
private async void OnRefreshDataClicked(object sender, EventArgs e)
{
try
{
await LoadEquipmentListAsync();
var equipmentId = _view.SelectedEquipmentId;
if (!string.IsNullOrEmpty(equipmentId))
{
await LoadProductionDataAsync(equipmentId);
}
_view.ShowMessage("数据刷新成功");
}
catch (Exception ex)
{
_view.ShowError($"刷新数据失败:{ex.Message}");
}
}
private async void OnUpdateParametersClicked(object sender, EventArgs e)
{
try
{
var currentEquipment = _view.CurrentEquipment;
if (currentEquipment == null)
{
_view.ShowMessage("请先选择设备");
return;
}
var success = await _equipmentService.UpdateEquipmentAsync(currentEquipment);
if (success)
{
_view.ShowMessage("参数更新成功");
}
else
{
_view.ShowError("参数更新失败");
}
}
catch (Exception ex)
{
_view.ShowError($"更新参数失败:{ex.Message}");
}
}
private void OnEquipmentStatusChanged(object sender, Equipment equipment)
{
_view.UpdateEquipmentStatus(equipment);
}
private async Task LoadEquipmentListAsync()
{
var equipments = await _equipmentService.GetAllEquipmentAsync();
_view.EquipmentList = equipments;
}
private async Task LoadProductionDataAsync(string equipmentId)
{
var endTime = DateTime.Now;
var startTime = endTime.AddHours(-1); // 获取最近1小时的数据
var productionData = await _equipmentService.GetProductionDataAsync(equipmentId, startTime, endTime);
_view.ProductionDataList = productionData;
}
}
}
🎯 核心优势:
C#public class EquipmentService : IEquipmentService
{
private readonly List<Equipment> _equipments;
private readonly Timer _statusUpdateTimer;
private readonly Random _random;
public event EventHandler<Equipment> EquipmentStatusChanged;
public EquipmentService()
{
_random = new Random();
_equipments = GenerateTestEquipment();
// 🔥 关键:使用定时器模拟实时数据更新
_statusUpdateTimer = new Timer(UpdateEquipmentStatus, null,
TimeSpan.Zero, TimeSpan.FromSeconds(5));
}
private void UpdateEquipmentStatus(object state)
{
foreach (var equipment in _equipments.Where(e => e.Status == EquipmentStatus.Running))
{
// 模拟传感器数据变化
var tempChange = (_random.NextDouble() - 0.5) * 2;
equipment.Temperature = Math.Max(0, Math.Min(200, equipment.Temperature + tempChange));
var pressureChange = (_random.NextDouble() - 0.5) * 0.5;
equipment.Pressure = Math.Max(0, Math.Min(20, equipment.Pressure + pressureChange));
equipment.LastUpdated = DateTime.Now;
// 🚀 触发状态变更事件
EquipmentStatusChanged?.Invoke(this, equipment);
}
}
public async Task<bool> StartEquipmentAsync(string id)
{
await Task.Delay(100); // 模拟网络延迟
var equipment = _equipments.FirstOrDefault(e => e.Id == id);
if (equipment != null)
{
equipment.Status = EquipmentStatus.Running;
equipment.LastUpdated = DateTime.Now;
EquipmentStatusChanged?.Invoke(this, equipment);
return true;
}
return false;
}
}
💪 实战技巧:
Timer实现定时数据更新C#public partial class FrmMain : Form, IMainView
{
public void UpdateEquipmentStatus(Equipment equipment)
{
// 🔥 关键:处理跨线程调用
if (InvokeRequired)
{
Invoke(new Action<Equipment>(UpdateEquipmentStatus), equipment);
return;
}
// 更新列表中的设备状态
for (int i = 0; i < lstEquipment.Items.Count; i++)
{
if (lstEquipment.Items[i] is EquipmentListItem item &&
item.Equipment.Id == equipment.Id)
{
item.Equipment = equipment;
// 刷新显示
var tempItem = lstEquipment.Items[i];
lstEquipment.Items.RemoveAt(i);
lstEquipment.Items.Insert(i, tempItem);
break;
}
}
// 更新状态栏
tslStatus.Text = $"设备 {equipment.Name} 状态已更新";
}
private void UpdateStatusDisplay(EquipmentStatus status)
{
switch (status)
{
case EquipmentStatus.Running:
lblStatusValue.Text = "运行中";
lblStatusValue.ForeColor = Color.Green;
break;
case EquipmentStatus.Warning:
lblStatusValue.Text = "警告";
lblStatusValue.ForeColor = Color.Orange;
break;
case EquipmentStatus.Error:
lblStatusValue.Text = "故障";
lblStatusValue.ForeColor = Color.Red;
break;
}
}
}
C#private async void OnRefreshDataClicked(object sender, EventArgs e)
{
try
{
// 🎯 使用using确保资源释放
using (var loadingForm = new LoadingForm())
{
loadingForm.Show();
await LoadEquipmentListAsync();
var equipmentId = _view.SelectedEquipmentId;
if (!string.IsNullOrEmpty(equipmentId))
{
await LoadProductionDataAsync(equipmentId);
}
}
_view.ShowMessage("数据刷新成功");
}
catch (HttpRequestException httpEx)
{
_view.ShowError($"网络连接失败:{httpEx.Message}");
}
catch (TimeoutException timeoutEx)
{
_view.ShowError($"操作超时:{timeoutEx.Message}");
}
catch (Exception ex)
{
_view.ShowError($"刷新数据失败:{ex.Message}");
// 记录详细日志
Logger.LogError(ex, "RefreshData failed");
}
}


Timer进行定期数据更新async/await通过这个工业监控系统的完整案例,我们掌握了MVP架构在C#项目中的实际应用。这种架构模式不仅让代码结构更清晰,还大大提高了项目的可维护性和可测试性。
三个关键收获:
在实际项目中,你还可以进一步优化:添加依赖注入容器、使用SignalR实现真实的实时通信、集成日志框架等。
💬 互动时间:
你在工业项目开发中遇到过哪些架构设计难题?欢迎在评论区分享你的经验,或者提出遇到的技术问题,让我们一起探讨最佳解决方案!
觉得这篇文章对你有帮助的话,别忘了转发给更多需要的同行!🚀
相关信息
通过网盘分享的文件:AppMVPIndustrialMonitorSystem.zip 链接: https://pan.baidu.com/s/1qBqcUl89wEcYfo9LXMcEKw?pwd=wb89 提取码: wb89 --来自百度网盘超级会员v9的分享:::
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!