Я, как предполагается, могу получить доступ к Диспетчеру, который принадлежит Представлению, я должен передать его ViewModel. Но Представление ничего не должно знать о ViewModel, поэтому как Вы передаете его? Представьте интерфейс, или вместо того, чтобы передать его экземплярам создают глобальный одиночный элемент диспетчера, который будет записан Представлением? Как Вы решаете это в своих приложениях MVVM и платформах?
Править: Обратите внимание, что, так как мой ViewModels мог бы быть создан в фоновых потоках, которые я не могу просто сделать Dispatcher.Current
в конструкторе ViewModel.
Я абстрагировал Диспетчер с помощью интерфейса IContext :
public interface IContext
{
bool IsSynchronized { get; }
void Invoke(Action action);
void BeginInvoke(Action action);
}
Это дает то преимущество, что вы можете более легко тестировать свои модели просмотра.
Я добавляю интерфейс в свои модели просмотра, используя MEF (структуру управляемой расширяемости). Другой возможный вариант - аргумент конструктора.
Однако мне больше нравится инъекция с использованием MEF.
Обновление (пример из ссылки pastebin в комментариях):
public sealed class WpfContext : IContext
{
private readonly Dispatcher _dispatcher;
public bool IsSynchronized
{
get
{
return this._dispatcher.Thread == Thread.CurrentThread;
}
}
public WpfContext() : this(Dispatcher.CurrentDispatcher)
{
}
public WpfContext(Dispatcher dispatcher)
{
Debug.Assert(dispatcher != null);
this._dispatcher = dispatcher;
}
public void Invoke(Action action)
{
Debug.Assert(action != null);
this._dispatcher.Invoke(action);
}
public void BeginInvoke(Action action)
{
Debug.Assert(action != null);
this._dispatcher.BeginInvoke(action);
}
}
В некоторых моих проектах WPF я столкнулся с такой же ситуацией. В моей модели MainViewModel (экземпляр Singleton) я получил статический метод CreateInstance (), который принимает диспетчер. И экземпляр create вызывается из View, чтобы я мог передать Dispatcher оттуда. А тестовый модуль ViewModel вызывает CreateInstance () без параметров.
Но в сложном многопоточном сценарии всегда хорошо иметь реализацию интерфейса на стороне просмотра, чтобы получить правильный диспетчер текущего окна.
Я получаю ViewModel для сохранения текущего диспетчера в качестве члена.
Если ViewModel создается представлением, вы знаете, что текущий диспетчер во время создания будет диспетчером представления.
class MyViewModel
{
readonly Dispatcher _dispatcher;
public MyViewModel()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
}
Диспетчер может и не понадобиться. Если вы привязываете свойства вашей модели просмотра к элементам графического интерфейса в вашем представлении, механизм привязки WPF автоматически маршалирует обновления графического интерфейса в поток графического интерфейса с помощью диспетчера.
РЕДАКТИРОВАТЬ:
Это редактирование является ответом на комментарий Исака Саво.
Внутри кода Microsoft для обработки привязки к свойствам вы найдете следующий код:
if (Dispatcher.Thread == Thread.CurrentThread)
{
PW.OnPropertyChangedAtLevel(level);
}
else
{
// otherwise invoke an operation to do the work on the right context
SetTransferIsPending(true);
Dispatcher.BeginInvoke(
DispatcherPriority.DataBind,
new DispatcherOperationCallback(ScheduleTransferOperation),
new object[]{o, propName});
}
Этот код маршалирует любые обновления пользовательского интерфейса в поток пользовательского интерфейса потока, так что даже если вы обновляете свойства, принимающие часть привязки из другого потока, WPF автоматически сериализует вызов потока пользовательского интерфейса.