Update UI from a different thread in a different class

偶尔善良 提交于 2020-01-15 05:51:04

问题


I have the main form class which contains a list box I want to change. The box is populated with items created in a time-consuming method. Right now it looks like this (inventing an example by hand, might not be valid 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;
}

What I would like to do instead is regularly update the list box as new strings are found. The other answers I found work when the thread method is in the same class, so you can set up an event handler. What do I do here?


回答1:


Here is how I like to do this, I create a method on the form like this:

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

I prefer invoke in this case to make sure the items are added synchronously, otherwise they can get out of order. If you don't care about the order then you can use BeginInvoke which will be a tad faster. Since this method is public, you can all it from any class in your application as long as you can get a reference to your form.

Another advantage of this is that you can call it from either your UI thread or a non-UI thread and it takes care of deciding whether or not it needs Invokeing. This way your callers don't need to be aware of which thread they are running on.

UPDATE To address your comment about how to get a reference to the Form, typically in a Windows Forms app your Program.cs file looks something like this:

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

}

This is typically what I would do, particularly in the case of a "Single Form" application:

static class Program
{
   public static MyForm MainWindow;

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

}

And then you can access it pretty much anywhere with:

Program.MainWindow.AddToList(...);



回答2:


The class containing the ListBox needs to expose a method to add a string - since this method might be called on a different thread, it needs to use

listBox1.Invoke( ...)

to create a thread-safe calling mechanism




回答3:


Would it be possible for you to rewrite GetStrings as an Iterator? Then in your UI you could start a background thread which iterates over the results of GetStrings, updating the listbox each time. Something like:

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

And in the UI (Assuming C# 4):

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

Probably cleaner ways to go about it, but this should get you what you're looking for.



来源:https://stackoverflow.com/questions/9009681/update-ui-from-a-different-thread-in-a-different-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!