0

我在堆栈溢出上搜索了调用所需的相关帖子的高低..它帮助我学到了很多..但我有几个问题..不仅与调用所需有关,还与后台工作人员有关..请多多包涵.. :)

我的应用程序做了一些很长的事情,需要在进程中更新 gui(进度条、状态栏、文本框)。我使用了一个线程,但是在更新 UI 时它给出了可怕的跨线程异常。我最近(有点)掌握了正确使用invokerequired的窍门..[自动化InvokeRequired代码模式]..我在这篇文章中使用的代码是:

public static partial class CHelper
{
    public static void InvokeIfRequired(this Control oCtrl, MethodInvoker fnAction)
    {
        if (oCtrl.InvokeRequired)
        {
            oCtrl.Invoke(fnAction);
        }
        else
        {
            fnAction();
        }
    }
}

public partial class Form1 : Form
{
    public void Test()
    {
        this.InvokeIfRequired(() =>
        {
            Text = "Window Title";
            button1.Text = "Hello";
        });
        button1.InvokeIfRequired(() =>
        {
            Text = "Window Title";
            button1.Text = "Hello";
        });
    }
}

现在这是我注意到的事情.. this.InvokeIfRequired 和 button1.InvokeIfRequired 都做同样的事情.. 为什么会这样?我期待 button1.InvokeIfRequired 中的 Text 对应于 button1 的 text 属性.. 但它指的是父表单的.. 对我来说这似乎很愚蠢、不直观和错误.. 或者我做错了什么..


另一个问题..当时我没有掌握invokerequired的窍门,所以我求助于使用后台工作者..并使用进度事件来更新gui..

public partial class Form1 : Form
{
    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        backgroundWorker1.ReportProgress(0, "Hello World");
    }
    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        if (e.UserState != null)
        {
            button1.Text = e.UserState as string;
        }
    }
}

到目前为止,它还没有给我带来问题..我想知道这两种方法中哪一种更好?为什么?另外,有没有办法将数据传递给之前的 InvokeIfRequired 函数?

谢谢.. =)


编辑 01:对父窗体或目标控件使用 Invoke 有什么区别吗?.. 是的 form.invoke 和 control.invoke 是一样的.. 但为什么会这样呢?它不直观,并给人以错误的印象..

4

1 回答 1

0

好吧,对于 Button 控件来说,这根本不重要。Windows 有一个硬性规则,即一个窗口的所有子窗口必须由同一个线程拥有。因此,根据该规则,表单和按钮的 Begin/Invoke() 方法都会将调用编组到正确的线程。

请注意,当您需要更新没有 Handle 属性的组件的属性时,它会变得很尴尬。像 ToolStripButton。现在你必须选择另一个控件。所以支持一致性并始终使用表格。

当然,请支持 BackgroundWorker。它有一个诀窍,可以通过限制你射脚的方式来帮助你做到这一点。

于 2012-11-04T05:30:27.717 回答