Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass the UI Dispatcher to the ViewModel

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.

like image 969
bitbonk Avatar asked Mar 01 '10 07:03

bitbonk


3 Answers

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);
    }
}
like image 165
Matthias Avatar answered Oct 26 '22 18:10

Matthias


why would not you use

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

instead of keeping reference to GUI dispatcher.

like image 24
Vitaliy Markitanov Avatar answered Oct 26 '22 18:10

Vitaliy Markitanov


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.


EDIT:

This edit is in response to Isak Savo's comment.

Inside Microsoft's code for handling binding to properties you will find the following code:

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.

like image 19
Jakob Christensen Avatar answered Oct 26 '22 20:10

Jakob Christensen