1 May
2010

Communicating Between Silverlight Applications

Category:UncategorizedTag: , :

Silverlight is a great platform for creating rich internet applications, but a full blown Silverlight app is not a solution to every web application.  Lets face it, the vast majority of web applications will always be some type of server based solution, such as ASP.NET, PHP, JSP, etc…  Everyday I see more and more creative uses of Silverlight inside web applications.  Developers are starting to use Silverlight more like Flash by creating responsive site navigation, web intros and splash pages,  animated ad rotators, and more.  They do this by using small Silverlight applications in parts of their web pages, instead of using Silverlight as the main application itself.  Some use more than one Silverlight app in a single page.  But what happens when you need these separate Silverlight applications to start communicating with each other?  For example, I may need a Silverlight-based banner ad with an animated effect that crosses over to a sidebar ad.  How would I coordinate the transition?  Easy, local messaging.

Local messaging enables you to create communication channels between multiple Silverlight plug-ins running on a single computer. You typically host the plug-ins in a single Web page and use local messaging to coordinate their behavior. This enables you to create complex layouts that combine multiple Silverlight-based applications with content based on other technologies.  You can also use local messaging to establish communication between a Silverlight-based application in a Web page and another one running outside the browser.  Heck, you can even go cross domains.

The Basics

Establishing a connection between Silverlight applications is as simple as creating a LocalMessageSender object in one application and a LocalMessageReceiver in another application.

The Receiver

When you create the receiver, you must give it a name that is unique globally or across the host domain.  Once you have created the receiver, you must handle the MessageReceived event.  This event is fired when a message is successfully received from a sender.  The sent message is available in the Message property.  The next required step is to call the Listen method on the receiver.  The receiver will continue receiving messages until you call its Dispose method.

LocalMessageReceiver messageReceiver = new LocalMessageReceiver(Constants.ReceiverName);
messageReceiver.MessageReceived += (object sender, MessageReceivedEventArgs e) =>
{
????//do something with e.Message
};
messageReceiver.Listen();

After a message is received you can send a response back to the sender by setting the Response property in the MessageReceived event handler.

messageReceiver.MessageReceived += (object sender, MessageReceivedEventArgs e) =>
{
????e.Response = "Message Received: " + e.Message;
};

The Sender

When you create the sender you must specify the receiver that will be listening for the messages.  To send a message, the sending application calls the SendAsync method, passing in a String message with a maximum size of 40 kilobytes.

LocalMessageSender sender = new LocalMessageSender(Constants.ReceiverName);
sender.SendAsync(message);

Any response from the receiver can be handled in the SendCompleted event.

sender.SendCompleted += (o, e) =>
{
????string response = e.Response;
};

The Sample Application

I have created a sample application to demonstrate a basic example of communicating between two Silverlight applications.  In my example, I decided to send an object as a message instead of a simple string, because lets be real, most of the time we need to send a complex message.  The first application has a list of objects.  As I select an object in the list, I will send that object to the second application which will display the objects details.  Once the receiving application has received the object, it will send a response back to the sending application, and the sending application will display the response.

I won?t bore you with all the code in my sample application.  You will get to download it anyways.  I just want to point out the important stuff.

The Sender

private void SendMessage(Entity entity)
{
????//serialize my object to a string
????string json = JsonUtilities.SerializeObjectToJson(entity);

????LocalMessageSender sender = new LocalMessageSender(Constants.ReceiverName);
????sender.SendCompleted += (o, e) =>
????{
????????Response.Text = string.Format("{0}", e.Response);
????};
????sender.SendAsync(json);
}

Each time a selection is made in the ListBox, this method is called and the selected object is passed to it.  I am also creating a handler or the SendCompleted event to display any responses sent back from the receiver. 

When sending complex messages, such as an object graph, you need to consider the size of your message.  Remember that 40 kb limit mentioned earlier?  Well, you better.  In order to send our complex message we need to serialize the object into something the other application to understand.  The easiest ways are XML and JSON, and since XML is heavy, I prefer JSON.  So I created a nice little helper method that converts my object graph into a JSON string.

public static string SerializeObjectToJson(object objectToSerialize)
{
????using (MemoryStream ms = new MemoryStream())
????{
????????DataContractJsonSerializer serializer = new DataContractJsonSerializer(objectToSerialize.GetType());
????????serializer.WriteObject(ms, objectToSerialize);
????????ms.Position = 0;

????????using (StreamReader reader = new StreamReader(ms))
????????{
????????????return reader.ReadToEnd();
????????}
????}
}

The Receiver

The receiver is extremely simple.  It has a view that shows the details of my object.

<Grid Grid.Row="1">
????<Grid.RowDefinitions>
????????<RowDefinition Height="Auto" />
????????<RowDefinition Height="Auto"/>
????????<RowDefinition Height="Auto"/>
????</Grid.RowDefinitions>
????<Grid.ColumnDefinitions>
????????<ColumnDefinition Width="Auto"/>
????????<ColumnDefinition Width="*"/>
????</Grid.ColumnDefinitions>
????
????<TextBlock Text="Customer Name: " Margin="4" />
????<TextBlock Grid.Column="1" Text="{Binding CustomerName}" Margin="4" />

????<TextBlock Grid.Row="1" Text="Phone Number: " Margin="4" />
????<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding PhoneNumber}" Margin="4" />

????<TextBlock Grid.Row="2" Text="Unit Sales: " Margin="4" />
????<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding UnitSales}" Margin="4" />
</Grid>

It has a LocalMessageReciever that listens for any messages sent from my sender application.  It takes the JSON message, deserializes it back into an object.  Set the DataContext of the form, and sends the response back to the sender application.

public MainPage()
{
????InitializeComponent();

????LocalMessageReceiver messageReceiver = new LocalMessageReceiver(Constants.ReceiverName);
????messageReceiver.MessageReceived += (object sender, MessageReceivedEventArgs e) =>
????{
????????//deserialize my json object
????????var entity = JsonUtilities.DeserializeObjectToJson<Entity>(e.Message);
????????this.DataContext = entity;
????????e.Response = string.Format("{0} Recieved", entity.CustomerName);
????};
????messageReceiver.Listen();
}

Here is my JSON deserialization method:

public static T DeserializeObjectToJson<T>(string jsonString)
{
????using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
????{
????????DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
????????return (T)serializer.ReadObject(ms);
????}
}

The Final Product

image

That is all there is to it.  As you can see, communicating between multiple Silverlight applications is pretty simple, and you can get pretty crazy with it, especially when you have an IE hosted application communicating with a Out-Of-Browser Silverlight app.  Cool stuff!

Download the Source and start tinkering.

Find me

RSS
Facebook
Twitter
LinkedIn
SOCIALICON
SOCIALICON

Disclaimer

The opinions and content expressed here are my own and not those of my employer.