DispatchedMessenger: making sure event handlers are called by a specific thread

Jun 3, 2015 at 9:27 AM
Hi,

my app displays information on two screens using Windows 8.1's ProjectionManager. Each screen has a separate UI thread and has its own View and ViewModel. I want these two ViewModels to communicate with StyleMVVM's DispatchedMessenger. However, it seems to me that the messenger doesn't switch the thread context when it sends a message. When I send a message from VM1 to VM2, the event handler of VM2 should be called by UI thread 2 but is actually called by UI thread 1.

Is there a way of making sure that event handlers are only called by a specific thread? If this is not possible with StyleMVVM, does anybody know a library that does that (not a whole new framework, but one only implementing the mediator pattern)?

Thanks!
Sr258
Coordinator
Jun 3, 2015 at 2:34 PM
Hi sr258,

The DispatchedMessenger is actually designed to handle just this specific situation. When a VM handlers are registered it uses the current Dispatcher as the dispatcher messages should be sent on. It sounds like for what ever reason the VM is being created on thread1.

How are you starting the second UI thread? How are you instantiating the Views and VM?

-Ian
Jun 3, 2015 at 4:01 PM
Edited Jun 3, 2015 at 4:04 PM
Thanks for your quick answer!

VM2 is instanciated by the dependency injection container when the view is created. I navigate to the view in a Frame. The code is mostly taken from the example for the ProjectionManager from MSDN.

Here is how I create the second view:
CoreApplicationView secondView = CoreApplication.CreateNewView();
int? secondViewId = null;
var thisDispatcher = Window.Current.Dispatcher;

await secondView.Dispatcher.RunAsync(
    CoreDispatcherPriority.Normal,
    () =>
    {
        secondViewId = ApplicationView.GetForCurrentView().Id;
        viewLifetimeControl = ViewLifetimeControl.CreateForCurrentView();
        var initData = new ProjectionViewPageInitializationData()
        {
            MainDispatcher = thisDispatcher,
            ProjectionViewPageControl = viewLifetimeControl,
            MainViewId = mainViewId,
            PrimaryView = this
        };

        var rootFrame = new Frame();
        Window.Current.Content = rootFrame;                      
        rootFrame.Navigate(typeof(WheelOfFortune.WheelView), initData);
    });
primaryViewId = mainViewId;
secondaryViewId = secondViewId;
this.viewLifetimeControl.StartViewInUse();
await ProjectionManager.StartProjectingAsync(secondaryViewId.Value, primaryViewId.Value);
this.viewLifetimeControl.StopViewInUse();
VM2 is actually instanciated in the correct thread (UI thread 2). I register at the DispatchedMessenger like this:
public WheelViewModel()
{
    System.Diagnostics.Debug.WriteLine("instanciating WheelViewModel: thread id: " + Environment.CurrentManagedThreadId.ToString());
    StyleMVVM.Messenger.DispatchedMessenger.Instance.Register<TestMessage>(TestMessageHandler);
}
However, when I send the message (see below), the handler is called on UI thread 1.
WheelOfFortune.TestMessage message = new WheelOfFortune.TestMessage() { Content = "Message received!" };
StyleMVVM.Messenger.DispatchedMessenger.Instance.Send(message);
The actual problem I have is that I get an exception when I call OnPropertyChanged in VM2. The exception is:
An exception of type 'System.InvalidCastException' occurred in StyleMVVM.CSharp.DLL but was not handled in user code
Additional information: Das COM-Objekt des Typs "System.ComponentModel.PropertyChangedEventHandler" kann nicht in den Klassentyp "System.ComponentModel.PropertyChangedEventHandler" umgewandelt werden. Instanzen von Typen, die COM-Komponenten repräsentieren, können nicht in andere Typen umgewandelt werden, die keine COM-Komponenten repräsentieren. Eine Umwandlung in Schnittstellen ist jedoch möglich, sofern die zugrunde liegende COM-Komponente QueryInterface-Aufrufe für die IID der Schnittstelle unterstützt.
In English it basically means that the com object of type '...' can't be cast to the class type '...'.
Coordinator
Jun 3, 2015 at 4:09 PM
Ok that sounds like a far deeper problem than what I was hoping :S

I'll see if I can find some time this week to try and take a look. What could really help speed the process along is if you could put together a small demo app showing the problem and I can debug it with the source.

Just so I'm clear this is a WinRT app not a .Net 4.5 app correct?
Jun 3, 2015 at 4:53 PM
Thanks for you effort!

I've compiled a small sample app that reproduces the error. How can I send it to you? My app is WinRT, indeed.