Silverlight 3 – Local Messaging Explained + Enhancement

One of the new features Silverlight 3 introduced is called Local Messaging. This feature supports communication between different Silverlight applications that are running on the same client. This is particular useful in areas like Sharepoint where you offer different parts to be positioned at all places on the screen. It’s possible to even communicate between two Slverlight applications running on different domains. For example one app is running on maindomain.com and the second app is running on maindomain.nl or even more different.

The Local Message API is very easy to use. But let’s first setup the Proof of Concept environment. I created a solution with two Silverlight Applications (SUILeft and SUIRight), a hosting project (Web) and a fourth project which is a Silverlight Class Library, we will come to that soon.

image

I combined both the “Left” and “Right” applications in one html page, like this (please remind to change the paths according to your setup).

<div id="silverlightControlHost">
    <object data="data:application/x-silverlight-2," type="application/x-silverlight-2"
        width="40%" height="100%">
        <param name="source" value="ClientBin/MM.Silverlight.SUILeft.xap" />
        <param name="onError" value="onSilverlightError" />
        <param name="background" value="white" />
        <param name="minRuntimeVersion" value="3.0.40624.0" />
        <param name="autoUpgrade" value="true" />
        <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration: none">
            <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight"
                style="border-style: none" />
        </a>
    </object>
    <object data="data:application/x-silverlight-2," type="application/x-silverlight-2"
        width="40%" height="100%">
        <param name="source" value="ClientBin/MM.Silverlight.SUIRight.xap" />
        <param name="onError" value="onSilverlightError" />
        <param name="background" value="white" />
        <param name="minRuntimeVersion" value="3.0.40624.0" />
        <param name="autoUpgrade" value="true" />
        <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration: none">
            <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight"
                style="border-style: none" />
        </a>
    </object>
</div>
<iframe id="_sl_historyFrame" style="visibility: hidden; height: 0px; width: 0px;
    border: 0px"></iframe>

Coding the Receiver (Sender to Receiver)

I coded the receiver in the “Left” application. In the constructor I created a new LocalMessageReceiver with the name PoC.Messaging. This name is important because also the sending applications need to know this.

private readonly LocalMessageReceiver _receiver;

public MainPage()
{
    InitializeComponent();
    _receiver = new LocalMessageReceiver("PoC.Messaging");
    _receiver.MessageReceived += _receiver_MessageReceived;
    _receiver.Listen();
}

private void _receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
{
    ResultBlock.Text = e.Message;
}

 

Sending messages (Sender to Receiver)

The sender looks similar, make sure the same name is used. I added a click handler to a button to send the static “Hello World!” messag.

private readonly LocalMessageSender _sender;

public MainPage()
{
    InitializeComponent();
    _sender = new LocalMessageSender("PoC.Messaging");
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    _sender.SendAsync("Hello World!");
}

 

Respond to messages (Receiver to Sender)

After receiving a message there’s an option to reply by simply setting the response. The enhanced MessageReceived handler now looks like this:

private void _receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
{
    ResultBlock.Text = e.Message;
    e.Response = "Hello to you as well!";
}

Accepting response messages (Receiver to Sender)

To enable the sender to accept response messages, a new handler needs to be added to the SendCompleted event. The complete sender code now looks like this:

private readonly LocalMessageSender _sender;

public MainPage()
{
    InitializeComponent();
    _sender = new LocalMessageSender("PoC.Messaging");
    _sender.SendCompleted += _sender_SendCompleted;
}

private void _sender_SendCompleted(object sender, SendCompletedEventArgs e)
{
    string response = e.Response;
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    _sender.SendAsync("Hello World!");
}

This is all you need to do, nothing more, nothing less. But limited to only send strings from sender to receiver, and sending messages back to the sender. But we want more, we don’t want to be limited to sending strings, do we?

Enhancing Local Messaging

The idea is simple: Serialize complete objects on the sender, deserialize the strings back to objects on the receiver.

It’s not difficult at all, we have the same API available in Silverlight as we are used in the full CLR. Just add a reference to System.Xml.Serialization in both the “Left” and “Right” project. Also this is where the “Messages” project comes in to play, add a reference to this project from “Left” and “Right” as well.

This was just for preparation. The Messages project will contain the structures that are going to be serialized and deserialized, kind of a message contract. So let’s add our first structure: Customer.

public class Customer
{
    public string Name { get; set; }
    public string Country { get; set; }
}

This is still a very simple class, but this idea works with larger objects as well. But it’s important to explain there’s a limit to the size of the message that is send, it’s 40 kilobyte. It’s time for the little bit of sending magic.

private void Button_Click(object sender, RoutedEventArgs e)
{
    var customer = new Customer {Name = "Best Customer", Country = "Silverlight Island"};
    var serializer = new XmlSerializer(typeof(Customer));
    using (var sw = new StringWriter())
    {
        serializer.Serialize(sw, customer);
        _sender.SendAsync(sw.GetStringBuilder().ToString());
    }
}

And on the receiver side we need to deserialize the message, like this:

private void _receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
{
    var serializer = new XmlSerializer(typeof (Customer));
    if (serializer.CanDeserialize(XmlReader.Create(new StringReader(e.Message))))
    {
        var deserializedCustomer =
            serializer.Deserialize(XmlReader.Create(new StringReader(e.Message))) as Customer;

        ResultBlock.Text = string.Format("Name:{0}, Country:{1}", deserializedCustomer.Name,
                                         deserializedCustomer.Country);
    }
}

That’s all. I’m interested to see more Silverlight applications making use of Local Messaging.