Shared ViewModels in Silverlight Applications using Messaging and Reflection

There are some situations, where you need to develop multiple Silverlight applications to be hosted on the same web page. I recently came across this scenario while developing some solution components for Microsoft Dynamics CRM 2011. Well, developing a couple of Silverlight applications and hosting them on the same web page is not a problem. But what if these applications need to be able to communicate or share some kind of state with each other?

Let’s start with something easy:

the LocalMessageSender and LocalMessageReceiver classes. These classes provide the means to establish a communication channel between Silverlight applications. The concept is quite simple, you register a receiver and a sender in each of the two applications that have to communicate with each other and then they are able to exchange messages with one another.

The LocalMessageSender class

You can instantiate a LocalMessageSender using the name of the receiver that is supposed to receive the message on the other end:

var localMessageSender = new LocalMessageSender("remoteReceiverName");

You can send a string message to the other end using:

localmessageSender.SendAsync("Hello Remote Receiver!");

The LocalMessageReceiver class

You can instantiate a LocalMessageReceiver using a name to register the receiver. This name is the name that will be used as the receiver name in the LocalMessageSender of the remote application:

var localMessageReceiver = new LocalMessageReceiver("nameOfTheLocalReceiver");

This receiver can listen for messages from the remote application using:

localMessageReceiver.Listen();
localMessageReceiver.MessageReceived += this.OnMessageFromRemoteApplicationReceived;

So now, let’s have a look at the code that each application needs to set everything up:

Application 1

private void SetupCommunications()
{
    var localMessageReceiver = new LocalMessageReceiver("Application1");
    localMessageReceiver.Listen();
    localMessageReceiver.MessageReceived += this.OnMessageFromRemoteApplicationReceived;

    var localMessageSender = new LocalMessageSender("Application2");
    localMessageSender.SendAsync("Hello from Application1!");
}

private void OnMessageFromRemoteApplicationReceived(object sender, MessageReceivedEventArgs messageReceivedEventArgs)
{
    MessageBox.Show(messageReceivedEventArgs.Message);
}

Application 2

private void SetupCommunications()
{
    var localMessageReceiver = new LocalMessageReceiver("Application2");
    localMessageReceiver.Listen();
    localMessageReceiver.MessageReceived += this.OnMessageFromRemoteApplicationReceived;

    var localMessageSender = new LocalMessageSender("Application1");
    localMessageSender.SendAsync("Hello from Application2!");
}

private void OnMessageFromRemoteApplicationReceived(object sender, MessageReceivedEventArgs messageReceivedEventArgs)
{
    MessageBox.Show(messageReceivedEventArgs.Message);
}

Taking it a step further: Shared ViewModels

Well, the LocalMessageSender and LocalMessageReceiver classes are helpful, they certainly provide a means of communication between applications, but what if I want to take it a a step further? The above classes are only able to exchange string messages. What if I want to exchange objects, or what if I want to share some state between them? Even better: what if I love MVVM and want them to share the same ViewModel?

I really liked the idea of a shared ViewModel. So I developed a library that allows Silverlight applications to do exactly this. The applications have no idea about what happens under the hood, they just know about a class named “SynchronizedViewModel”, in which they can inject their ViewModel and the magic already happens. “But how?” you will ask: using Reflection and Serialization I will answer.

When thinking about how to implement this, I couldn’t help but thinking about the OSI Model with its layered structure (good old university times), in which each layer is unaware of the specific implementations of the other layers and it just knows that the underlying layer can get the job done (reliably or not reliably).

So I thought of the LocalMessageSender and LocalMessageReceiver classes as my primitive lowest level layer that can only exchange string messages. My next layer on the way to the top would be a layer that could give some meaning to these string messages, a layer that could structure these strings into something meaningful, say an object? And what best way to do this than using the DataContractJsonSerializer class?

So here is my JsonSerializer class:

using System;

namespace Silverlight.Synchronization
{
    using System.IO;
    using System.Runtime.Serialization.Json;
    using System.Text;

