C# winfrom跟新界面

一、一般使用方法

private void UpdateUIData()
{
    // 确保在主线程更新UI
    if (this.InvokeRequired)
    {
        // 使用 BeginInvoke 异步更新,避免阻塞
        this.BeginInvoke(new Action(UpdateUIData));
        return;
    }

    try
    {
        // 更新UI控件值,显示三位小数
        XposValue.Text = mReadMpos[0].ToString("0.000");
        YposValue.Text = mReadMpos[1].ToString("0.000");
    }
    catch (Exception ex)
    {
        // 捕获异常并记录日志
        LogException(ex, "更新UI数据时发生错误");
    }
}

1、调用 BeginInvoke 的作用 

if (this.InvokeRequired)
{
    this.BeginInvoke(new Action(UpdateUIData));
    return;
}

  • this.InvokeRequired:

    • 检测当前线程是否是UI线程。
    • 如果当前线程不是UI线程,则 this.InvokeRequired == true
  • this.BeginInvoke:

    • 使用 BeginInvoke 将指定的委托(这里是 new Action(UpdateUIData))加入到UI线程的消息队列中。
    • 这个委托的目标是再次调用 UpdateUIData 方法,但这次会在UI线程中执行。
  • return:

    • 由于已经将任务交给UI线程处理,当前线程无需继续执行,直接返回。

2、UpdateUIData 再次被调用

BeginInvoke 将任务(UpdateUIData 方法)提交到UI线程后,UI线程会在空闲时从消息队列中取出该任务并执行。因为此时已经在UI线程中运行,this.InvokeRequired 将返回 false,所以直接进入 else

else
{
    try
    {
        // 更新UI控件值
        XposValue.Text = mReadMpos[0].ToString("0.000");
        YposValue.Text = mReadMpos[1].ToString("0.000");
    }
    catch (Exception ex)
    {
        // 捕获异常并记录日志
        LogException(ex, "更新UI数据时发生错误");
    }
}

在这里:

  • XposValue.TextYposValue.Text 将被赋值并更新到界面上。
  • 因为当前代码已经运行在UI线程上,所以可以安全地操作这些控件。

是的,再次进入 UpdateUIData 方法时会执行 else,从而完成界面的更新。这背后的执行逻辑如下:


1. 调用 BeginInvoke 的作用


csharp

复制代码

if (this.InvokeRequired) { this.BeginInvoke(new Action(UpdateUIData)); return; }

  • this.InvokeRequired:

    • 检测当前线程是否是UI线程。
    • 如果当前线程不是UI线程,则 this.InvokeRequired == true
  • this.BeginInvoke:

    • 使用 BeginInvoke 将指定的委托(这里是 new Action(UpdateUIData))加入到UI线程的消息队列中。
    • 这个委托的目标是再次调用 UpdateUIData 方法,但这次会在UI线程中执行。
  • return:

    • 由于已经将任务交给UI线程处理,当前线程无需继续执行,直接返回。

2. UpdateUIData 再次被调用

BeginInvoke 将任务(UpdateUIData 方法)提交到UI线程后,UI线程会在空闲时从消息队列中取出该任务并执行。因为此时已经在UI线程中运行,this.InvokeRequired 将返回 false,所以直接进入 else 块。


csharp

复制代码

else { try { // 更新UI控件值 XposValue.Text = mReadMpos[0].ToString("0.000"); YposValue.Text = mReadMpos[1].ToString("0.000"); } catch (Exception ex) { // 捕获异常并记录日志 LogException(ex, "更新UI数据时发生错误"); } }

在这里:

  • XposValue.TextYposValue.Text 将被赋值并更新到界面上。
  • 因为当前代码已经运行在UI线程上,所以可以安全地操作这些控件。

3. 流程图示意

  1. 第一次调用 UpdateUIData

    • 检查 this.InvokeRequired
    • 如果当前线程不是UI线程,进入 if 块,调用 BeginInvoke 将任务提交到UI线程,然后返回。
  2. UI线程执行任务

    • UI线程从消息队列中取出任务,调用 UpdateUIData
    • 此时,this.InvokeRequired == false,直接执行 else 块,更新UI控件。

为什么再次进入时会执行 else 块?

这是因为 this.InvokeRequired 的值在 不同线程中 会有不同的结果:

  • 非UI线程 调用 UpdateUIData

    • this.InvokeRequired == true,表示当前线程不是UI线程,需要将任务切换到UI线程。
  • UI线程 执行任务时:

    • this.InvokeRequired == false,因为已经在UI线程中运行,所以可以直接更新UI控件。

整体逻辑的总结

  1. 非UI线程首次调用

    • 检测到 this.InvokeRequired == true,使用 BeginInvoke 将方法切换到UI线程并返回。
  2. UI线程执行任务

    • 在UI线程中运行 UpdateUIData 方法,this.InvokeRequired == false,执行 else 块直接更新界面。

关键点

  1. BeginInvoke 的作用

    • 将方法切换到UI线程异步执行。
    • 本质上是将委托任务加入到UI线程的消息队列中。
  2. this.InvokeRequired 的意义

    • 判断当前线程是否为UI线程。
    • 决定是否需要切换到UI线程。
  3. 再进入 UpdateUIData 的流程

    • 通过 BeginInvoke 提交的委托任务会在UI线程中执行。
    • 再次进入 UpdateUIData 时,因为已经在UI线程中,所以直接执行 else 块更新UI控件。

补充说明:为什么是安全的?

  • UI线程处理消息队列中的任务时,执行是线程安全的。
  • 因为所有对控件的操作都是在UI线程上进行的,避免了多线程竞争导致的异常或不可预测行为。

希望这个解释清晰,如果还有疑问,可以进一步交流!

二、线程安全工具:SafeInvoke 方法(如果使用)

public static void SafeInvoke(Control control, Action action)
{
    if (control.InvokeRequired)
    {
        // 如果不是主线程,切换到主线程执行操作
        control.BeginInvoke(action);
    }
    else
    {
        // 如果是主线程,直接执行操作
        action();
    }
}
private void UpdateUIData()
{
    SafeInvoke(this, () =>
    {
        try
        {
            XposValue.Text = mReadMpos[0].ToString("0.000");
            YposValue.Text = mReadMpos[1].ToString("0.000");
        }
        catch (Exception ex)
        {
            LogException(ex, "更新UI数据时发生错误");
        }
    });
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值