In Silverlight 3, there’s a proper mechanism to connect multiple plug-in instances on a single web page with each other. I wrote a hacky mechanism for this a while ago ( which I can’t currently find! ) based on the HTML bridge so it’s nice to see a proper mechanism for doing this and it’s called “Local Connection”.
With Local Connection, there are two key classes;
- LocalMessageSender
- LocalMessageReceiver
and it’s pretty likely that there’ll be 2 or more Silverlight controls on the same page which may or may not come from the same domain ( which can be “interesting” from a security perspective and so needs factoring into the capabilities and the object model that surfaces them ).
As a simple example, I can write a “sending” application with a UI defined like this;
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel x:Name="LayoutRoot" Background="Red">
<TextBox
Margin="10"
FontSize="24"
x:Name="txtMessage" />
<Button
Content="Send"
HorizontalAlignment="Right"
Margin="10"
Click="OnSendMessage" />
<TextBlock
Foreground="White"
FontSize="24"
x:Name="txtResponse"
HorizontalAlignment="Center" />
</StackPanel>
</UserControl>
and a little bit of code that sits behind it and sends the message ( on the button press ) to a receiver which has come from the same domain as the sender;
public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); } void OnSendMessage(object sender, RoutedEventArgs args) { // This is sending to receivers served from the same domain name. // Otherwise, we can use the 2nd constructor which also takes a // domain name as in; // LocalMessageSender msgSender = new LocalMessageSender("myReceiver", // "someotherdomain.com"); LocalMessageSender msgSender = new LocalMessageSender("myReceiver"); // Can also register for notification when the message has been picked // up EventHandler<SendCompletedEventArgs> handler = null; handler = (s, e) => { Dispatcher.BeginInvoke(() => { msgSender.SendCompleted -= handler; if (e.Error != null) { txtResponse.Text = String.Format("Error [{0}]", e.Error.Message); } else { txtResponse.Text = String.Format("Response [{0}]", e.Response == null ? "None" : e.Response); } }); }; msgSender.SendCompleted += handler; // Asynchronous send/response msgSender.SendAsync(txtMessage.Text); } }
and then I can have a receiving application which might have a simple UI like this;
<UserControl x:Class="SilverlightApplication2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Grid x:Name="LayoutRoot" Background="Blue" >
<TextBlock
Foreground="White"
FontSize="24"
x:Name="txtMessage"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</UserControl>
and a little bit of code behind that which uses the LocalMessageReceiver in order to pick up messages sent to its “name” from code which was served from the same domain;
public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); this.Loaded += OnLoaded; } void OnLoaded(object sender, RoutedEventArgs args) { // Only listening to messages sent to this name from the same domain // that this code was loaded from. Can specify an overload that // is "Global" which means this receiver name needs to be globally // unique and also a list of domains allowed to send us messages. receiver = new LocalMessageReceiver("myReceiver"); receiver.MessageReceived += (s, e) => { e.Response = "Thanks for the message!"; Dispatcher.BeginInvoke(() => { txtMessage.Text = String.Format("Received [{0}]", e.Message); }); }; // Asynchronous listening - not blocking anything here receiver.Listen(); } LocalMessageReceiver receiver; }
and that’s pretty much it – note that the receiver doesn’t have to send a response back to the sender if they don’t want to and the delivery is async’d in both directions.
The additional complexity comes around cross-domain operations and whether the sender is willing to send only to code from its own domain and/or whether the receiver is willing to pick up messages only from its own domain and there’s a couple of additional constructors/properties that come into play for dealing with those scenarios as indicated by the comments in the code above.
You can download the code for this post from here.
This is one of a series of posts taking a quick look at Silverlight 3 – expect them to be a little “rough and ready” and that they’ll get expanded on as I’ve had more time to work with Silverlight 3.