从不同类中的其他线程更新UI

时间:2012-01-25 20:20:39

标签: c# .net winforms multithreading backgroundworker

我有一个主窗体类,其中包含一个我想要更改的列表框。该框中填充了以耗时方法创建的项目。现在它看起来像这样(手工发明一个例子,可能不是有效的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;
}

我想要做的是定期更新列表框,因为找到了新的字符串。当线程方法在同一个类中时,我发现其他答案,所以你可以设置一个事件处理程序。我该怎么办?

3 个答案:

答案 0 :(得分:4)

我喜欢这样做,我在表单上创建了一个方法:

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

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

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

<强>更新 要解决有关如何获取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(...);

答案 1 :(得分:0)

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

listBox1.Invoke( ...)

创建一个线程安全的调用机制

答案 2 :(得分:0)

您是否可以将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)));
    }
}

可能更清洁的方法,但这应该可以帮到你找到你想要的东西。