【问题标题】:Update UI from a different thread in a different class从不同类中的不同线程更新 UI
【发布时间】:2012-01-25 20:20:39
【问题描述】:

我有一个主窗体类,其中包含一个我想更改的列表框。该框填充了以耗时的方法创建的项目。现在看起来像这样(手动发明一个例子,可能不是有效的 C#):

List<string> strings = StaticClassHelper.GetStrings(inputString);
foreach(string s in strings)
{
    listBox1.Add(s);
}

//meanwhile, in a different class, in a different file...

public static List<string> GetStrings(inputString)
{
    List<string> result = new List<string>();
    foreach(string s in inputString.Split('c'))
    {
        result.Add(s.Reverse());
        Thread.Sleep(1000);
    }
    return result;
}

我想做的是在发现新字符串时定期更新列表框。当线程方法在同一个类中时,我发现的其他答案有效,因此您可以设置事件处理程序。我在这里做什么?

【问题讨论】:

  • 澄清一下,GetStrings 获取每个字符串的速度很慢,并且您希望在每次找到字符串时在后台更新列表框的问题?
  • @Bort:是的,就是这样。

标签: c# .net winforms multithreading backgroundworker


【解决方案1】:

我喜欢这样做,我在表单上创建一个方法,如下所示:

public void AddItemToList(string Item)
{
   if(InvokeRequired)
      Invoke(new Action<string>(AddItemToList), Item);
   else
      listBox1.Add(Item);
}

我更喜欢在这种情况下调用以确保同步添加项目,否则它们可能会出现乱序。如果您不关心订单,那么您可以使用BeginInvoke,这会快一点。由于此方法是公开的,因此您可以从应用程序中的任何类中获取所有方法,只要您可以获得对表单的引用。

这样做的另一个优点是您可以从您的 UI 线程或非 UI 线程调用它,它负责决定是否需要 Invokeing。这样你的调用者就不需要知道他们在哪个线程上运行。

更新 要解决您对如何获取对 Form 的引用的评论,通常在 Windows 窗体应用程序中,您的 Program.cs 文件看起来像这样:

static class Program
{
   static void Main() 
   {
       MyForm form = new MyForm();
       Application.Run(form);  
   }

}

这通常是我会做的,尤其是在“单一表单”应用程序的情况下:

static class Program
{
   public static MyForm MainWindow;

   static void Main() 
   {
       mainWindow = new MyForm();
       Application.Run(form);  
   }

}

然后您几乎可以在任何地方访问它:

Program.MainWindow.AddToList(...);

【讨论】:

  • 我明白了。我不明白的是如何在静态方法中引用表单。
【解决方案2】:

包含 ListBox 的类需要公开一个添加字符串的方法 - 因为这个方法可能在不同的线程上被调用,所以它需要使用

listBox1.Invoke( ...)

创建线程安全的调用机制

【讨论】:

    【解决方案3】:

    您可以将 GetStrings 重写为迭代器吗?然后在您的 UI 中,您可以启动一个后台线程,该线程遍历 GetStrings 的结果,每次都更新列表框。比如:

    public static System.Collections.IEnumerable GetStrings(inputString)
    {
        foreach(string s in inputString.Split('c'))
        {
            yield return s.Reverse();
            Thread.Sleep(1000);
        }
    }
    

    在 UI 中(假设 C# 4):

    Task.Factory.StartNew(() =>
    {
        foreach (string s in StaticClassHelper.GetStrings(inputString))
        {
            string toAdd = s;
            listBox1.Invoke(new Action(() => listBox1.Add(toAdd)));
        }
    }
    

    可能是更简洁的方法,但这应该可以满足您的需求。

    【讨论】:

      猜你喜欢
      • 2010-10-18
      • 1970-01-01
      • 2014-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-16
      相关资源
      最近更新 更多