In a previous article I wrote about creating server side SignalR timer events.
As part of my learning of SignalR I wanted to see how easy it would be to create a Universal Windows app consuming these server side events.
So first off I created a new blank Universal app:
Next installed the SignalR NuGet package into both app projects: install-package Microsoft.AspNet.SignalR.Client
In the Windows 8.1 Store app project XAML I added a simple bound TextBlock that will display the server messages:
<Page
x:Class="UpTimeUni.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UpTimeUni"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Viewbox>
<TextBlock Text="{Binding Uptime}">please wait...</TextBlock>
</Viewbox>
</Grid>
</Page>
I also added similar XAML in the Windows Phone 8.1 main page.
In the code behind for both main pages I created a data context from a viewmodel class that is defined in the UpTimeUni.Shared project. This means it will be available from both apps.
public MainPage()
{
this.InitializeComponent();
DataContext = new MainViewModel();
}
Next the viewmodel class looks like the following:
using System;
using System.Diagnostics;
using Windows.UI.Core;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.AspNet.SignalR.Client.Transports;
using PropertyChanged;
namespace UpTimeUni
{
[ImplementPropertyChanged]
internal class MainViewModel
{
private readonly CoreDispatcher _dispatcher;
public MainViewModel()
{
_dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
var connection = new HubConnection("http://internetuptime.azurewebsites.net/signalr");
var hubProxy = connection.CreateHubProxy("UptimeHub");
hubProxy.On<string>("internetUpTime", UpdateTime);
connection.Start();
}
public string Uptime { get; set; }
private async void UpdateTime(string s)
{
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => Uptime = s);
}
}
}
Here I’m using Fody to automatically implement all the INotifyPropertyChanged boilerplate code, hence the [ImplementPropertyChanged] attribute. This relies on installing the PropertyChanged.Fody NuGet package into both the app projects.
Now running the Windows Store app we can see the app updates with the Internet uptimes every second.
So next I changed the startup project to the phone app and running showed just a blank screen.
At first I thought I had messed up the XAML binding but this was fine. I put a breakpoint in the viewmodel’s UpdateTime method but it never got hit, it was never getting called from SignalR.
I added some SignalR tracing code (based on this) to the viewmodel constructor to see what was going on:
public MainViewModel()
{
_dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
var connection = new HubConnection("http://internetuptime.azurewebsites.net/signalr");
// Add some tracing help
connection.TraceLevel = TraceLevels.All;
connection.Closed += () => Debug.WriteLine("[Closed]");
connection.ConnectionSlow += () => Debug.WriteLine("[ConnectionSlow]");
connection.Error += (error) => Debug.WriteLine("[Error] {0}", error.ToString());
connection.Received += (payload) => Debug.WriteLine("[Received] {0}", payload);
connection.Reconnected += () => Debug.WriteLine("[Reconnected]");
connection.Reconnecting += () => Debug.WriteLine("[Reconnecting]");
connection.StateChanged +=
(change) => Debug.WriteLine("[StateChanged] {0} {1}", change.OldState, change.NewState);
var hubProxy = connection.CreateHubProxy("UptimeHub");
hubProxy.On<string>("internetUpTime", UpdateTime);
connection.Start();
}
And now running the phone app I could see debug messages such as:
[StateChanged] Disconnected Connecting
[StateChanged] Connecting Connected
[ConnectionSlow]
[StateChanged] Connected Reconnecting
[Reconnecting]
[StateChanged] Reconnecting Connected
[Reconnected]
After some searching online I found a Stack Overflow comment that suggested you need to configure SignalR to use long polling when running in the emulator. I don’t have a Windows Phone 8.1 device to check this on outside of the emulator at present.
So I added the following code to configure the phone app to use long polling:
#if WINDOWS_PHONE_APP
// Doesn't work (just?) in emulator with default
connection.Start(new LongPollingTransport());
#else
connection.Start();
#endif
Running the phone app now works as expected.
Once I update my phone to 8.1 I can see if this behaviour is just an emulator quirk.
If you strip away all the additional stuff, it’s effectively just 4 lines of code to receive SignalR messages, which is pretty amazing.
SHARE: