How to pass the UI Dispatcher to the ViewModel

前端 未结 16 567
独厮守ぢ
独厮守ぢ 2020-11-28 02:16

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

相关标签:
16条回答
  • 2020-11-28 02:25

    If you're only needing the dispatcher for modifying a bound collection in another thread take a look at the SynchronizationContextCollection here http://kentb.blogspot.com/2008/01/cross-thread-collection-binding-in-wpf.html

    Works well, only issue I found is when using View Models with SynchronizationContextCollection properties with ASP.NET synch context, but easily worked around.

    HTH Sam

    0 讨论(0)
  • 2020-11-28 02:26

    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);
        }
    }
    
    0 讨论(0)
  • 2020-11-28 02:27

    Another common pattern (which is seeing much use now in the framework) is the 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.

    0 讨论(0)
  • 2020-11-28 02:29

    Maybe I am a bit late to this discussion, but I found 1 nice article https://msdn.microsoft.com/en-us/magazine/dn605875.aspx

    There is 1 paragraph

    Furthermore, all code outside the View layer (that is, the ViewModel and Model layers, services, and so on) should not depend on any type tied to a specific UI platform. Any direct use of Dispatcher (WPF/Xamarin/Windows Phone/Silverlight), CoreDispatcher (Windows Store), or ISynchronizeInvoke (Windows Forms) is a bad idea. (SynchronizationContext is marginally better, but barely.) For example, there’s a lot of code on the Internet that does some asynchronous work and then uses Dispatcher to update the UI; a more portable and less cumbersome solution is to use await for asynchronous work and update the UI without using Dispatcher.

    Assume if you can use async/await properly, this is not an issue.

    0 讨论(0)
  • 2020-11-28 02:32

    You don't need to pass the UI Dispatcher to the ViewModel. The UI Dispatcher is available from the current application singleton.

    App.Current.MainWindow.Dispatcher
    

    This will make your ViewModel dependent on the View. Depending on your application, that may or may not be fine.

    0 讨论(0)
  • 2020-11-28 02:32

    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.

    0 讨论(0)
提交回复
热议问题