Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET多线程。动态更新UI_C#_.net_Vb.net_Multithreading - Fatal编程技术网

C# .NET多线程。动态更新UI

C# .NET多线程。动态更新UI,c#,.net,vb.net,multithreading,C#,.net,Vb.net,Multithreading,我有一个按钮点击事件,在里面我做了很多工作 当我在做这项工作时(不是从UI项目),我想向UI发送消息以显示给用户 我正在做一些类似于: void Button_Click(object sender, EventArgs e) { DoMyWork(SendMessage); { public void SendMessage(string message) { TextBox1.Text = message + "\n"

我有一个按钮点击事件,在里面我做了很多工作

当我在做这项工作时(不是从UI项目),我想向UI发送消息以显示给用户

我正在做一些类似于:

    void Button_Click(object sender, EventArgs e)
    {
        DoMyWork(SendMessage);
    {

    public void SendMessage(string message)
    {
        TextBox1.Text = message + "\n"; 
    }
在另一个程序集中的DoMyWork中,我可以调用SendMessage,它会写入文本框。但这些消息仅在DoMyWork完成时显示

如何在不将DoMyWork放在后台线程上的情况下动态更新TextBox1.Text,还是必须将其放在后台线程上?我确实想阻止用户在DoMyWork运行时执行任何操作


谢谢

您需要将代码放入线程中。在启动线程之前,请禁用任何UI。当线程死亡时,重新启用UI


从工作线程更新UI时,请在控件上使用Invoke方法,因为所有UI更改都必须来自“UI线程”。

您可以使用
BackgroundWorker

要从另一个线程更新UI,请使用Control.Invoke方法-如前所述。您可以使用BackgroundWorker在另一个线程中实际执行工作(否则UI将变得无响应),或者滚动您自己的线程-BackgroundWorker只是一个帮助器类,但使调用Application.DoEvents()更简单;设置文本框文本后,确实需要将长时间运行的操作放在单独的线程上。如果不这样做,UI将变得无响应,用户将认为它已挂断。有两种启动辅助线程的常用方法

  • 使用该类
  • 手动创建并启动一个
使用工作线程生成的数据安全地更新UI有两种普遍接受的方法

  • 让工作线程通过
    控件推送通知。调用
  • 让UI线程定期轮询工作线程更新的共享数据结构
当将
ReportProgress
方法与
ProgressChanged
事件结合使用时,
BackgroundWorker
类固有地使用push方法(通过
Control.Invoke
)来报告进度

通过手动创建线程,您可以更好地控制UI和工作线程交互的发生方式,而不必自己连接所有管道


由于我不知道长时间运行操作的细节,也不知道需要显示的消息的性质,所以我不能说某些方向最适合您。从
BackgroundWorker
方法开始可能没问题。

BackgroundWorker
是一门很棒的课程。如果不希望用户在进程运行时使用表单,可以将其
Enabled
属性设置为
false
。但是您不想在长时间运行的过程中阻塞UI线程。

不知道您的开发状态,您可能无法等待C#5。但是,您应该看看“async-await”语言特性在该领域的应用: 目前的方法可能有点难以让你改变主意,但他们确实解决了这个问题


GJ

将其作为任务或后台工作人员是最佳解决方案。但是,您可以将消息从常规线程传送到UI

在thread类中需要一个委托子类

delegate void UpdateTextHandler(string msg);
线程内的代码

private void UpdateUI(string msg)
{
    form1 f = My.Application.OpenForms("form1 ");
    f.Invoke(new UpdateTextHandler(f.UpdateLabel), new object[] { msg });
}
UI中的代码

public void UpdateLabel(string msg)
{
    label1.Text = msg;
}
基本上,您在这里试图做的只是调用具有UI的实际线程并向其发送消息


不幸的是,这并不能真正防止用户实际上无法成为任何人。您还必须通过并禁用控件

我通常用于这类目的的代码是,它最初用于从子用户控件捕获UpdateStatus事件,但在这方面应该同样有效

    Delegate Sub SetStatus(ByVal sender As Object, ByVal text As String)

    Private Sub UpdateStatus_Event(ByVal sender As Object, ByVal str As String)
     If tsStatus.InvokeRequired Then
        Dim delStatus As New SetStatus(AddressOf UpdateStatus_Event)
        Me.Invoke(delStatus, New Object() {sender, str})
     Else
        tsStatus.Text = str
        Application.DoEvents()
     End If
    End Sub

    Private Sub btn_Click(Byval sender as Object, Byval e as EventArgs) Handles btn1.Click
     Dim oTh as New Threading.Thread(AddressOf WorkSub)
     oTh.Start()
    End Sub

    Private Sub WorkSub()
     ALotOfWork()
     UpdateStatus_Event(btn1,"message")
    End Sub

补充:如果您不在另一个线程中运行它,那么您的应用程序看起来将挂起,并且将变得无响应。如果你按照Nick说的去做并禁用UI,但是继续提供更新并包括一些停止你正在做的事情的方法(如果可能的话),这是实现你正在尝试做的事情的最好方法。我认为消息抽取技术有很大的缺陷。重新进入,决定在哪里抽水。。。极坏的c#中的线程非常简单,可以随时使用。