WPF /多线程:MVVM中的UI调度程序

时间:2011-01-07 01:04:26

标签: c# wpf multithreading mvvm

所以说在MVVM环境中,我在后台线程中,我想在ui控件上运行更新。所以通常我会去myButton.Dispatcher.BeginInvoke(blabla),但我无法访问myButton(因为viewmodel无法访问视图的控件)。那么这样做的正常模式是什么?

(我想总是有约束力,但我想知道如何通过调度员这样做)

7 个答案:

答案 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
  • View&amp; amp;视图模型
  • 您可以在ViewModel上定义依赖项属性,并且不会编写任何代码来更新视图,因为这些属性会发生变化。

答案 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。