DarksideCookie

Come to the dark side...we have cookies!

SignalR and Silverlight

SignalR is a somewhat newish open source project that makes it possible to simulate push notifications on the web. It does so using long-polling (websockets available as an extra download), so it isn’t true push as such, but it gives the appearance of push. And on top of that, it is so ridiculously simple to set up…

Unfortunately, the NuGet package for SignalR doesn’t include the Silverlight client, which the GitHub project does. This is most likely going to be fixed in the near future, but currently you will have to do it the manual way instead of using NuGet.

The server part of SignalR works fine through NuGet, however, it doesn’t play nice with the Silverlight version as a property has been renamed and causes issues. So we have to do the whole thing manually to make sure both the client and server has the modified property… Once again, it is probably going to be fixed some time really soon.

So let’s start off by running off to GitHub and getting the source. The address is https://github.com/SignalR/SignalR. As soon as that is done, run the build.cmd command file to build the application. This causes some red warnings during the build, but it still seems to work fine.

Next we need to set up a solution of our own to try it out, so start up VS and create a new Silverlight Application project, and host it in a ASP.NET MVC application. As soon as these projects are up and running, it is time to add all the necessary resources.

Let’s start with the Web application. The first thing we need here is of course the SignalR assemblies that we just built, as well as some supporting assemblies. These are easily copied from the newly built SignalR solution. Copy the 3 dll files that you find under /SignalR/bin/Release somwhere good, and then add a reference to them from your web application.

If you want to use SignalR from a web client as well, you will have to upgrade the installed jQuery libraries to a later version as well. ASP.NET MVC apps come with jQuery 1.4.1, and you need at least 1.6.2. But that is ok to just NuGet…

Next, we need to reference the SignalR Silverlight client from the Silverlight project. That assembly is available in the SignalR folder under /SignalR.Client.Silverlight/bin/Release. This time, just copy the 2 assemblies (SignalR and Json.NET) somewhere good, and add a reference to them from the Silverlight project.

Ok, now that all the pre-requisites are in place, it is time to actually write some SignalR code.

I am going to build a really simple application, but SignalR can obviously be used for a lot more complicated scenarios. I am actually going to more or less replicate one of the SignalR JavaScript demos.

So let’s get started. The first thing we need is a Hub. A Hub is a central resource that clients can connect to and use to send, as well as receives messages. All you need to do is create a class that inherits from Hub, and define the methods that you would like the clients to be able to call. In my case, I will create a REALLY simple Hub

public class ChatHub : Hub
{
public void SendMessage(string message)
{
Clients.NewMessage(message);
}
}

The property Clients is a dynamic object that represents all connected clients. By calling a method on it, we are sending a message to each of the clients.

In JavaScript, this is really cool as it looks as though the server is actually invoking a method on the client. In Silverlight, it doesn’t look quite as cool unfortunately.

That is all we need to do on the server end. The Hub will automatically be registered at runtime through an auto-registration feature backed by the Microsoft.Web.Infrastructure assembly that you added a reference to.

Next up is the client. First off, we need a simple chat window. Something like this

<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ItemsControl Grid.ColumnSpan="2" />
<TextBox Grid.Row="1" />
<Button Grid.Row="1" Grid.Column="1" Content="Send" Margin="10,0,0,0" />
</Grid>

Now that that is done, we need a ViewModel to handle the communication. It is once again REALLY simple…

public class MainViewModel
{
private ObservableCollection<string> _messages = new ObservableCollection<string>();

public MainViewModel()
{
SendMessage = new RelayCommand(message =>
{

});
}

public IEnumerable<string> Messages { get { return _messages; } }

public ICommand SendMessage { get; private set; }
}

Obviously all the Hub code is not there yet, but the rest of it is. Very simple. And then that ViewModel needs to be hooked up in the view. That is standard stuff though, so I won’t show that, and instead focus on the SignalR stuff…

The first thing we need is a HubConnection. From that connection, we can create an IHubProxy, which is what we use to send and receive messages from a Hub. The HubConnection takes the Url to the server as a parameter, and works everything else out on its own.

Once we have the IHubProxy, we can hook up a listener using the extension method called On<T>(strrng, Action<T>). The T defines the type of the data being sent, in out case it is a string, but it could be any object that can be serialized using Json. The string is the name of the method that we called on the Clients object on the server, and the Action is the method to be called when a message arrives using the defined event name. Like this

