2010-04-13 6 views
1

Следующий метод не применяет изменения МОФА (фон = красный) до завершения 2-го методы (DoWork) выходит:WPF звонков не работает в течение длительного обработки метода

private void change() 
{ 
    Background = Brushes.Red; 
    Dispatcher.BeginInvoke((Action) DoWork); 
} 

DoWork() занимает несколько секунд для запуска и Я действительно не хочу помещать его в поток, так как этот код будет использоваться в нескольких местах и, вероятно, будет взаимодействовать с потоком диспетчера через различные промежутки времени. Я попытался вызвать методы Invalidate ...(), но безрезультатно. Добавлен BeginInvoke(), чтобы увидеть, будет ли задержка позволять применить изменение фона до того, как будет вызвана логика. Как правило, логика будет частью этого метода. Btw, большая часть логики выполняется в другом потоке и не должна блокировать поток Dispatcher ?!

Может кто-нибудь помочь? Спасибо

+0

Я попытался использовать фоновый подход, но это вызывает проблему, когда изменения в пользовательском интерфейсе предпринимаются за пределами потока Диспетчер. Например, переменные, привязанные к пользовательским элементам управления, запускаются в методе DoWork(), которые генерируют исключения из-за того, что они не выполняются в потоке диспетчера. Вышеупомянутый подход также означает, что любое изменение пользовательского интерфейса от DoWork() должно быть завернуто в вызов Dispatcher (Begin) Invoke(), что означает раздутый код. Должен быть другой способ сделать это ?! Спасибо –

+0

У вас есть три варианта: 1) запустить долговременную задачу в потоке пользовательского интерфейса и позволить блоку пользовательского интерфейса и ждать, 2) запустить его в другом потоке и решить проблемы с перекрестными потоками при попытке обновить интерфейс пользователя во время 3) не обновлять пользовательский интерфейс во время выполнения задачи. Если вы решите, что вам нужно идти с № 2, проблемы, с которыми вы сталкиваетесь, присущи.Использование «BackgroundWorker» помогает несколько; он дает вам последовательный способ изолировать код обновления UI в событии, сообщающем о ходе выполнения. Но обновление значений свойств привязки данных в фоновом потоке? № –

+0

Простым способом решения этой проблемы является создание модели представления, привязка пользовательского интерфейса к ней и обновление модели представления из фонового потока. Таким образом, привязка данных WPF может касаться всех проблем с потоками. Непосредственное обновление пользовательского интерфейса из кода, не проходя через модель представления, всегда является плохой идеей. –

ответ

3

Нить «Диспетчер» - это поток пользовательского интерфейса.

Когда вы вызываете Dispatcher.BeginInvoke((Action) DoWork);, вы в основном блокируете поток пользовательского интерфейса до выхода DoWork, так как он работает в потоке пользовательского интерфейса.

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

Вы действительно должны рассмотреть возможность перемещения DoWork на фоновый поток, либо через ThreadPool напрямую, либо BackgroundWorker, либо какие-либо другие средства. Это полностью устранит эту проблему и не позволит вам блокировать поток пользовательского интерфейса в течение нескольких секунд (что произойдет, если вы запустите это с помощью потоковой передачи Dispatcher).

+0

Не Dispatcher.Invoke() блокирует код, а не BeginInvoke()? Использование потока подразумевает, что вы должны использовать потоки для всей обработки за ui, что звучит очень утомительно. Не работает ли механизм WPF для этого, будучи исправленным в фоновом режиме? Во всяком случае, звучит так, будто я должен сбросить логику в поток. Спасибо за вашу помощь ... –

+0

@Colin: Если вы находитесь в потоке пользовательского интерфейса, оба будут блокироваться. Использование диспетчера заставляет ваш код запускать ** в потоке пользовательского интерфейса **, это и есть цель. Если вы хотите, чтобы это выполнялось в фоновом потоке, вы должны использовать ThreadPool.QueueUserWorkItem, а не Dispatcher (Dispatcher для противоположной цели - если вы находитесь в фоновом потоке и хотите что-то запустить в потоке пользовательского интерфейса, вы используете Dispatcher) –

+0

спасибо, что работает –

0

Для связи между потоками у нас есть класс Dispatcher в WPF.

метод
Dispatcher.Invoke(DispatcherPriority.Normal, (NoArgDelegate)delegate 
      { 
       ------ 
      }); 
0

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

Вы можете поставить более низкий приоритет для вызова DoWork, чтобы быть уверенным, что фон изменяется первая:

 private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     grid.Background = new SolidColorBrush(Colors.Red); 
     Dispatcher.BeginInvoke((Action)DoWork, DispatcherPriority.ContextIdle); 
    } 

    private void DoWork() 
    { 
     Thread.Sleep(1000); 
     btn.Content = "Done"; 
    } 

Но это не самое лучшее решение, известное человечеству.

Лучше было бы продолжить DoWork в отдельной теме. выше примера, используя нить подход будет выглядеть следующим образом:

private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     grid.Background = new SolidColorBrush(Colors.Red); 

     Task t = new Task(DoWork); 
     t.Start(); 
    } 

    private void DoWork() 
    { 
     Thread.Sleep(1000); 
     // Synchronize calls to the UI elements 
     Application.Current.Dispatcher.BeginInvoke((Action)(() => { btn.Content = "Done"; })); 

    } 

Только не забудьте поместить все операции над элементами пользовательского интерфейса в потоке пользовательского интерфейса.