Silverlight 3: What's New with Local Messaging

This is the third post in a series where I will be walking through some of the new features in Silverlight 3. I enjoyed reading Tim Sneath’s blog post, https://visitmix.com/Opinions/Whats-New-in-Silverlight-3 but as I read the post I kept saying cool, but how do I do it. So in these posts I will try to answer the “how do I” questions around the new features.

Let’s get started.

Local Messaging

Local Messaging provides cross Silverlight plug-in messaging. This will enable you to communicate between multiple controls on the same page. You can also communicate across browser tabs and even across browsers. String messages. You can only talk with other Silverlight controls from the same domain and if both controls opt-in to communicate. You can of course choose to open your control up to communicate with any control. This feature really opens up a world of cool composite applications. In my book, SharePoint Development using Silverlight 2.0, I demonstrate a technique to communicate between Silverlight controls on the same page using the HTMLBridge. But now with Silverlight 3 you can communicate across tabs and across browsers.

Create a new Silverlight project

In order to create Silverlight 3 projects you will need to install Visual Studio 2008, and the Silverlight 3 SDK and tools. Create a Silverlight C# project called "Local Messaging" and accept the defaults which will create 2 projects in the solution.

Create the UI

In this example I want to keep it super simple to let you see how the communication works. You will create a single form that has 2 text boxes, one for the local text and one for the remote text. Whatever you type in the local text box will be echoed in the remote text box.  Here is what the MainPage.xaml form will look like when you are finished.

image

Here is the xaml for the MainPage.xaml. The remote text box is readonly because it is set by the incoming message.

 <UserControl
     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" 
    x:Class="Local_Messaging.MainPage" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">

 
        <TextBox x:Name ="LocalText"  
                 Height="24" Margin="76,63,82,0" 
                 VerticalAlignment="Top" Text="Local Text" 
                 TextWrapping="Wrap" KeyUp="LocalText_KeyUp"/>
        <TextBox x:Name ="RemoteText"  
                 Margin="76,144,82,132" Text="Remote Text" 
                 TextWrapping="Wrap" IsReadOnly ="True" />
        <controls:Label x:Name="LocalLabel" 
                        HorizontalAlignment="Left" Margin="76,43,0,0" 
                        VerticalAlignment="Top" Content="Local Text"/>
        <controls:Label x:Name="LocalLabel_Copy" 
                        HorizontalAlignment="Left" Margin="76,124,0,0" 
                        VerticalAlignment="Top" Content="Remote Text"/>

    </Grid>
</UserControl>

Get the end point names

Each endpoint requires a unique name. One of the easiest ways to set this name is to pass in the values as parameters to the Silverlight plug-in. Open the App.xaml.cs file and update the Application_Startup method. This passes the 2 params LocalName and RemoteName to the constructor of the MainPage.xaml.

 private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = 
        new MainPage(e.InitParams["LocalName"] , 
                     e.InitParams["RemoteName"] );
}
Update the constructor

Update the constructor of the MainPage.xaml.cs to get the startup parameters.

 public partial class MainPage : UserControl
{
    string localName, remoteName;

    public MainPage(string localName, string remoteName)
    {
        InitializeComponent();

        this.localName = localName;
        this.remoteName = remoteName;
    }
}

Listen for incoming messages

Add the code to listen for incoming messages.

 public MainPage(string localName, string remoteName)
{
    InitializeComponent();

    this.localName = localName;
    this.remoteName = remoteName;

    //Create a new message reciever with a local name passed into the plugin
System.Windows.Messaging.LocalMessageReceiver incomingMessage =        new System.Windows.Messaging.LocalMessageReceiver(localName); 


    //Add an event handler for a new message
incomingMessage.MessageReceived += incomingMessage_MessageReceived; 

    //incomingMessage.MessageReceived += 
    //    new EventHandler<
    //        System.Windows.Messaging.MessageReceivedEventArgs>
    //        (incomingMessage_MessageReceived);
    
    //Start listening
incomingMessage.Listen(); 
}

In this code I have left in the fully qualified name to show you that the LocalMessageReceiver is from the System.Windows.Messaging namespace. Also I have commented out the full event handler which is what is created from intellisense and replaced it with just the name of the eventhandler method. The code is very simple, Create a message receiver giving a unique name, then hook an event for incoming messages and finally start listening.

Handle the incoming message

When a new message arrives the event handler is called. The incoming message is passed to the event as a System.Windows.Messaging.MessageReceivedEventArgs object. Simply set the text of the remoteText textbox with the message property.

 void incomingMessage_MessageReceived(object sender, System.Windows.Messaging.MessageReceivedEventArgs e)
{
    RemoteText.Text = e.Message;
}
Send a message

Sending a message is just as easy. Create a LocalMessageSender object with the unique name of the receiver of the message. The send is asynchronous so you can handle an event when the message has been sent. Finally just call the SendAsync method passing the message as a string.

 private void LocalText_KeyUp(object sender, KeyEventArgs e)
{
    //Send a message
    string message = LocalText.Text;

    System.Windows.Messaging.LocalMessageSender outgoingMessage =        new System.Windows.Messaging.LocalMessageSender(remoteName); 


    outgoingMessage.SendCompleted += outgoingMessage_SendCompleted; 
        
        //new EventHandler<
        //    System.Windows.Messaging.SendCompletedEventArgs>
        //    (outgoingMessage_SendCompleted);

outgoingMessage.SendAsync(message); 

}
Set the parameters for the Silverlight plugin

The last step is to modify the test aspx page to include 2 controls (local and remote) and set the parameters. I have also set the border color to make it easier to see the 2 controls. Change the Local MessagingTestPage.aspx with the following code. You can see in the code that I swap the local and remote names for the 2 controls.

 <body>
    <form id="form1" runat="server" style="height:100%;">
        <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
        <div style="padding: 20px; margin: 20px; background-color: #FF0000">
            <asp:Silverlight 
            ID ="SilverlightAppOne"  
            runat="server" 
            Source="~/ClientBin/Local Messaging.xap" 
            MinimumVersion="3.0.40307.0" 
            Width="100%" Height="100%"
            InitParameters ="LocalName=AppOne,RemoteName=AppTwo" 
             />
        </div>
        <div style="padding: 20px; margin: 20px; background-color: #0000FF">
            <asp:Silverlight 
            ID ="SilverlightAppTwo"  
            runat="server" 
            Source="~/ClientBin/Local Messaging.xap" 
            MinimumVersion="3.0.40307.0" 
            Width="100%" Height="100%"
            InitParameters ="LocalName=AppTwo,RemoteName=AppOne" 
             />
        </div>
    </form>
</body>

 
Run the App

This demonstrates the controls communicating with each other on the same page.

image

Communicate across tabs

You are not restricted to the same page. You can also communicate across browser tabs.

image image

Communicate across browsers

Not only can you communicate intra-page, but across tabs and across different browsers

Joe Stegman has a great demo of the Silverlight Chess application battling across different browsers.

https://blogs.msdn.com/jstegman/archive/2009/03/23/local-messaging-samples.aspx

image

Advanced / Security

You can also control the domains that you receive messages from. There is an overload for the LocalMessageReceiver class that allows you to pass the scope of the name of the receiver and a collection of valid domains.