    internal class JsonSerializer
    {
        public static string Serialize(Type objectType, object objectToSerialize)
        {
            using (var stream = new MemoryStream())
            {
                var serializer = new DataContractJsonSerializer(objectType);
                serializer.WriteObject(stream, objectToSerialize);
                stream.Position = 0;
                using (var reader = new StreamReader(stream))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        public static object Deserialize(Type objectType, string jsonString)
        {
            using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
            {
                var serializer = new DataContractJsonSerializer(objectType);
                return serializer.ReadObject(stream);
            }
        }
    }
}

It’s just a generic and simple JSON serializer class.

The next step was to think about how I could synchronize properties between the ViewModels of the Silverlight applications. So I decided to use Reflection. The idea was that I would send a message to the other application containing a property name and a property value of the ViewModel. Then the receiving application would get the message, deserialize it and set its own ViewModel property using Reflection. Easy, right?

This led me to creating a class ”SynchronizedProperty” for the properties that are to be synchronized:

namespace Silverlight.Synchronization
{
    internal class SynchronizedProperty
    {
        public string PropertyName { get; set; }

        public string PropertyValue { get; set; }
    }
}

This class will contain the Name of the ViewModel property and its value as a JSON Serialized string. What I want to do is Serialize this class once again using JSON, to send it over the wire.

So I wrote a class called “RemoteViewModel” that would handle receiving and sending serialized messages, deserializing messages and extracting the SynchronizedProperty class out of them.

namespace Silverlight.Synchronization
{
    using System;
    using System.Windows.Messaging;

    internal class RemoteViewModel : INotifyRemotePropertyChanged
    {
        private readonly LocalMessageReceiver localMessageReceiver;

        private readonly LocalMessageSender localMessageSender;

        public RemoteViewModel(
            string localApplicationName,
            string remoteApplicationName)
        {
            if (localApplicationName == null)
            {
                throw new ArgumentNullException("localApplicationName");
            }

            if (remoteApplicationName == null)
            {
                throw new ArgumentNullException("remoteApplicationName");
            }

            this.localMessageReceiver = new LocalMessageReceiver(localApplicationName);
            this.localMessageSender = new LocalMessageSender(remoteApplicationName);
            this.localMessageReceiver.Listen();
            this.localMessageReceiver.MessageReceived += this.OnMessageFromRemoteApplicationReceived;
        }

        private void OnMessageFromRemoteApplicationReceived(object sender, MessageReceivedEventArgs messageReceivedEventArgs)
        {
            if (messageReceivedEventArgs == null)
            {
                throw new ArgumentNullException("messageReceivedEventArgs");
            }

            var synchronizedProperty = (SynchronizedProperty)JsonSerializer.Deserialize(
                typeof(SynchronizedProperty), messageReceivedEventArgs.Message);
            this.InvokeSynchronizedPropertyChangedEvent(synchronizedProperty);
        }

        public event EventHandler<SynchronizedPropertyChangedEventArgs> SynchronizedPropertyChanged = delegate { };

        public void InvokeRemotePropertyChanged(string propertyName, Type propertyType, object propertyValue)
        {
            var jsonPropertyValue = JsonSerializer.Serialize(propertyType, propertyValue);
            var synchronizedProperty = new SynchronizedProperty { PropertyName = propertyName, PropertyValue = jsonPropertyValue };
            var jsonSynchronizedProperty = JsonSerializer.Serialize(typeof(SynchronizedProperty), synchronizedProperty);
            this.localMessageSender.SendAsync(jsonSynchronizedProperty);
        }

        private void InvokeSynchronizedPropertyChangedEvent(SynchronizedProperty synchronizedProperty)
        {
            this.SynchronizedPropertyChanged(this, new SynchronizedPropertyChangedEventArgs(synchronizedProperty));
        }
    }
}

This class wraps the whole communication with the other Silverlight application. It is listening for messages from the other end, then it deserializes the messages to SynchronizedProperty classes and invokes events to notify about changes in properties. Sounds a lot like the INotifyPropertyChanged interface, so that’s why I created a INotifyRemotePropertyChanged interface for my class to implement:

namespace Silverlight.Synchronization
{
    using System;

    internal interface INotifyRemotePropertyChanged
    {
        event EventHandler<SynchronizedPropertyChangedEventArgs> SynchronizedPropertyChanged;

        void InvokeRemotePropertyChanged(string propertyName, Type propertyType, object propertyValue);
    }
}

and a SynchronizedPropertyChangedEventArgs class to hold my SynchronizedProperty:

namespace Silverlight.Synchronization
{
    using System;

