.net 如何将 UI Dispatcher 传递给 ViewModel
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2354438/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to pass the UI Dispatcher to the ViewModel
提问by bitbonk
I'm supposed to be able to access the Dispatcherthat belongs to the View I need to pass it to the ViewModel. But the View should not know anything about the ViewModel, so how do you pass it? Introduce an interface or instead of passing it to the instances create a global dispatcher singleton that will be written by the View? How do you solve this in your MVVM applications and frameworks?
我应该能够访问属于我需要将它传递给 ViewModel 的视图的调度程序。但是View应该对ViewModel一无所知,那怎么传递呢?引入一个接口,或者不是将它传递给实例,而是创建一个将由 View 编写的全局调度程序单例?你如何在你的 MVVM 应用程序和框架中解决这个问题?
EDIT: Note that since my ViewModels might be created in background threads I can't just do Dispatcher.Currentin the constructor of the ViewModel.
编辑:请注意,由于我的 ViewModels 可能是在后台线程中创建的,因此我不能只Dispatcher.Current在 ViewModel 的构造函数中进行。
采纳答案by Matthias
I have abstracted the Dispatcher using an interface IContext:
我使用接口IContext抽象了 Dispatcher :
public interface IContext
{
bool IsSynchronized { get; }
void Invoke(Action action);
void BeginInvoke(Action action);
}
This has the advantage that you can unit-test your ViewModels more easily.
I inject the interface into my ViewModels using the MEF (Managed Extensibility Framework). Another possibility would be a constructor argument.
However, I like the injection using MEF more.
这样做的好处是您可以更轻松地对 ViewModel 进行单元测试。
我使用 MEF(托管扩展框架)将接口注入我的 ViewModel。另一种可能性是构造函数参数。但是,我更喜欢使用 MEF 进行注射。
Update (example from pastebin link in comments):
更新(来自评论中的 pastebin 链接的示例):
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);
}
}
回答by Vitaliy Markitanov
why would not you use
你为什么不使用
System.Windows.Application.Current.Dispatcher.Invoke(
(Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); } ));
instead of keeping reference to GUI dispatcher.
而不是保留对 GUI 调度程序的引用。
回答by Jakob Christensen
You may not actually need the dispatcher. If you bind properties on your viewmodel to GUI elements in your view, the WPF binding mechanism automatically marshals the GUI updates to the GUI thread using the dispatcher.
您可能实际上并不需要调度员。如果将视图模型上的属性绑定到视图中的 GUI 元素,WPF 绑定机制会使用调度程序自动将 GUI 更新编组到 GUI 线程。
EDIT:
编辑:
This edit is in response to Isak Savo's comment.
此编辑是对 Isak Savo 评论的回应。
Inside Microsoft's code for handling binding to properties you will find the following code:
在 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});
}
This code marshals any UI updates to the thread UI thread so that even if you update the properties taking part of the binding from a different thread, WPF will automatically serialize the call to the UI thread.
此代码将任何 UI 更新封送至线程 UI 线程,以便即使您更新了来自不同线程的绑定部分的属性,WPF 也会自动序列化对 UI 线程的调用。
回答by Andrew Shepherd
I get the ViewModel to store the current dispatcher as a member.
我让 ViewModel 将当前调度程序存储为成员。
If the ViewModel is created by the view, you know that the current dispatcher at creation time will be the View's dispatcher.
如果 ViewModel 是由视图创建的,则您知道创建时的当前调度程序将是视图的调度程序。
class MyViewModel
{
readonly Dispatcher _dispatcher;
public MyViewModel()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
}
回答by dotNET
As of MVVM Light 5.2, the library now includes a DispatcherHelperclass in GalaSoft.MvvmLight.Threadingnamespace that exposes a function CheckBeginInvokeOnUI()that accepts a delegate and runs it on the UI thread. Comes in very handy if your ViewModel is running some worker threads which affect VM properties to which your UI elements are bound.
从 MVVM Light 5.2 开始,该库现在DispatcherHelper在GalaSoft.MvvmLight.Threading命名空间中包含一个类,该类公开一个CheckBeginInvokeOnUI()接受委托并在 UI 线程上运行它的函数。如果您的 ViewModel 正在运行一些影响您的 UI 元素绑定到的 VM 属性的工作线程,则非常方便。
DispatcherHelpermust be initialized by calling DispatcherHelper.Initialize()at an early stage in the life of your application (e.g. App_Startup). You can then run any delegate (or lambda) using the following call:
DispatcherHelper必须通过DispatcherHelper.Initialize()在应用程序生命周期的早期阶段调用来初始化(例如App_Startup)。然后,您可以使用以下调用运行任何委托(或 lambda):
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
//Your code here
});
Note that the class is defined in GalaSoft.MvvmLight.Platformlibrary which is not referenced by default when you add it through NuGet. You must manually add a reference to this lib.
请注意,该类是在GalaSoft.MvvmLight.Platform库中定义的,当您通过 NuGet 添加它时,默认情况下不会引用该类。您必须手动添加对此库的引用。
回答by dotNET
Another common pattern (which is seeing much use now in the framework) is the SynchronizationContext.
另一种常见模式(现在在框架中非常有用)是SynchronizationContext。
It enables you to dispatch synchronously and asynchronously. You can also set the current SynchronizationContext on the current thread, meaning it is easily mocked. The DispatcherSynchronizationContext is used by WPF apps. Other implementations of the SynchronizationContext are used by WCF and WF4.
它使您能够同步和异步调度。您还可以在当前线程上设置当前 SynchronizationContext,这意味着它很容易被模拟。WPF 应用程序使用 DispatcherSynchronizationContext。WCF 和 WF4 使用 SynchronizationContext 的其他实现。
回答by ΩmegaMan
As of WPF version 4.5 one can use CurrentDispatcher
从 WPF 4.5 版开始,可以使用CurrentDispatcher
Dispatcher.CurrentDispatcher.Invoke(() =>
{
// Do GUI related operations here
}, DispatcherPriority.Normal);
回答by Parth Sehgal
for WPF and Windows store apps use:-
对于 WPF 和 Windows 商店应用程序,请使用:-
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); } ));
keeping reference to GUI dispatcher is not really the right way.
保持对 GUI 调度程序的引用并不是真正正确的方法。
if that doesn't work (such as in case of windows phone 8 apps) then use:-
如果这不起作用(例如在 Windows Phone 8 应用程序的情况下),则使用:-
Deployment.Current.Dispatcher
回答by arborinfelix
hi maybe i am too late since it has been 8 months since your first post... i had the same proble in a silverlight mvvm applicatioin. and i found my solution like this. for each model and viewmodel that i have, i have also a class called controller. like that
嗨,也许我太晚了,因为距您的第一篇文章已经 8 个月了……我在 Silverlight mvvm 应用程序中遇到了同样的问题。我找到了这样的解决方案。对于我拥有的每个模型和视图模型,我还有一个称为控制器的类。像那样
public class MainView : UserControl // (because it is a silverlight user controll)
public class MainViewModel
public class MainController
my MainController is in charge of the commanding and the connection between the model and viewmodel. in the constructor i instanciate the view and its viewmodel and set the datacontext of the view to its viewmodel.
我的 MainController 负责模型和视图模型之间的命令和连接。在构造函数中,我实例化视图及其视图模型,并将视图的数据上下文设置为其视图模型。
mMainView = new MainView();
mMainViewModel = new MainViewModel();
mMainView.DataContext = mMainViewModel;
//(in my naming convention i have a prefix m for member variables)
//(在我的命名约定中,我的成员变量有一个前缀 m)
i also have a public property in the type of my MainView. like that
我的 MainView 类型也有一个公共属性。像那样
public MainView View { get { return mMainView; } }
(this mMainView is a local variable for the public property)
(这个 mMainView 是公共属性的局部变量)
and now i am done. i just need to use my dispatcher for my ui therad like this...
现在我完成了。我只需要像这样将我的调度员用于我的 ui therad...
mMainView.Dispatcher.BeginInvoke(
() => MessageBox.Show(mSpWeb.CurrentUser.LoginName));
(in this example i was asking my controller to get my sharepoint 2010 loginname but you can do what your need)
(在这个例子中,我要求我的控制器获取我的 sharepoint 2010 登录名,但你可以做你需要的)
we are almost done you also need to define your root visual in the app.xaml like this
我们快完成了,您还需要像这样在 app.xaml 中定义根视觉对象
var mainController = new MainController();
RootVisual = mainController.View;
this helped me by my application. maybe it can help you too...
这对我的申请有帮助。也许它也可以帮助你...
回答by Rana Ian
You don't need to pass the UI Dispatcher to the ViewModel. The UI Dispatcher is available from the current application singleton.
您不需要将 UI Dispatcher 传递给 ViewModel。UI Dispatcher 可从当前应用程序单例中获得。
App.Current.MainWindow.Dispatcher
This will make your ViewModel dependent on the View. Depending on your application, that may or may not be fine.
这将使您的 ViewModel 依赖于该视图。根据您的应用程序,这可能好也可能不好。

