假设在MVVM环境下,我在后台线程中,想要在UI控件上运行更新操作。通常情况下,我会使用myButton.Dispatcher.BeginInvoke(blabla)方法,但是我无法访问myButton(因为视图模型无法访问视图的控件)。那么,如何正常地完成这一任务呢?
(我猜总有绑定的方式,但我想知道如何通过调度程序完成)
假设在MVVM环境下,我在后台线程中,想要在UI控件上运行更新操作。通常情况下,我会使用myButton.Dispatcher.BeginInvoke(blabla)方法,但是我无法访问myButton(因为视图模型无法访问视图的控件)。那么,如何正常地完成这一任务呢?
(我猜总有绑定的方式,但我想知道如何通过调度程序完成)
我通常使用Application.Current.Dispatcher
:由于Application.Current
是静态的,所以不需要引用控件。
来自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())
Catel 的 ViewModelBase 类有一个 Dispatcher 属性,您可以使用它。
大多数情况下,ViewModel 中不需要使用 Dispatcher(>99% 的情况)。早期版本的 .NET 无法适当地将 PropertyChanged 事件转发到 UI 线程,这会导致问题。有一种方法可以解决这个问题,需要以一种能够意识到 Dispatcher 并在需要时自动转发的方式引发该事件。.NET 3.5 及以上版本现在会自动执行此操作。
因为 Dispatcher 是一个 UI 概念,在 ViewModel 中出现它是一个很大的代码异味。这表明您正在做错事情。更可能的情况是,您需要注入某些东西,以将您的 ViewModel 与您正在操作的 UI 资源(更改鼠标光标是一个很好的例子)抽象出来,或者您实际上已经将 View 和 ViewModel 耦合在一起。在后一种情况下,通常可以通过某种附加行为来修复此问题,该附加行为将订阅 ViewModel 上的事件和属性更改。
这里有一个问题... CollectionChanged 具有线程亲和性(间接通过 WPF 自动创建的 CollectionViews),最好的解决方法是在引发该事件时检查事件委托订阅者上的 SynchronizationContexts。虽然这很糟糕,但仍不需要将 Dispatcher 传递给 VM。