1

I have worker class with

public event EventHandler<EventArgs> DataModified;

which can be raised from other than UI thread (it is network client which fetches updates from server). I have ModelView with

ObservableCollection<DataModel> DataItems;

to which View binds. My modelview must subscribe to ModifiedEvent, so it can reflect changes in DataItems. How do I update DataItems from callback event? I do not have access to UI Dispatcher from my model view (because view should not be aware of modelview). What is proper .NET 4.5 way for handling this?

0xDEAD BEEF
  • 2,074
  • 7
  • 31
  • 46

2 Answers2

6

You can create a service wich holds a CoreDispatcher and inject into your view model (or make it static)

public static class SmartDispatcher
{
    private static CoreDispatcher _instance;
    private static void RequireInstance()
    {
        try
        {
            _instance = Window.Current.CoreWindow.Dispatcher;

        }
        catch (Exception e)
        {
            throw new InvalidOperationException("The first time SmartDispatcher is used must be from a user interface thread. Consider having the application call Initialize, with or without an instance.", e);
        }

        if (_instance == null)
        {
            throw new InvalidOperationException("Unable to find a suitable Dispatcher instance.");
        }
    }

    public static void Initialize(CoreDispatcher dispatcher)  
    {
        if (dispatcher == null)
        {
            throw new ArgumentNullException("dispatcher");
        }

        _instance = dispatcher;
    }

    public static bool CheckAccess()
    {
        if (_instance == null)
        {
            RequireInstance();
        }
        return _instance.HasThreadAccess;
    }

    public static void BeginInvoke(Action a)
    {
        if (_instance == null)
        {
            RequireInstance();
        }

        // If the current thread is the user interface thread, skip the
        // dispatcher and directly invoke the Action.
        if (CheckAccess())
        {
            a();
        }
        else
        {
            _instance.RunAsync(CoreDispatcherPriority.Normal, () => { a(); });
        }
    }
}

You should initialize SmartDispatcher in App.xaml.cs:

 var rootFrame = new Frame();
 SmartDispatcher.Initialize(rootFrame.Dispatcher);

 Window.Current.Content = rootFrame;
 Window.Current.Activate();

use it in this way:

SmartDispatcher.BeginInvoke(() => _collection.Add(item));

This class is based on Jeff Wilcox's analog for windows phone 7.

Anton Sizikov
  • 9,105
  • 1
  • 28
  • 39
  • Well I am doing something similar to this right now, but having all great await and async keywords this just does not seem like proper way anymore. More over - I have ran in some performance problems due to magic delays. More over - since I do replicate Model data in ModelView observable collection, I see many race conditions possible in my code. This is awful... – 0xDEAD BEEF Sep 10 '12 at 14:08
2

In WinRT, you have to get access to the CoreDispatcher. CoreDispatcher resides in the Windows.UI namespace and is only exposed via the Window.Current. That means you have to pass it to the ViewModel somehow - either constructor, property, or helper class.. Certainly not ideal, but I have not found any way around it. I'll be interested to see if anyone has found a solution.

See this thread on some code to make life easier... How do I determine if I need to dispatch to UI thread in WinRT/Metro?

Community
  • 1
  • 1
Jeff Brand
  • 5,623
  • 1
  • 23
  • 22