Показаны сообщения с ярлыком Dispathcer. Показать все сообщения
Показаны сообщения с ярлыком Dispathcer. Показать все сообщения

вторник, 11 августа 2015 г.

Показ окна в отдельном потоке

Время от времени в приложении появляются "долгие операции" во время которых интерфейс тормозит и пользователь не понимает что происходит с приложением. Обычно такие операции выносятся в фоновый поток, а в основном потоке приложения показываем прогресс выполнения работы или некую анимацию дающую понять, что приложение не повисло. Но что делать, если работа выполняется в основном потоке и вынести ее в фоновый нельзя (например, идет чтение из визуальных компонентов)? Вот об этом и поговорим под катом.

понедельник, 23 ноября 2009 г.

Создание окна из потока отличного от потока диспетчера приложения

Возникла задача, в процессе вызова метода из workflow показать форму для ввода дополнительных данных (для тех, кто не в курсе - workflow выполняются в отдельном потоке, и соответственно если метод вызывается из workflow, он тоже выполняется в этом потоке). Приложение написано на WPF. Привычная последовательность действий:
    public void StartReadFromScala(Guid idWorkflow)
    {
      WndReadFromScala form = new WndReadFromScala();
      form.Show();
    }


* This source code was highlighted with Source Code Highlighter.

Выполнялось, но форма не появлялась :(

Пришлось пойти на следующее ухищрение:
    public void StartReadFromScala(Guid idWorkflow)
    {
      // Создаем форму для ввода данных, но создаем ее в главном потоке
      Application.Current.Dispatcher.Invoke(new NoParamHandler(CreateWndReadFromScala), new object[] { });
    }

    private void CreateWndReadFromScala()
    {
      WndReadFromScala form = new WndReadFromScala();
      form.Show();
    }


* This source code was highlighted with Source Code Highlighter.

А вот теперь все работает ;)

среда, 12 августа 2009 г.

Выполнение кода после изменения текста в ComboBox

Стоит задача после изменения значения в ComboBox, вывести некоторое сообщение пользователю, например, в MessageBox.
В XAML все просто:
<ComboBox SelectionChanged="ComboBox_SelectionChanged" >
  <ItemsControl>1</ItemsControl>
  <ItemsControl>2</ItemsControl>
  <ItemsControl>3</ItemsControl>
  <ItemsControl>4</ItemsControl>
</ComboBox>

* This source code was highlighted with Source Code Highlighter.

Но если в обработчике события тупо вызывать MessageBox.Show возникает проблема: пользователь уже видит MessageBox, а изменение текста еще не произошло :( Что может ввести пользователя в заблуждение.
Решить проблему можно вот таким достаточно извращенным способом:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
  DispatcherTimer t = null;
  t = new DispatcherTimer(new TimeSpan(1), DispatcherPriority.Background, (s, e1) => { t.Stop(); MessageBox.Show("asdfvasdvc"); }, this.Dispatcher);
  t.Start();
}

* This source code was highlighted with Source Code Highlighter.


Но надо будет еще подумать... что то ведь должно быть проще.

вторник, 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