вторник, 19 мая 2009 г.

Многопоточный доступ в WPF

Одной из проблем при работе в WPF с визуальными компонентами заключается в том, что доступ к ним из любого потока отличного от их породившего приводит к исключению.

Для примера возьмем простое приложение, со следующим интерфейсом:

При нажатии на кнопку "Старт" запускается длительная операция выводящая состояние процесса в ProgressBar.


Код метода длительной операции для простоты возьмем вот такой:

private int LongOperation()
{
for (int i = 0; i < 100; i++)
{
// Имитация полезной работы
Thread.Sleep(100);
// Сообщаем в визуальную часть, что часть работы выполнена
SetProgressBarValue(i + 1);
}
return 0;
}


* This source code was highlighted with Source Code Highlighter.


Обработчик клика на кнопке:


private void startButton_Click(object sender, RoutedEventArgs e)
{
Func<int> operation = LongOperation;
operation.BeginInvoke(null, null);
}


* This source code was highlighted with Source Code Highlighter.

При попытке реализовать метод SetProgressBarValue в лоб:


private void SetProgressBarValue(int newValue)
{
workProgress.Value++;
}


* This source code was highlighted with Source Code Highlighter.

мы получим InvalidOperationException

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


// Делегат для перевызова метода SetProgressBarValue через диспетчер
private delegate void SetProgressBarValueHandler(int newValue);

private void SetProgressBarValue(int newValue)
{
// Проверяем совпадает ли поток диспетчера с потоком вызвавшим метод
if (Dispatcher.Thread == Thread.CurrentThread)
{
// Все замечательно :) меняем значение прогресбара
workProgress.Value = newValue;
}
else
{
// Нет :( все плохо :( перезапускаем метод в потоке диспетчера
Dispatcher.Invoke(new SetProgressBarValueHandler(SetProgressBarValue), new object[] { newValue });
}
}


* This source code was highlighted with Source Code Highlighter.

Вуаля, все заработало :)

Проект можно скачать здесь: MultithreadingWPF.rar

4 комментария:

  1. Этот комментарий был удален автором.

    ОтветитьУдалить
  2. В .net есть стандартный класс BackgroundWorker, который позволяет выполнить длительную операцию в отдельном, потоке. Он собственно и предназначен для решения подобных задач. И какой смысл изобретать велосипед?

    ОтветитьУдалить
  3. Этот комментарий был удален автором.

    ОтветитьУдалить
  4. Этот комментарий был удален автором.

    ОтветитьУдалить