C# Windows窗体应用程序-如何从另一个线程和类更新GUI?

8

我已经搜索了很多,但好像找不到与我的具体问题相关的任何内容。

我想能够从另一个类(SocketListener)更新我的MainUI表单,在其中我有一个处理网络的线程(clientThread)。目前,我可以从网络线程运行简单的输出,例如写入调试器输出并创建MessageBox。

但是我真正想做的是能够调用来自clientThread的代码,在我的MainUI实例上执行操作。我该怎么做呢?

此外,如果有人想要特定部分的代码,那么我可以发布它以帮助您更好地理解我的问题。

最好的问候!

3个回答

17

检查Control类的InvokeRequired,如果为真,则调用Invoke并传递一个委托(通常是匿名方法),该委托在客户端线程上执行所需操作。

示例:

public void DoWork(Form form)
{
    if (form.InvokeRequired)
    {
        // We're on a thread other than the GUI thread
        form.Invoke(new MethodInvoker(() => DoWork(form)));
        return;
    }

    // Do what you need to do to the form here
    form.Text = "Foo";
}

由于我所在的类与UI不同,我该如何获取指向当前运行表单的变量“form”,以便将其传递给DoWork? - Luke S.
这种样板代码使用类似于自动属性的语法糖不是很好吗? - MusiGenesis
1
@firstEncounter:你可以在你的类中添加一个以Form为参数的构造函数,然后将引用存储在类级变量中。 - MusiGenesis
你需要将想要修改的控件的可见性更改为public或internal。或者,在你的表单类中创建一个方法来更改你想要更改的控件,并调用它。无论哪种方式,你都需要在构造函数(或从任何地方获取表单实例的地方)中向下转换为你派生的表单类型。 - user153498
@firstEncounter,你是如何更改表单属性的?我在这方面遇到了一些问题。我正在开发一个非常类似的程序。 - Bubo

4
是的,您可以给您的类添加一个以MainUI表单为参数的构造函数。这是快速的方法,但它引入了一个“反向”依赖,从您的类到MainUI,理论上不需要任何依赖关系。
作为替代方案,建议您在您的类中添加一个公共事件,然后MainUI表单可以订阅该事件。当您的类需要更新MainUI(或MainUI内的控件)时,该类将简单地“触发”该事件。这将调用MainUI在订阅时注册的方法。由于它已经是MainUI表单的方法,所以您只需更新表单上的适当控件,确保考虑每个控件的InvokeRequired属性。
在这样做时,这是我在所有代码中使用的结构。
class NetworkEventArgs : EventArgs { /* defined class here */ }
private void NetworkEventHandler(object sender, NetworkEventArgs e)
{ 
    Invoke( ( MethodInvoker ) delegate {
        myTextBox.Text = e.Message;
    }
}

我基于这篇博客文章(链接)。我从未遇到过问题,所以我认为没有必要在代码中增加对InvokeRequired属性的递归检查,以免让代码变得复杂。


我发现这种代码结构对我非常有效。我想补充一下,我曾经在使用Invoke/MethodInvoker时遇到了问题,我的GUI会卡住。我将其改为BeginInvoke(new MyDelegate(MyDelegateMethod),myParam); 现在运行得非常好。 - Tony D

0

你可以在clientThread类中定义一个事件

并在mainform中处理它,当clientThread需要让mainform执行某些操作(例如更新某些控件状态)时,你应该触发该事件

这样,mainform就可以从事件中获取参数并调用更新函数


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接