public MainViewModel()
{
var conn = new HubConnection(Application.Current.Host.Source.GetComponents(UriComponents.Scheme | UriComponents.HostAndPort, UriFormat.Unescaped));
var hub = conn.CreateProxy("DarksideCookie.Ag.SignalR.Client.Web.Hubs.ChatHub");
hub.On<string>("NewMessage", message => Deployment.Current.Dispatcher.BeginInvoke(() => _messages.Add(message)));
conn.Start();

SendMessage = new RelayCommand(message => hub.Invoke("SendMessage", message));
}

Oh yeah, don’t forget to start the connection. That is the call that sets everything in motion.

From the code above, you can also see the hub.Invoke() call that we use to call the SendMessage() method on the Hub on the server. Once again, we could pass much more complex objects, but string seemed good to begin with…

If you run the code now, you will see that when you send a message, it comes bouncing back to you  in the “chat box”. And if that isn’t cool enough, try opening 2 windows with the same client in them and then send a message. That is much cooler for some reason even if it shows off exactly the same thing…

So, what else is there to say? Well, I would like to highlight a few things.

The first, and most obvious, is the fact that all Hub callbacks on the client some on the wrong thread. So we need to marshal them to the right one. It would be nice if the SignalR client did that on it’s own, but still a tiny problem…

The next thing is that using SignalR on the server causes restarts, and redeploys, to the IIS to be slow. I believe it is because SignalR is waiting for the long-polling connections to disconnect, which causes the restart to be slower. It can be quite annoying when debugging though…

And finally, I want to mention another nice feature that I am currently using on a project. The Hub class has a static Hub.GetClients<T>() that will get you all clients connected to the hub of type T. Giving you access to the Clients object we had in the hub, anywhere in the application. Using this, we can send messages to clients from anywhere. In my case, the Hub has no functionality at all, so the clients cant call anything on it. But instead, I let my WCF services notify clients of changes by pulling up a client list and sending a message when certain things happen… Very nice and simple…

That was it for my quick SignalR for Silverlight intro. There is obviously code for this. It includes a very minimalistic MVC app and a Silverlight project, that I suggest you look at and then extend to try out the Hub.GetClients<T>() functionality. It is nice!

Code: DarksideCookie.Ag.SignalR.zip (497.61 kb)

Cheers!

Posted: Dec 15 2011, 20:53 by ZeroKoll | Comments (8) |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Manage post: :)

Comments (8) -

Rory United Kingdom said:

If you are going to put include a solution for downloading, at least make sure it works !

Can't even get the Client project to load into the solution

# February 11 2012, 11:49

Jamie Canada said:

Its easy enough to fix.  Just use a new  SL project to get the missing files.

# February 14 2012, 01:08

ZeroKoll Sweden said:

Ok...download was missing Silverlight.js and an aspx. Sorry about that. But it should have been fairly easy to figure out... Download updated...

# February 23 2012, 15:21

Kevin United States said:

Thanks for the post. It got me started Smile. It looks like they've updated the API since you posted. They're now using a port of the Tasks library (it's in SL5, but a separate DLL for SL4). Here's the link: https://github.com/SignalR/SignalR/wiki/SignalR-Client-Hubs

# April 02 2012, 17:20

JD United States said:

Thanks for the post for deploying in silverlight. I think have it implemented about 90% done, I am at a bug I can't seem to figure out, its when I make the hub.invoke call, doesn't call the method that is setup in the hub.on.  I am using silverlight 5 on my local IIS with https.  any help would be greatly appreciated.  

thanks,
jd

# June 27 2012, 22:41

René Koch Germany said:

Thanks a lot for posting this, gave me a godd, quick start.
However, to make it more robust, I'd suggest to use:

            string uri = Application.Current.Host.Source.GetComponents(UriComponents.Scheme | UriComponents.Host, UriFormat.Unescaped);
            string path = Application.Current.Host.Source.LocalPath;
            int idx = path.IndexOf("/ClientBin");
            if (idx > 0) path = path.Substring(0, idx);            
            uri += path;
          
            var conn = new HubConnection(uri);



and
  await conn.Start(); // allow Exceptions
and
  await _hub.Invoke("SendMessage", msg); // allow exceptions

# November 28 2012, 17:18

ZeroKoll Sweden said:

Hi René!
I that is definitely sound good tips. I just tried to keep my code as simple as possible (or at least simple). But thanks for the comments, and I hope people reading this from now on will use your suggestions!
Cheers,
Chris

# December 04 2012, 13:45

akswift Germany said:

Hi,
i just tried to update signalr components to version 1.0.
so i installed the client and server components from nuget.

But i'm not able to get a connection.

conn.Start().Wait(); trhows an exception: ...Not found

kind regards

# February 22 2013, 10:10
Comments are closed