    internal class SynchronizedPropertyChangedEventArgs : EventArgs
    {
        public SynchronizedPropertyChangedEventArgs(SynchronizedProperty synchronizedProperty)
        {
            if (synchronizedProperty == null)
            {
                throw new ArgumentNullException("synchronizedProperty");
            }

            this.SynchronizedProperty = synchronizedProperty;
        }

        public SynchronizedProperty SynchronizedProperty { get; private set; }
    }
}

Finally, I needed another class that would handle the top level layer. A class that would do all the Reflection stuff and update Properties in the ViewModel. So I wrote the SynchronizedViewModel class:

using System;

namespace Silverlight.Synchronization
{
    using System.ComponentModel;
    using System.Globalization;

    public class SynchronizedViewModel
    {
        private readonly INotifyPropertyChanged viewModel;

        private readonly INotifyRemotePropertyChanged remoteViewModel;

        public SynchronizedViewModel(
            INotifyPropertyChanged viewModel,
            string localApplicationName,
            string remoteApplicationName)
        {
            if (viewModel == null)
            {
                throw new ArgumentNullException("viewModel");
            }

            if (localApplicationName == null)
            {
                throw new ArgumentNullException("localApplicationName");
            }

            if (remoteApplicationName == null)
            {
                throw new ArgumentNullException("remoteApplicationName");
            }

            this.viewModel = viewModel;
            this.viewModel.PropertyChanged += this.OnLocalPropertyChanged;
            this.remoteViewModel = new RemoteViewModel(localApplicationName, remoteApplicationName);
            this.remoteViewModel.SynchronizedPropertyChanged += this.OnSynchronizedPropertyChanged;
        }

        private void OnSynchronizedPropertyChanged(object sender, SynchronizedPropertyChangedEventArgs e)
        {
            var synchronizedProperty = e.SynchronizedProperty;
            var localPropertyInfo = this.viewModel.GetType().GetProperty(synchronizedProperty.PropertyName);
            if (localPropertyInfo == null)
            {
                return;
            }

            var localPropertyValue = localPropertyInfo.GetValue(this.viewModel, null);
            var jsonLocalPropertyValue = JsonSerializer.Serialize(localPropertyInfo.PropertyType, localPropertyValue);
            if (jsonLocalPropertyValue == synchronizedProperty.PropertyValue)
            {
                return;
            }

            var synchronizedPropertyValue = JsonSerializer.Deserialize(
                localPropertyInfo.PropertyType, synchronizedProperty.PropertyValue);

            localPropertyInfo.SetValue(
                this.viewModel,
                Convert.ChangeType(synchronizedPropertyValue, localPropertyInfo.PropertyType, CultureInfo.InvariantCulture),
                null);
        }

        private void OnLocalPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var propertyName = e.PropertyName;
            var propertyInfo = this.viewModel.GetType().GetProperty(e.PropertyName);
            var propertyValue = propertyInfo.GetValue(this.viewModel, null);
            if (propertyValue != null)
            {
                this.remoteViewModel.InvokeRemotePropertyChanged(
                    propertyName, propertyInfo.PropertyType, propertyValue);
            }
        }
    }
}

The constructor of the class expects a class that implements INotifyPropertyChanged, in this case this would be the ViewModel to be synchronized. It also expects two string names. A name for the local application and a name for the remote application to use for the registration of the senders and receivers. This class listens for Property changes in the viewmodel and forwards these changes to the underlying transmission channel. It also listens for changes transmitted from the Remote ViewModel and uses Reflection to set the new Value of the Property to the local ViewModel.

So all in all, say you have an application “Application1” and an application “Application2”. In order to synchronize their ViewModels, they both have to reference my Synchronization library and only use these lines of code to synchronize their ViewModels:

Application 1:

INotifyPropertyChanged viewModel = new ViewModel();
var synchronizedViewModel = new SynchronizedViewModel(viewModel, "Application1", "Application2");

Application 2:

INotifyPropertyChanged viewModel = new ViewModel();
var synchronizedViewModel = new SynchronizedViewModel(viewModel, "Application2", "Application1");

That’s all. Once they have done this, all ViewModel Properties that have the same name across the applications will be synchronized!

I wrote a sample web page that implements this scenario. It includes two separate SIlverlight Applications on the same web page. They both have a textbox, and everything you type in the one textbox automatically propagates to the other textbox through the synchronized ViewModels!

You can find this article including source code at the MSDN Code Gallery here.