WPF C # - редактирование списка из другого потока - PullRequest
9 голосов
/ 16 ноября 2010

Я понимаю, что я делаю, возможно, довольно глупо, но я нахожусь в процессе изучения WPF и хотел бы знать, как это сделать.

У меня есть окно со списком на нем,Список используется для доставки сообщений о состоянии программы во время ее работы.Например, «Сервер запущен», «Новое соединение с IP #» и т. Д. Я хотел, чтобы это постоянно обновлялось в фоновом режиме, поэтому я создал новый поток для обработки обновления этого, но когда я сделал вызов, чтобы добавить элемент, я получилсообщение об ошибке «Вызывающий поток не может получить доступ к этому объекту, поскольку он принадлежит другому потоку.»

Есть идеи, как обновить список из другого потока?Или в фоновом режиме и т. Д.

Ответы [ 3 ]

25 голосов
/ 16 ноября 2010

ОБНОВЛЕНИЕ

Если вы используете C # 5 и .NET 4.5 или выше, вы можете избежать перехода на другой поток с помощью async и await, например:

private async Task<string> SimLongRunningProcessAsync()
{
    await Task.Delay(2000);
    return "Success";
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    button.Content = "Running...";
    var result = await SimLongRunningProcessAsync();
    button.Content = result;
}

Легко:

Dispatcher.BeginInvoke(new Action(delegate() 
  {
     myListBox.Items.Add("new item"));
  }));

Если вы находитесь в коде позади.В противном случае вы можете получить доступ к Диспетчеру (который есть на каждом UIElement), используя:

Application.Current.MainWindow.Dispatcher.BeginInvoke(...

Хорошо, это много в одной строке, позвольте мне перейти по нему:

Когда вы хотите обновитьпользовательский интерфейс контролирует, как говорится в сообщении, это нужно сделать из потока пользовательского интерфейса.Существует способ передачи делегата (метода) в поток пользовательского интерфейса: Dispatcher.Получив Dispatcher, вы можете либо Invoke() из BeginInvoke() передать делегат для запуска в потоке пользовательского интерфейса.Единственная разница в том, что Invoke() вернется только после запуска делегата (т.е. в вашем случае был добавлен новый элемент ListBox), тогда как BeginInvoke() вернется немедленно, так что ваш другой поток, из которого вы вызываете, может продолжить (Диспетчеркак можно быстрее запустите ваш делегат, который, вероятно, будет в любом случае незамедлительным.)

Я передал анонимный делегат выше:

delegate() {myListBox.Items.Add("new item");}

Бит между {} является блоком метода.Это называется анонимным, поскольку создается только одно и у него нет имени (обычно это можно сделать с помощью лямбда-выражения, но в этом случае C # не может разрешить вызов метода BeginInvoke ()).Или я мог бы создать экземпляр делегата:

Action myDelegate = new Action(UpdateListMethod);

void UpdateListMethod() 
{
  myListBox.Items.Add("new item");
}

Затем передать это:

Dispatcher.Invoke(myDelegate);

Я также использовал класс Action, который является встроенным делегатом, но вы могли бы создать свой собственный -Вы можете прочитать больше о делегатах на MSDN, поскольку это немного не по теме ..

5 голосов
/ 16 ноября 2010

Вы также можете использовать делегат Action с анонимными методами для обновления основного потока из рабочего потока. Для получения дополнительной информации о классе Action вы можете посмотреть здесь:

http://msdn.microsoft.com/en-us/library/018hxwa8.aspx

Если вы хотите обновить список из нескольких точек, я предлагаю явно установить делегат, однако, если вы просто хотите обновить поток в одной точке метода одним вызовом, это можно сделать следующим образом: *

        listbox.Dispatcher.BeginInvoke(new Action(delegate()
        {
            listbox.Items.Add(item); //where item is the item to be added and listbox is the control being updated.
        }));

Обратите внимание, что я использую класс Action, поскольку он инкапсулирует метод с единственным параметром (в данном случае это listItem).

2 голосов
/ 16 ноября 2010

Вы захотите использовать Dispatcher.BeginInvoke . Например, если список имеет имя listbox

  // On worker thread
  listbox.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
  new Action(delegate() { listbox.Items.Add("Server started") });

Это поставит в очередь делегата для выполнения в потоке пользовательского интерфейса, которому принадлежит listbox.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...