You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 11:27:15