.net 如何将UI调度程序传递给ViewModel

.net 如何将UI调度程序传递给ViewModel,.net,wpf,mvvm,dispatcher,.net,Wpf,Mvvm,Dispatcher,我应该能够访问属于视图的,我需要将其传递给ViewModel。但是视图不应该知道关于ViewModel的任何信息,那么如何传递它呢?引入一个接口,或者创建一个由视图编写的全局调度程序单例,而不是将其传递给实例?如何在MVVM应用程序和框架中解决这个问题 编辑:请注意,由于我的ViewModel可能是在后台线程中创建的,因此我不能在ViewModel的构造函数中只执行Dispatcher.Current。我让ViewModel将当前Dispatcher存储为成员 如果视图模型是由视图创建的,则您知

我应该能够访问属于视图的,我需要将其传递给ViewModel。但是视图不应该知道关于ViewModel的任何信息,那么如何传递它呢?引入一个接口,或者创建一个由视图编写的全局调度程序单例,而不是将其传递给实例?如何在MVVM应用程序和框架中解决这个问题


编辑:请注意,由于我的ViewModel可能是在后台线程中创建的,因此我不能在ViewModel的构造函数中只执行
Dispatcher.Current

我让ViewModel将当前Dispatcher存储为成员

如果视图模型是由视图创建的,则您知道创建时的当前调度程序将是视图的调度程序

class MyViewModel
{
    readonly Dispatcher _dispatcher;
    public MyViewModel()
    {
        _dispatcher = Dispatcher.CurrentDispatcher;
    }
}

我已经使用接口IContext抽象了调度器:

公共接口IContext
{
bool已同步{get;}
无效调用(行动);
无效启动(行动);
}
这样做的好处是,您可以更轻松地对ViewModels进行单元测试
我使用MEF(托管可扩展性框架)将接口注入到ViewModels中。另一种可能是构造函数参数。 然而,我更喜欢使用MEF的注射

更新(来自评论中pastebin链接的示例):

公共密封类WpfContext:IContext
{
专用只读调度程序\u调度程序;
公共布尔值是同步的
{
得到
{
返回此。\u dispatcher.Thread==Thread.CurrentThread;
}
}
公共WpfContext():此(Dispatcher.CurrentDispatcher)
{
}
公共WpfContext(调度程序)
{
Assert(dispatcher!=null);
这是.\u dispatcher=dispatcher;
}
公共无效调用(操作)
{
Assert(action!=null);
此._dispatcher.Invoke(操作);
}
公共无效开始激活(操作)
{
Assert(action!=null);
此._调度程序开始激活(操作);
}
}

您可能实际上不需要调度器。如果将viewmodel上的属性绑定到视图中的GUI元素,则WPF绑定机制会使用dispatcher自动将GUI更新封送到GUI线程

class MyViewModel
{
    readonly Dispatcher _dispatcher;
    public MyViewModel()
    {
        _dispatcher = Dispatcher.CurrentDispatcher;
    }
}

编辑:

此编辑是对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});
} 

此代码将任何UI更新封送到线程UI线程,以便即使您从不同线程更新参与绑定的属性,WPF也会自动序列化对UI线程的调用。

我的一些WPF项目也遇到了相同的情况。在我的MainViewModel(Singleton实例)中,我得到了我的CreateInstance()静态方法,该方法接受调度程序。从视图调用create实例,这样我就可以从那里传递调度器。 ViewModel测试模块调用CreateInstance()无参数


但是在复杂的多线程场景中,最好在视图端有一个接口实现,以便获得当前窗口的适当调度程序。

如果您只需要调度程序来修改另一个线程中的绑定集合,请在此处查看SynchronizationContextCollection

工作正常,我发现的唯一问题是在ASP.NET同步上下文中使用具有SynchronizationContextCollection属性的视图模型时,但很容易解决

嗯 Sam

如果您被使用,您可以轻松地进行异步行为。看一看


而且我认为需要一些修改才能使它在温莎城堡(没有uNhAddIns)上工作。

嗨,也许我太迟了,因为你的第一篇帖子发布已经8个月了。。。 我在silverlight mvvm应用程序中也遇到了同样的问题。我找到了这样的解决方案。对于我拥有的每个模型和viewmodel,我还有一个名为controller的类。 那样

public class MainView : UserControl  // (because it is a silverlight user controll)
public class MainViewModel
public class MainController
我的主控制器负责指挥模型和viewmodel之间的连接。在构造函数中,我实例化视图及其viewmodel,并将视图的datacontext设置为其viewmodel

mMainView = new MainView();
mMainViewModel = new MainViewModel();
mMainView.DataContext = mMainViewModel; 
//(在我的命名约定中,成员变量的前缀为m)

我的MainView类型中还有一个公共属性。那样

public MainView View { get { return mMainView; } }
(此mMainView是公共属性的局部变量)

现在我完成了。我只需要使用我的调度程序来处理我的ui任务,就像这样

mMainView.Dispatcher.BeginInvoke(
    () => MessageBox.Show(mSpWeb.CurrentUser.LoginName));
(在本例中,我要求我的控制器获取我的sharepoint 2010登录名,但您可以根据需要执行操作)

我们差不多完成了,您还需要像这样在app.xaml中定义您的根可视化

var mainController = new MainController();
RootVisual = mainController.View;
这对我的申请很有帮助。也许它也可以帮助您……

另一种常见模式(现在在框架中有很多用途)是

它使您能够同步和异步调度。您还可以在当前线程上设置当前SynchronizationContext,这意味着它很容易被模拟。WPF应用程序使用DispatcherSynchronizationContext。WCF和WF4使用了SynchronizationContext的其他实现。

我找到了另一种(最简单的)方法:

添加到应在Dispatcher中调用的视图模型操作:

public class MyViewModel
{
    public Action<Action> CallWithDispatcher;

    public void SomeMultithreadMethod()
    {
        if(CallWithDispatcher != null)
            CallWithDispatcher(() => DoSomethingMetod(SomeParameters));
    }
}
现在您在测试方面没有问题了,而且它很容易实现。
我已将其添加到我的

中,您无需将UI调度程序传递给ViewModel。 UI调度程序可从当前应用程序单例获得

App.Current.MainWindow.Dispatcher
这将使ViewModel依赖于视图。取决于您的应用程序,这可能是好的,也可能不是好的。

为什么不使用

 System.Windows.Application.Current.Dispatcher.Invoke(
         (Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); } ));

不要保留对GUI dispatcher的引用。

对于WPF和Windows应用商店应用程序,请使用:-

class MyViewModel
{
    readonly Dispatcher _dispatcher;
    public MyViewModel()
    {
        _dispatcher = Dispatcher.CurrentDispatcher;
    }
}
       System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); } ));
保留对GUI dispatcher的引用并不是真正正确的方法

如果这不起作用(例如windows phone 8应用程序),请使用:-

       Deployment.Current.Dispatcher
从MVVM Light 5.2开始
Dispatcher.CurrentDispatcher.Invoke(() =>
{
    // Do GUI related operations here

}, DispatcherPriority.Normal); 
Dispatcher dis = Application.Current.Dispatcher