2013-07-24

Connecting a Windows Store app to a website with SignalR

There are different kinds of clients we can use with SignalR. Web browsers, Silverlight, WinRT, WPF are some of them.

On this post, I’ve shown you how to create a chat application with ASP.NET using SignalR library. There I’ve used the browser as the client. I extend that example to integrate a console application as the client in this post.

Now I’m going to show you how to connect a Windows 8 Store app to a website with SignalR library. If you need to know how to implement the server side, please go to the below link

http://ruchirac.blogspot.com/2013/02/creating-chat-application-in-aspnet.html

As the first step, I have to install SignalR .NET Client library to my Windows Store App project. I can easily do this using NuGet Package Manager (PROJECT –> Manage NuGet Package Manager…). Type SignalR on the Manage NuGet Packages dialog box so it will list down all the SignalR libraries. Select Microsoft ASP.NET SignalR .NET Client and click install. It will take care of adding all the relevant DLLs to my project.

Now we are ready to develop the Windows Store App.

I’m going to use the default MainPage.xaml. Below is the markup of that page.

<Page
    x:Class="SignalRWindows8App.MainPage"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SignalRWindows8App"
    xmlns:d="
http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Loaded="Page_Loaded">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Popup x:Name="enterName">
            <Popup.ChildTransitions>
                <TransitionCollection>
                    <PaneThemeTransition />
                </TransitionCollection>
            </Popup.ChildTransitions>
            <Border BorderBrush="{StaticResource ApplicationForegroundThemeBrush}"
                Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
                BorderThickness="2" x:Name="border">
                <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
                    <TextBlock Text="Enter your name:" FontSize="26.667" Margin="0,50,0,0"/>
                    <TextBox x:Name="chatName" HorizontalAlignment="Center" FontSize="26.667" Height="50" Width="300" Margin="0,50,0,0" />
                    <Button Content="OK" HorizontalAlignment="Center" Click="Button_Click_1" Height="50" Width="100" Margin="0,50" />
                </StackPanel>
            </Border>
        </Popup>
        <TextBlock x:Name="chatShow" Margin="0,0,0,243" FontSize="14.667" ScrollViewer.HorizontalScrollMode="Auto" ScrollViewer.VerticalScrollMode="Auto"/>
        <TextBox x:Name="txtMessage" Height="100" Margin="31,573,778,95" />
        <Button Click="Button_Click" Content="Send" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="609,0,655,95" Height="50" Width="100" />
    </Grid>
</Page>

I’m using a popup to enter the name for the user.

Then I have a TextBlock called ‘chatShow’ to display the chat and a TextBox called ‘txtMessage’ for user to enter the chat message. Finally I’m using a button to send the message.

Let’s look at the code behind implementation.

using Microsoft.AspNet.SignalR.Client;
using Microsoft.AspNet.SignalR.Client.Hubs;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace SignalRWindows8App
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        IHubProxy chat;
        public SynchronizationContext Context { get; set; }

        public MainPage()
        {
            this.InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            if (!enterName.IsOpen)
            {
                border.Width = Window.Current.Bounds.Width;
                border.Height = Window.Current.Bounds.Height;
                enterName.IsOpen = true;
                chatName.Focus(FocusState.Pointer);
            }
        }

        async private void makeConnection()
        {
            try
            {
                var hubConnection = new HubConnection("
http://localhost:53748");
                chat = hubConnection.CreateHubProxy("ChatHub");
                chatShow.Text = "";
                Context = SynchronizationContext.Current;
                chat.On<string, string>("broadcastMessage",
                    (name, message) =>
                        Context.Post(delegate
                    {
                        this.chatShow.Text += name + ": "; this.chatShow.Text += message + "\n";
                    }, null)
                        );
                await hubConnection.Start();
                await chat.Invoke("Notify", chatName.Text, hubConnection.ConnectionId);
            }
            catch (Exception ex)
            {

            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            sendMessage();
        }

        async private void sendMessage()
        {
            try
            {
                await chat.Invoke("Send", chatName.Text, txtMessage.Text);
            }
            catch (Exception ex)
            {
            }
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            if (enterName.IsOpen)
            {
                enterName.IsOpen = false;
            }
            makeConnection();
        }
    }
}

Let’s analyze the code behind.

