C# WinForms如何将父窗体电机位置数据传递至子窗体绘图
问题根因
- 你之前的所有方案都没有把转换完成的有效位置数据传递给子窗体:子窗体的
ShowPosition方法用的是默认无数据的EventArgs参数,要么直接访问父窗体的变量遇到多线程值同步问题,要么子窗体自身的posZ字段从未被正确赋值,拿不到实时位置。 - 方法调用时机错误:你贴出的父窗体轮询代码里,仅在UI线程回调了父窗体自身的
ShowPosition方法,没有在单位转换完成、父窗体UI更新结束的正确时机通知子窗体更新。 - 跨线程/跨窗体直接访问字段存在安全问题:后台轮询线程和UI线程同时读写位置字段,会出现内存可见性问题、值不一致问题,Winform控件默认禁止跨线程调用,直接操作会静默失败不抛异常。
- 你用构造函数传值的方案逻辑完全不成立:构造函数仅在子窗体实例化时执行一次,后续每300ms的位置更新不会触发构造函数,自然拿不到实时数据。
修复步骤
1. 定义带位置数据的自定义事件参数
不要用默认的空EventArgs,直接把转换好的三轴位置通过事件参数传递,彻底避免跨窗体访问变量的问题:
public class PositionUpdatedEventArgs : EventArgs { public double PosX { get; } public double PosY { get; } public double PosZ { get; } public PositionUpdatedEventArgs(double posX, double posY, double posZ) { PosX = posX; PosY = posY; PosZ = posZ; } }
2. 修改父窗体逻辑
首先在父窗体类内定义公开的位置更新事件,解耦父子窗体的依赖:
// 父窗体类内添加事件定义 public event EventHandler<PositionUpdatedEventArgs> PositionUpdated;
修改父窗体的ShowPosition方法,在完成单位转换、更新完自身UI后,触发事件通知所有订阅者(包含绘图子窗体),所有逻辑都在UI线程执行,避免多线程冲突:
private void ShowPosition(object sender, EventArgs e) { // UI线程内完成单位转换,避免多线程读写变量冲突 double convertedX = devices.deviceUnitConverterX.DeviceUnitToRoundedReal64( positionX, DeviceUnitConverter.UnitType.Length); double convertedY = devices.deviceUnitConverterY.DeviceUnitToRoundedReal64( positionY, DeviceUnitConverter.UnitType.Length); double convertedZ = devices.deviceUnitConverterZ.DeviceUnitToRoundedReal64( positionZ, DeviceUnitConverter.UnitType.Length); // 更新父窗体自身显示 posX = convertedX; posY = convertedY; posZ = convertedZ; Lbl_posX.Text = $"{posX} mm"; Lbl_posY.Text = $"{posY} mm"; Lbl_posZ.Text = $"{posZ} mm"; // 通知所有订阅者位置已更新,传入转换完成的有效数据 PositionUpdated?.Invoke(this, new PositionUpdatedEventArgs(convertedX, convertedY, convertedZ)); }
父窗体原有的GetPosition后台轮询逻辑不需要修改,保持原有300ms轮询、BeginInvoke回调UI线程的逻辑即可。
3. 修改子窗体逻辑
修改子窗体的ShowPosition方法签名,直接从事件参数中获取位置值,不要直接访问父窗体的字段:
public void ShowPosition(object sender, PositionUpdatedEventArgs e) { if (displayZ) { var series = chart1.Series["Z stage"]; // 限制最大显示点数,避免长时间运行内存占用过高 if (series.Points.Count > 1000) { series.Points.RemoveAt(0); } // 直接从事件参数取Z轴位置添加到图表 series.Points.Add(e.PosZ); // 自动调整坐标轴范围,实现滚动显示效果 chart1.ResetAutoValues(); } }
4. 绑定事件(打开子窗体时添加)
在父窗体打开子窗体的代码中,完成事件订阅,同时在子窗体关闭时取消订阅避免内存泄漏:
private void Btn_OpenChart_Click(object sender, EventArgs e) { ChildForm chartForm = new ChildForm(); // 订阅位置更新事件 this.PositionUpdated += chartForm.ShowPosition; // 窗体关闭时取消订阅、释放资源 chartForm.FormClosed += (s, args) => { this.PositionUpdated -= chartForm.ShowPosition; chartForm.Dispose(); }; chartForm.Show(); }
额外排查点
如果改完还是不显示,检查以下配置:
- 确认子窗体的
displayZ变量已被正确赋值为true - 确认图表中
"Z stage"系列确实存在,ChartType已设置为Spline(样条曲线) - 确认图表系列的坐标轴ValueType设置为数值类型,不要设置为字符串类型导致绘图异常
内容的提问来源于stack exchange,提问作者Christree
相关产品推荐
相关产品推荐

