bit*_*onk 69 .net wpf dispatcher mvvm
我应该能够访问属于View 的Dispatcher,我需要将它传递给ViewModel.但是View应该对ViewModel一无所知,那么你如何传递它呢?引入一个接口或者不是将它传递给实例创建一个将由View编写的全局调度程序单例?您如何在MVVM应用程序和框架中解决这个问题?
编辑:请注意,因为我的ViewModel可能是在后台线程中创建的,所以我不能只Dispatcher.Current在ViewModel的构造函数中创建.
Mat*_*ias 45
我使用接口IContext抽象了Dispatcher :
public interface IContext
{
bool IsSynchronized { get; }
void Invoke(Action action);
void BeginInvoke(Action action);
}
Run Code Online (Sandbox Code Playgroud)
这样做的好处是可以更轻松地对ViewModel进行单元测试.
我使用MEF(Managed Extensibility Framework)将接口注入我的ViewModels.另一种可能性是构造函数参数.但是,我更喜欢使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
Vit*_*nov 38
你为什么不用
System.Windows.Application.Current.Dispatcher.Invoke(
(Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); } ));
Run Code Online (Sandbox Code Playgroud)
而不是保持对GUI调度程序的引用.
Jak*_*sen 18
您实际上可能不需要调度员.如果将viewmodel上的属性绑定到视图中的GUI元素,则WPF绑定机制会使用调度程序自动将GUI更新封送到GUI线程.
编辑:
此编辑是对Isak Savo评论的回应.
在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});
}
Run Code Online (Sandbox Code Playgroud)
此代码将对线程UI线程的任何UI更新进行编组,以便即使您从另一个线程更新参与绑定的属性,WPF也会自动将调用序列化到UI线程.
And*_*erd 15
我得到ViewModel将当前的调度程序存储为成员.
如果ViewModel是由视图创建的,则您知道创建时的当前调度程序将是View的调度程序.
class MyViewModel
{
readonly Dispatcher _dispatcher;
public MyViewModel()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
}
Run Code Online (Sandbox Code Playgroud)
从MVVM Light 5.2开始,该库现在DispatcherHelper在GalaSoft.MvvmLight.Threading命名空间中包含一个类,该类公开一个CheckBeginInvokeOnUI()接受委托并在UI线程上运行它的函数.如果ViewModel正在运行一些影响UI元素绑定的VM属性的工作线程,则非常方便.
DispatcherHelper必须通过DispatcherHelper.Initialize()在申请生命的早期阶段致电来初始化(例如App_Startup).然后,您可以使用以下调用运行任何委托(或lambda):
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
//Your code here
});
Run Code Online (Sandbox Code Playgroud)
请注意,该类在GalaSoft.MvvmLight.Platform库中定义,当您通过NuGet添加时,默认情况下不会引用该类.您必须手动添加对此lib的引用.
小智 5
另一种常见模式(现在在框架中非常有用)是SynchronizationContext。
它使您能够同步和异步调度。您还可以在当前线程上设置当前 SynchronizationContext,这意味着它很容易被模拟。WPF 应用程序使用 DispatcherSynchronizationContext。WCF 和 WF4 使用 SynchronizationContext 的其他实现。