2011-01-07 2 views
15

Так что скажите в среде MVVM, я в фоновом потоке, и я хотел бы запустить обновление на элементе управления ui. Так что обычно я бы пошел myButton.Dispatcher.BeginInvoke (blabla), но у меня нет доступа к myButton (потому что в viewmodel нет доступа к элементам управления view). Итак, какова нормальная картина для этого?WPF/многопоточность: Диспетчер интерфейса в MVVM

(я предполагаю, что есть всегда обязательный, но я хотел бы знать, как сделать это с помощью диспетчера)

+2

Дубликат http://stackoverflow.com/questions/486758/is-wpf-dispatcher-the-solution-of-multi-threading-problems? –

+0

Не дубликат ... он спрашивает, как получить диспетчера из фонового потока, запущенного ViewModel (который обычно не имеет доступа к диспетчеру). –

ответ

36

Я обычно использую Application.Current.Dispatcher: поскольку Application.Current является static, вам не нужна ссылка на элемент управления

+7

И как вы будете тестировать свои модели без объекта приложения? –

+2

@Geert van Horrik Вы можете высмеять объект приложения для тестирования ваших моделей. Извините за комментарий necro - я просто наткнулся на эту статью SO во время поиска действительного решения MVVM для многопоточного программирования. – SRM

0

Вы можете поднять событие на ваш взгляд модели (возможно, с помощью именовании, чтобы указать, что это будет быть поднятым из потока, отличного от UI, например NotifyProgressChangedAsync). Тогда ваш View, который прикреплен к событию, может обращаться с диспетчером соответствующим образом.

Или вы можете передать делегат функции синхронизации в вашу модель просмотра (из вашего вида).

13

С Caliburn Micro исходного кода:

public static class Execute 
{ 
    private static Action<System.Action> executor = action => action(); 

    /// <summary> 
    /// Initializes the framework using the current dispatcher. 
    /// </summary> 
    public static void InitializeWithDispatcher() 
    { 
#if SILVERLIGHT 
     var dispatcher = Deployment.Current.Dispatcher; 
#else 
     var dispatcher = Dispatcher.CurrentDispatcher; 
#endif 
     executor = action =>{ 
      if(dispatcher.CheckAccess()) 
       action(); 
      else dispatcher.BeginInvoke(action); 
     }; 
    } 

    /// <summary> 
    /// Executes the action on the UI thread. 
    /// </summary> 
    /// <param name="action">The action to execute.</param> 
    public static void OnUIThread(this System.Action action) 
    { 
     executor(action); 
    } 
} 

Перед использованием вы должны вызвать Execute.InitializeWithDispatcher() из потока пользовательского интерфейса, то вы можете использовать его как этот Execute.OnUIThread(()=>SomeMethod())

+0

Я все еще получаю ошибку, если я использую Dispatcher.CurrentDispatcher. Он работает правильно, если я использую Application.Current.Dispatcher. –

0

Передайте диспетчер потока пользовательского интерфейса в конструктор ViewModel и сохраните его в виртуальной машине.

Обратите внимание, что каждый поток может иметь свой собственный диспетчер. Вам понадобится поток пользовательского интерфейса!

4

ViewModelBase Catel имеет свойство Dispatcher, которое вы можете использовать.

+0

Метод GetCurrentDispatcher в классе DispatcherHelper.https://catel.codeplex.com/SourceControl/latest#src/Catel.MVVM/Catel.MVVM.NET40/Windows/Threading/Helpers/Dispatcherhelper.cs –

4

Я, как правило, имею, что мои объекты ViewModels наследуются от DependencyObject и гарантируют, что они построены на потоке пользовательского интерфейса, что идеально их справляется с этой ситуацией - они имеют свойство Dispatcher, которое соответствует диспетчеру потока пользовательского интерфейса. Затем вам не нужно загрязнять ваше представление деталями реализации ViewModel.

Некоторые другие плюсы:

  • Unit проверяемость: вы можете модульного тестирования эти без запущенного приложения (а не полагаться на Application.Current.Dispatcher)
  • слабосвязанности между видом & ViewModel
  • Вы можете определить свойства зависимостей на ViewModel и не писать код для обновления представления, поскольку эти свойства изменяются.
Смежные вопросы