从非ui线程更新控件

时间:2015-11-11 20:52:49

标签: c# multithreading

伙计我知道这个问题已被多次询问,但我仍然找不到对我有用的单一回复。

我的表格上有超过400个控件。

我有一个后台线程,可以轮询一堆设备并收集要在表单上显示的各种数据。

然后我调用一个方法" UpdateDisplay(string [] data)"。这个例程获取字符串数组data []中的所有信息,并填写表单上的所有组件。我填写了标签,文本框。显示和隐藏了面板和TableLayouts以及其他控件。

他们的数百倍!!

如果我必须测试每个组件以查看我是否必须调用我的程序将变成50亿行代码!!!!!

有没有办法简单地看看是否需要在UI线程上调用整个UpdateDisplay方法,而不是它接触到的所有400多个组件?

我输入以下代码:

    if (InvokeRequired)
    {
        BeginInvoke(new MethodInvoker(() => UpdateDisplay(data)));
    }

作为display方法中的第一个语句,我不再获得有关从非ui线程调用ui组件的运行时异常。

接下来是更新方法的其余部分,其中数百个组件正在使用data []

中的信息进行更新

但是现在我在System.Forms.dll中收到了一堆System.InvalidOperationException ???

如果我将调试异常选项设置为中断所有invalidoperationexceptions,我会看到在使用关于从非ui线程更新组件的讨厌的内容更新UpdateDisplay方法中的组件时,它们会被随机抛出。

有人可以帮我理解并解决这个问题吗?

我可以发布整个UpdateDisplay方法,以显示如果我必须使用invokerequired if语句包装每个组件更新调用,那将是多么荒谬。不夸张它会增加每个控件三行代码或大约1200行额外代码!那太疯狂了!

3 个答案:

答案 0 :(得分:2)

这是我如何使用来自另一个线程的消息更新ListBox的示例。

private void WriteProgressMessage(string message)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new Action(() => this.WriteProgressMessage(message)));
    }
    else
    {
        this.ProgressList.Items.Add(message);
        this.ProgressList.SelectedIndex = this.ProgressList.Items.Count - 1;
        this.ProgressList.SelectedIndex = -1;
    }
}

答案 1 :(得分:2)

您没有提供足够的代码,但如果您的方法看起来像这样:

void UpdateDisplay(string[] data)
{
    if (InvokeRequired)
    {
        BeginInvoke(new MethodInvoker(() => UpdateDisplay(data)));
    }
    Label1.Text = data[0];
    // Update more controls here
}

然后您运行UpdateDisplay两次,一次在UI线程上由于BeginInvoke而在BeginInvoke返回时在工作线程上运行一次。通常的模式是,如果您使用InvokeBeginInvoke来自行调用,则该方法会立即返回并且不会进一步处理:

void UpdateDisplay(string[] data)
{
    if (InvokeRequired)
    {
        BeginInvoke(new MethodInvoker(() => UpdateDisplay(data)));
        return; // Don't run any code below when BeginInvoke returns
    }
    Label1.Text = data[0];
    // Update more controls here
}

JimmyV还提供了一个替代版本,在BeginInvoke

之后也没有进一步的工作
void UpdateDisplay(string[] data)
{
    if (InvokeRequired)
    {
        BeginInvoke(new MethodInvoker(() => UpdateDisplay(data)));
    }
    else // Don't run any code below when BeginInvoke returns
    {
        Label1.Text = data[0];
        // Update more controls here
    }
}

答案 2 :(得分:0)

一种可能性是,由于您使用BeingInvokerather than Invoke来更新UI线程,因此可能存在同步问题。在没有看到更完整的代码示例的情况下很难分辨,请尝试拨打Invoke并查看是否获得更少的错误