我应该能够访问属于视图的Dispatcher我需要将它传递给ViewModel。但视图不应该了解任何有关ViewModel的内容,那么你如何通过它?引入一个接口,或者不是将它传递给实例,而是创建一个将由View编写的全局调度器单例?你如何在你的MVVM应用程序和框架中解决这个问题?如何将UI调度程序传递给ViewModel
编辑:请注意,因为我的ViewModels可能创建在后台线程中,我不能只是在ViewModel的构造函数中做Dispatcher.Current
。
我应该能够访问属于视图的Dispatcher我需要将它传递给ViewModel。但视图不应该了解任何有关ViewModel的内容,那么你如何通过它?引入一个接口,或者不是将它传递给实例,而是创建一个将由View编写的全局调度器单例?你如何在你的MVVM应用程序和框架中解决这个问题?如何将UI调度程序传递给ViewModel
编辑:请注意,因为我的ViewModels可能创建在后台线程中,我不能只是在ViewModel的构造函数中做Dispatcher.Current
。
我一直在使用的接口IContext抽象的调度。
我使用MEF(托管扩展框架)将接口注入到ViewModels中。另一种可能性是构造参数。 但是,我更喜欢使用MEF进行注射。
更新(例如从引擎收录链接在评论):
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);
}
}
我得到的ViewModel存储当前的调度成员。
如果ViewModel是由视图创建的,那么您知道创建时的当前调度器将是View的调度器。
public interface IContext
{
bool IsSynchronized { get; }
void Invoke(Action action);
void BeginInvoke(Action action);
}
这样做的好处是可以更轻松地单元测试你的ViewModels:
class MyViewModel
{
readonly Dispatcher _dispatcher;
public MyViewModel()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
}
在我的场景中,ViewModels是在线程中创建的。这就是我首先要问的原因。 – bitbonk 2010-03-01 07:57:29
但单元测试存在问题。你如何编写这种代码和单元测试你的虚拟机? – 2010-03-01 16:52:55
@Roy:看到这个问题http://stackoverflow.com/questions/1106881/using-the-wpf-dispatcher-in-unit-tests/1303762#1303762 – 2010-03-01 21:54:53
您可能实际上并不需要的调度。如果将viewmodel上的属性绑定到视图中的GUI元素,那么WPF绑定机制会自动使用调度程序将GUI更新整理到GUI线程。
编辑:
此编辑为响应伊萨克萨沃的评论。
微软内部的处理结合属性,你会发现下面的代码代码:
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线程。
但是有很多情况,您可能需要这样做,想象一下绑定到UI的ObservableCollection,并且您试图调用_collection .Add()来自工作线程 – 2010-03-01 08:58:13
我知道。当然,通常的考虑因素仍然适用。 – 2010-03-01 09:01:46
目前,我们只需将调度程序添加到ObservableCollection中即可。 – bitbonk 2010-03-01 09:25:02
我的一些WPF项目面临同样的情况。在我的MainViewModel(Singleton实例)中,我得到了我的CreateInstance()静态方法需要调度器。然后创建实例从视图中调用,以便我可以从那里传递分派器。 而ViewModel测试模块调用CreateInstance()无参数。
但是在复杂的多线程场景中,在View端有一个接口实现总是好的,以便获得当前窗口的正确Dispatcher。
如果你只需要在另一个线程修改绑定集合调度看看这里的SynchronizationContextCollection http://kentb.blogspot.com/2008/01/cross-thread-collection-binding-in-wpf.html
效果很好,使用与ASP SynchronizationContextCollection性能视图模型只时,我发现问题。 NET同步上下文,但很容易解决。
HTH 山姆
哇,有趣,像这样的东西应该是WPF的一部分 – bitbonk 2010-10-04 09:25:15
喜也许我太晚了,因为它已经因为你的第一篇文章已有8个月〜 我在silverlight mvvm应用程序中遇到同样的问题。我发现我的解决方案是这样的。对于我有的每个模型和视图模型,我也有一个叫做控制器的类。 像
public class MainView : UserControl // (because it is a silverlight user controll)
public class MainViewModel
public class MainController
我MainController分管指挥和模型视图模型和之间的连接。在构造函数中,实例化视图及其视图模型,并将视图的datacontext设置为其视图模型。
mMainView = new MainView();
mMainViewModel = new MainViewModel();
mMainView.DataContext = mMainViewModel;
//(在我的命名约定我有成员变量的前缀米)
我也有我的MainView的类型的公共属性。这样
public MainView View { get { return mMainView; } }
(这mMainView是对公共财产的局部变量)
,现在我做。我只需要使用我的调度员对我的UI therad这样的...
mMainView.Dispatcher.BeginInvoke(
() => MessageBox.Show(mSpWeb.CurrentUser.LoginName));
(在这个例子中我是问我的控制器,让我的SharePoint 2010的登录名,但你可以做什么你的需要)
我们几乎做了你还需要定义你的根在App.xaml中视这样
var mainController = new MainController();
RootVisual = mainController.View;
帮我通过我的应用程序。也许它可以帮助你太...
另一种常见的模式(也就是现在看到的框架多大用处)为SynchronizationContext。
它使您可以同步和异步分派。您也可以在当前线程上设置当前的SynchronizationContext,这意味着它很容易被嘲弄。 DispatcherSynchronizationContext由WPF应用程序使用。 WCF和WF4使用SynchronizationContext的其他实现。
这是我认为最好也是最简单的方法。您可以将ViewModel的SynchronizationContext默认为创建ViewModel的线程当前上下文,并且如果UserControl需要更改它,则可以随意更改它。 – ken 2012-04-16 16:49:18
我已经找到另一个(最简单的)方式:
添加到浏览模式的行动,就是要在调度员可拨打:
public class MyViewModel
{
public Action<Action> CallWithDispatcher;
public void SomeMultithreadMethod()
{
if(CallWithDispatcher != null)
CallWithDispatcher(() => DoSomethingMetod(SomeParameters));
}
}
,并考虑构造函数中添加此动作处理程序:
public View()
{
var model = new MyViewModel();
DataContext = model;
InitializeComponent();
// Here
model.CallWithDispatcher += act => _taskbarIcon.Dispatcher
.BeginInvoke(DispatcherPriority.Normal, act) ;
}
现在你没有测试问题,而且很容易实现。 我已将它添加到我的site
您不需要将UI调度程序传递给ViewModel。 UI调度程序可从当前应用程序单例中获得。
App.Current.MainWindow.Dispatcher
这将使您的ViewModel依赖于视图。根据您的应用程序,可能会也可能不会。
MainWindow可能为null。 – Cologler 2015-06-02 01:42:54
你为什么不使用
System.Windows.Application.Current.Dispatcher.Invoke(
(Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); }));
,而不是保持参照GUI调度。
WPF和Windows应用商店的应用程序使用: -
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); }));
保存参考GUI调度员是不是真的正确的方式。
如果不工作(例如在窗口电话8个应用的情况下),然后使用: -
Deployment.Current.Dispatcher
作为MVVM灯5.2的,库现在包括一个DispatcherHelper
类GalaSoft.MvvmLight.Threading
命名空间暴露一个函数CheckBeginInvokeOnUI()
,它接受一个委托并在UI线程上运行它。如果您的ViewModel正在运行一些影响您的UI元素所绑定的VM属性的工作线程,则非常方便。
DispatcherHelper
必须在应用程序生命周期的早期阶段通过调用DispatcherHelper.Initialize()
进行初始化(例如App_Startup
)。然后,您可以使用下面的调用运行任何委托(或lambda):
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
//Your code here
});
注意类是当你通过的NuGet添加它默认不引用GalaSoft.MvvmLight.Platform
库中定义。您必须手动添加对此库的引用。
从WPF版本4开始。5可以使用CurrentDispatcher
Dispatcher.CurrentDispatcher.Invoke(() =>
{
// Do GUI related operations here
}, DispatcherPriority.Normal);
这个单例在多线程场景中不能很好地工作。 – bitbonk 2016-05-17 15:18:58
也许我有点晚了这次讨论,但我发现1好文章https://msdn.microsoft.com/en-us/magazine/dn605875.aspx
有1款
此外,查看以外的所有代码图层(即ViewModel 和模型图层,服务等)不应取决于绑定到特定UI平台的任何类型 。任何直接使用Dispatcher (WPF/Xamarin/Windows Phone/Silverlight),CoreDispatcher(Windows 存储)或ISynchronizeInvoke(Windows窗体)是一个坏主意。 (SynchronizationContext略微好一点,但几乎没有。)对于 示例,Internet上有很多代码用于执行一些异步工作,然后使用Dispatcher更新UI;更多的 便携式和不太麻烦的解决方案是使用等待异步 工作和更新UI而不使用分派器。
假设您可以正确使用异步/等待,这不是问题。
你能否提供一个wpf UserControl实现的例子? – Femaref 2010-06-10 09:37:38
是的,可以提供有关实施的任何示例?谢谢。 – K2so 2010-11-01 13:46:35
比从未更好地迟到;-)尝试以下方法:http://pastebin.com/eXTQf9vm – Matthias 2011-07-10 10:03:57