所以说在MVVM环境中,我在后台线程中,我想在ui控件上运行更新。所以通常我会去myButton.Dispatcher.BeginInvoke(blabla),但我无法访问myButton(因为viewmodel无法访问视图的控件)。那么这样做的正常模式是什么?
(我想总是有约束力,但我想知道如何通过调度员这样做)
答案 0 :(得分:37)
我通常使用Application.Current.Dispatcher
:由于Application.Current
是静态的,因此您不需要对控件的引用
答案 1 :(得分:15)
来自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);
}
}
在使用之前,您必须从UI线程中调用Execute.InitializeWithDispatcher()
,然后就可以像Execute.OnUIThread(()=>SomeMethod())
答案 2 :(得分:5)
我倾向于让我的ViewModel继承自DependencyObject并确保它们是在UI线程上构建的,这使得它们能够完美地处理这种情况 - 它们具有与UI线程的调度程序相对应的Dispatcher
属性。然后,您不需要使用ViewModel的实现细节污染您的视图。
其他一些优点:
Application.Current.Dispatcher
)答案 3 :(得分:4)
Catel的ViewModelBase具有您可以使用的Dispatcher属性。
答案 4 :(得分:0)
您可以在视图模型上引发一个事件(可能使用命名约定来指示它将从非UI线程引发 - 例如NotifyProgressChangedAsync)。然后您的View附加到事件可以适当地处理调度员。
或者,您可以将委托传递给View Model的同步功能(从您的View中)。
答案 5 :(得分:0)
将UI线程的调度程序传递给ViewModel的构造函数,并将其存储在VM中。
请注意,每个线程可能都有自己的调度程序。你将需要UI线程!
答案 6 :(得分:0)
大多数时候,您不需要在ViewModel中使用Dispatcher(> 99%的时间)。早期版本的.NET没有将PropertyChanged事件适当地封送给UI线程,这会导致问题。有一种解决方法,它要求您以知道Dispatcher的方式引发该事件,并可以在需要时自动进行封送。 .NET 3.5及更高版本现在可以自动执行此操作。
由于Dispatcher是一个UI概念,因此它在ViewModel中的存在是一种巨大的代码味道。这表明您做错了什么。更有可能的是,您需要注入一些东西来从正在处理的UI资源中提取ViewModel(改变鼠标光标是一个很好的例子),或者实际上是将View耦合到ViewModel。在后一种情况下,通常可以通过某种附加的行为来解决此问题,该行为将在ViewModel上订阅事件和属性更改。
与此相关的是一个挂断... CollectionChanged确实具有线程亲和力(间接通过WPF自动创建的CollectionViews),解决此问题的最佳方法是在引发该事件时检查事件委托子控件上的SynchronizationContexts。它很臭,但是仍然不需要您将Dispatcher传递给VM。