Обновление пользовательского интерфейса из другого потока в другом классе - PullRequest
2 голосов
/ 26 января 2012

У меня есть основной класс формы, который содержит список, который я хочу изменить.Поле заполняется элементами, созданными с использованием трудоемкого метода.Прямо сейчас это выглядит так (изобретение примера вручную может быть недопустимым 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 ]

4 голосов
/ 26 января 2012

Вот как мне нравится это делать, я создаю метод на форме следующим образом:

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

Я предпочитаю вызывать в этом случае, чтобы убедиться, что элементы добавляются синхронно, иначе они могут выйти из строя. Если вы не заботитесь о заказе, вы можете использовать BeginInvoke, что будет немного быстрее. Поскольку этот метод общедоступен, вы можете получить все это из любого класса в вашем приложении, если можете получить ссылку на свою форму.

Еще одним преимуществом этого является то, что вы можете вызывать его из вашего потока пользовательского интерфейса или потока не из пользовательского интерфейса, и он позаботится о том, чтобы решить, нужно ли ему Invoke ING. Таким образом, ваши абоненты не должны знать, в каком потоке они работают.

UPDATE Чтобы ответить на ваш комментарий о том, как получить ссылку на Form, обычно в приложении Windows Forms, ваш файл Program.cs выглядит примерно так:

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

}

Как правило, это то, что я бы сделал, особенно в случае приложения "Single Form":

static class Program
{
   public static MyForm MainWindow;

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

}

И тогда вы можете получить к нему доступ практически в любом месте с помощью:

Program.MainWindow.AddToList(...);
0 голосов
/ 26 января 2012

Можно ли переписать GetStrings как итератор? Затем в вашем пользовательском интерфейсе вы можете запустить фоновый поток, который перебирает результаты GetStrings, обновляя список каждый раз. Что-то вроде:

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

И в пользовательском интерфейсе (при условии C # 4):

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

Возможно, более чистые способы сделать это, но это должно дать вам то, что вы ищете.

0 голосов
/ 26 января 2012

Класс, содержащий ListBox, должен предоставлять метод для добавления строки - поскольку этот метод может вызываться в другом потоке, он должен использовать

listBox1.Invoke( ...)

для создания механизма вызова, безопасного для потока

...