How to pass the UI Dispatcher to the ViewModel

2020-01-25 04:05发布

I'm supposed to be able to access the Dispatcher that 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?

EDIT: Note that since my ViewModels might be created in background threads I can't just do Dispatcher.Current in the constructor of the ViewModel.

15条回答
爷的心禁止访问
2楼-- · 2020-01-25 04:27

I've find another (most simplest) way:

Add to view model action that's should be call in Dispatcher:

public class MyViewModel
{
    public Action<Action> CallWithDispatcher;

    public void SomeMultithreadMethod()
    {
        if(CallWithDispatcher != null)
            CallWithDispatcher(() => DoSomethingMetod(SomeParameters));
    }
}

And add this action handler in view constructor:

    public View()
    {
        var model = new MyViewModel();

        DataContext = model;
        InitializeComponent();

        // Here 
        model.CallWithDispatcher += act => _taskbarIcon.Dispatcher
            .BeginInvoke(DispatcherPriority.Normal, act) ;
    }

Now you haven't problem with testing, and it's easy to implement. I've add it to my site

查看更多
男人必须洒脱
3楼-- · 2020-01-25 04:27

Some of my WPF projects I have faced the same situation. In my MainViewModel (Singleton instance), I got my CreateInstance() static method takes the dispatcher. And the create instance gets called from the View so that I can pass the Dispatcher from there. And the ViewModel test module calls CreateInstance() parameterless.

But in a complex multithread scenario it is always good to have an interface implementation on the View side so as to get the proper Dispatcher of the current Window.

查看更多
一纸荒年 Trace。
4楼-- · 2020-01-25 04:29

As of MVVM Light 5.2, the library now includes a DispatcherHelper class in GalaSoft.MvvmLight.Threading namespace 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.

DispatcherHelper must 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.CheckBeginInvokeOnUI(
        () =>
        {
           //Your code here
        });

Note that the class is defined in GalaSoft.MvvmLight.Platform library which is not referenced by default when you add it through NuGet. You must manually add a reference to this lib.

查看更多
狗以群分
5楼-- · 2020-01-25 04:33

why would not you use

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

instead of keeping reference to GUI dispatcher.

查看更多
地球回转人心会变
6楼-- · 2020-01-25 04:34

for WPF and Windows store apps use:-

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

keeping reference to GUI dispatcher is not really the right way.

if that doesn't work (such as in case of windows phone 8 apps) then use:-

       Deployment.Current.Dispatcher
查看更多
相关推荐>>
7楼-- · 2020-01-25 04:36

I have abstracted the Dispatcher using an interface IContext:

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.

Update (example from pastebin link in comments):

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);
    }
}
查看更多
登录 后发表回答