Since I’m going to use my Hub Proxy in more than one places (Page_Loaded method and Button_Click method), I declare it as a Global variable.

IHubProxy chat;

Also, here I’m using a SynchronizationContext to separately handle the updates from UI thread.

I’m going to show a popup as soon as application launches. My popup control’s name is ‘enterName’.

private void Page_Loaded(object sender, RoutedEventArgs e)
{
    if (!enterName.IsOpen)
    {
        border.Width = Window.Current.Bounds.Width;
        border.Height = Window.Current.Bounds.Height;
        enterName.IsOpen = true;
        chatName.Focus(FocusState.Pointer);
    }
}

So on the Page_Loaded method, I’m checking whether it’s already opened by calling enterName.IsOpen. If it returns false, I set the width and height of the popup to window width and height because I like my popup to take the entire screen when it displayed. Then I set the cursor to textbox ‘chatName’ so user can enter the name.

When the user press OK button, I hide the popup and make the connection between the server and Windows Store App. To make the connection, I’m calling the makeConnection() method as shown below.

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    if (enterName.IsOpen)
    {
        enterName.IsOpen = false;
    }
    makeConnection();
}

Following is the implementation of makeConnection() method.

async private void makeConnection()
{
    try
    {
        var hubConnection = new HubConnection("
http://localhost:53748");
        chat = hubConnection.CreateHubProxy("ChatHub");
        chatShow.Text = "";
        Context = SynchronizationContext.Current;
        chat.On<string, string>("broadcastMessage",
            (name, message) =>
                Context.Post(delegate
            {
                this.chatShow.Text += name + ": "; this.chatShow.Text += message + "\n";
            }, null)
                );
        await hubConnection.Start();
        await chat.Invoke("Notify", chatName.Text, hubConnection.ConnectionId);
    }
    catch (Exception ex)
    {

    }
}

Note that I’ve made the method as async. I’m wishing to call some long running processes inside that method. So I need to use the await keyword in front of that method.

I’m creating the hub connection by passing the URL I need my app to connect to. Since I run my web application from Visual Studio, I use the localhost address.

var hubConnection = new HubConnection("http://localhost:53748");

Next I’m using that connection to create a hub proxy. The class name of my server side hub is ‘ChatHub’. So I’m passing that name to create a hub proxy.

chat = hubConnection.CreateHubProxy("ChatHub");

Then I register the ‘broadcastMessage’ method on my app to display the messages I get from the server (The server will actually broadcast the messages get from clients). I’m getting two values, name and message from the server and I display them on my app.

chat.On<string, string>("broadcastMessage",
           (name, message) =>
               Context.Post(delegate
           {
               this.chatShow.Text += name + ": "; this.chatShow.Text += message + "\n";
           }, null)
               );

Then I start the connection between server and my app. I will call a server method at next line. When I go to next line, I need to be sure that connection has been made to the server in order to run the server method. Since I’m using await keyword, the method will return but will not execute the rest of the lines in async method until the the connection has been made.

await hubConnection.Start();

Then I’m calling a server method called ‘Notify’, which will be responsible for notifying other clients that my app is connected to the chat system. For that I’m using the below code

await chat.Invoke("Notify", chatName.Text, hubConnection.ConnectionId);

I’m passing the name I got from the popup and the connection ID which get assigned to my app. There also I’m using await because I need it to be finished first before I start sending the messages from my app.

Now we have successfully made the connection between my app and the server. Now let’s look how we can send messages through my app.

On the Send button click event, I’m calling a method called ‘sendMessage’

private void Button_Click(object sender, RoutedEventArgs e)
{
    sendMessage();
}

The sendMessage method implementation is like below

async private void sendMessage()
{
    try
    {
        await chat.Invoke("Send", chatName.Text, txtMessage.Text);
    }
    catch (Exception ex)
    {
    }
}

This method is also marked as async. I invoke the server side method called “Send” to send the messages. I pass the name and message to that method so the server method can broadcast my message to other connected clients.

That’s all it takes to create a windows store app which connects, display and send messages in real time with a browser client, using SignalR library.

Following is a screenshot of the first screen of my app (Popup)

1

Below is how it interacts with the browser

2

As you can see, it updates the messages in real time both in windows store app and browser!

No comments: