从另一个线程更新标签的最简单方法是什么?

我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。

我怎么能这样做?


当前回答

最简单的方法是调用如下:

 Application.Current.Dispatcher.Invoke(new Action(() =>
             {
                    try
                    {
                        ///
                    }
                    catch (Exception)
                    {
                      //
                    }


                    }
     ));

其他回答

您可以使用现有的委托操作:

private void UpdateMethod()
{
    if (InvokeRequired)
    {
        Invoke(new Action(UpdateMethod));
    }
}

当我遇到同样的问题时,我向谷歌寻求帮助,但它并没有给我一个简单的解决方案,而是举了MethodInvoker和blah blah的例子,让我更加困惑。所以我决定自己解决。以下是我的解决方案:

制作一个这样的代理:

Public delegate void LabelDelegate(string s);

void Updatelabel(string text)
{
   if (label.InvokeRequired)
   {
       LabelDelegate LDEL = new LabelDelegate(Updatelabel);
       label.Invoke(LDEL, text);
   }
   else
       label.Text = text
}

您可以像这样在新线程中调用此函数

Thread th = new Thread(() => Updatelabel("Hello World"));
th.start();

不要与线程(()=>…..)混淆。在处理线程时,我使用匿名函数或lambda表达式。为了减少代码行,您也可以使用ThreadStart(..)方法,我不打算在这里解释。

致敬!在搜索了这个问题之后,我发现FrankG和Oregon Ghost的答案对我来说是最简单、最有用的;所以我不太确定结果如何。

我有一个名为form_Diagnostics的对话框,它有一个称为updateDiagWindow的富文本框,我正在使用它作为一种日志显示。我需要能够从所有线程更新它的文本。额外的行允许窗口自动滚动到最新的行。

因此,我现在可以从整个程序的任何地方,以您认为无需任何线程的方式,用一行代码更新显示:

  form_Diagnostics.updateDiagWindow(whatmessage);

主代码(将其放入表单的类代码中):

#region "---------Update Diag Window Text------------------------------------"
// This sub allows the diag window to be updated by all threads
public void updateDiagWindow(string whatmessage)
{
    var _with1 = diagwindow;
    if (_with1.InvokeRequired) {
        _with1.Invoke(new UpdateDiagDelegate(UpdateDiag), whatmessage);
    } else {
        UpdateDiag(whatmessage);
    }
}
// This next line makes the private UpdateDiagWindow available to all threads
private delegate void UpdateDiagDelegate(string whatmessage);
private void UpdateDiag(string whatmessage)
{
    var _with2 = diagwindow;
    _with2.appendtext(whatmessage);
    _with2.SelectionStart = _with2.Text.Length;
    _with2.ScrollToCaret();
}
#endregion
Label lblText; //initialized elsewhere

void AssignLabel(string text)
{
   if (InvokeRequired)
   {
      BeginInvoke((Action<string>)AssignLabel, text);
      return;
   }

   lblText.Text = text;           
}

请注意,BeginInvoke()比Invoke(()更受欢迎,因为它不太可能导致死锁(然而,在这里,将文本分配给标签时,这不是问题):

使用Invoke()时,您正在等待方法返回。现在,可能是您在调用的代码中执行了一些需要等待线程的操作,如果它隐藏在您正在调用的某些函数中,这可能不会立即显现出来,而这本身可能会通过事件处理程序间接发生。因此,您将等待线程,线程将等待您,并且您处于死锁状态。

这实际上导致了我们发布的一些软件挂起。用BeginInvoke()替换Invoke(。除非需要同步操作(如果需要返回值,则可能是这种情况),否则请使用BeginInvoke()。

为了在WPF中实现这一点,我采用以下方式。

 new Thread(() => 
 {
     while (...)
     {
         SomeLabel.Dispatcher.BeginInvoke((Action)(() => SomeLabel.Text = ...));
     }
 }).Start();