Gaurav Mantri's Personal Blog.

How I Built an “Awesome Chat” application for Windows 8 with Windows Azure Mobile Service

In this blog post, I’m going to talk about how I built a Windows 8 application using Windows Azure Mobile Service. Windows Azure Mobile Service is the newest Windows Azure Services that you can use to provide backend services for your mobile applications (currently Windows 8, more coming soon). You can learn more and sign up for a preview of this service here: http://www.windowsazure.com/en-us/home/scenarios/mobile-services/. More information about this service can be found on Scott Guthrie’s blog post here: http://weblogs.asp.net/scottgu/archive/2012/08/28/announcing-windows-azure-mobile-services.aspx. The service looked really promising and I thought I should give it a try and trust me, you’re not going to be disappointed. All-in-all it took me about 4 hours to build the application (and that included installing Windows 8, VS 2012, signing up for a marketplace account etc.). Since I’m quite excited about this service and the work I was able to accomplish in such a short amount of time, I’m going to dramatize this blog post a bit! I am big fan of TV series 24 so I will use that for dramatization :). Without further ado, let me start!

Timeline

Following takes place between 5:00 PM and 10:00 AM. All events occur in real time :).

No employees were hurt during the implementation of this project 🙂

5:00 PM – Meeting with Boss

Boss: We need a chat application for our company.

Me: Ummm, OK. Let’s get Skype on everybody’s computer.

Boss: No. No Skype.

Me: OK. How about other chat applications?

Boss: No. We need to build our own. It has to be a Windows 8 application.

Me: OK. What else?

Boss: It should scale up to thousands of users.

Me: (Thinking) Dude, we only got 7 people in the office … but whatever.

Me: What else?

Boss: Should be cheap. I don’t want to pony up large moolah upfront.

Me: Understood. What else?

Boss: That should do it for now.

Me: Let me think about it.

5:15 – 5:30 PM – In my thinking chair

I’ve heard of node.js and I think this would be the right choice for this kind of an application. I can then host the application in Windows Azure. The problem is I don’t know node.js (I’m just a front-end developer). How would I write a scalable application. I hear Glenn Block is in China :). May be I can convince my boss to bring him to India for a day or two. He sure can build this app in no time.

5:30 PM – Back in Boss’s office

Me: I think we should use node.js for this application. From what I have read, it would certainly fit the bill.

Boss: OK.

Me: But I don’t know how to program in node.js. Can I get Glenn Block? He’s in China nowadays. We can fly him to India over the weekend.

Boss: You don’t listen, do you? I said, I want it cheap.

Me: How am I supposed to do it? I’m just a front-end developer. I know a bit of XAML and C#. I don’t know how to build massively scalable applications.

Boss: Like I care. And one more thing …

Me: Yeah.

Boss: I need it by tomorrow morning.

Me: Huh!!! and how am I going to do that?

Boss: You don’t listen, do you? I said “Like I care …”

5:45 PM – Back in my thinking chair

Suddenly I had an epiphany: Windows Azure team has just released Windows Azure Mobile Services. May be I should look at that. So I watched some videos, read some blog posts and was convinced that I could use this service to build the application. There were so many things I liked there:

  • Since it’s in preview mode, I don’t pay anything just yet. Once it goes Live, I will pay only for the resources I consume.
  • It’s massively scalable (after all it is backed by Windows Azure). So if I (ever) need to support thousands of employees in my company, I can just scale my service.
  • I really don’t need to do any server side coding. The service provides me with enough hooks to perform CRUD operations needed for my application. And if I do need to do work on the server i can take advantage of the nifty server side scripts feature. It supports Push Notifications which is something I would certainly need for my chat application. It also supports User Authentication which I can use later on.
  • My current need is to build for Windows 8 which the service supports. I’m told that support for other mobile platforms like iOS and Android is coming in near future so I’m covered on that part as well.

All in all it looked like a winning solution to me. With that, I signed up for Windows Azure Mobile Service Preview and got approved in less than 10 minutes. And then I went home and completely forgot about it :).

11:00 PM – Realized I have an application to deliver

Man, I completely forgot that I had an application to deliver next morning. Forget sleep! Time to do some work.

11:00 PM – 11:30 PM – Install, Install, Install

Then I realized that I didn’t have necessary software installed. Luckily I had most of the things downloaded already. I started by installing Windows 8 and then Visual Studio 2012 and finally Windows Azure Mobile Services SDK was done by 11:30 PM.

11:30 PM – 00:30 AM – Build basic application

With the help of excellent tutorials and samples, I was able to build a basic chat application. All the application did at this time was save my messages in the backend database. Didn’t know (really didn’t want to know) how the data was saved but as long as I can see the data in the portal, I was happy.

image

Next I needed to worry about push notifications. Again tutorials to the rescue! After reading the tutorials, I realized I needed to sign up for Windows 8 Developer account first.

00:30 AM – 2:00 AM – Signing, Paying, Paying and more Paying

I spent next one and a half hour figuring how to sign up for Windows 8 Developer account. Man, this process can certainly be improved. They’re not taking individual developer account registration so I ended up signing for a corporate account. Again when signing up for a corporate account, I cannot list my email address as both developer and approver. What if I am a single person shop right now? Anyways, I ended up with that process and managed to get all the things I needed for push notification (namely client secret and package sid). Back to coding again!

2:00 AM – 2:30 AM – Implementing push notification

Again, excellent tutorial and code sample came in handy. I just copied the code and pasted it in proper places and I was getting push notifications in my application. Yay!!!

image

2:30 AM – 3:00 AM – Wrapping it up

Only thing I needed to do now is enable push notification for multiple users so I thought I will take care of it in the morning as I needed somebody to chat with me. Tweeted about the application I built and then got retweeted by none other than @scottgu. My day could not have ended on a better note :).

9:00 AM – 9:30 AM – Implementing push notification for multiple users

Again, excellent tutorial and code sample came in handy. I just copied the code and pasted it in proper places and I was getting push notifications in my application from other users. Couldn’t be happier and badly wanted to show it off to my boss!!!

image

10:00 AM – Outside Boss’s office

Me: Is boss in?

Boss’s assistant: He has gone on a 10 day vacation. Didn’t he tell you that?

Me: WTF!

Anyways, the point I am trying to make here is that it is insanely simple to build mobile applications without really worrying too much about the server side coding using this service.

Technical Details

Enough kidding! Now let’s see some code. Here’s how I built the application:

Prerequisites

I’m assuming that you have already signed up for the preview services and have created the service and the database required for storing data. So I will not explain those.

Create table to store chat data

First thing you would need to create is a table which will store chat data. This is done on the portal itself. One interesting thing here is that even though the database used behind the scenes is SQL Azure (which is SQL Server), you can really have flexible schema. In fact, if you want you can just define the table and not define any columns (when a table is created, a column by the name “id” is created automatically for you). When the data is inserted the first time, the service automatically creates necessary columns for you. For this, make sure that dynamic schema is enabled as shown in the screenshot below:

image

Create model

Next thing you would want to do is create model for the data that you want to move back and forth between the devices. Since we’re building a chat application, we kept the model simple.


    public class ChatMessage
    {
        public int Id
        {
            get;
            set;
        }

        public string From
        {
            get;
            set;
        }

        public string Content
        {
            get;
            set;
        }

        public string DateTime
        {
            get;
            set;
        }

        [DataMember(Name = "channel")]
        public string Channel
        {
            get;
            set;
        }

        public override string ToString()
        {
            return string.Format("[{0}] {1} Says: {2}", DateTime, From, Content);
        }
    }

Create XAML page

Again because our application was really simple, we created a single page application. This is how our XAML file looks like.


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

    <Grid Background="White">

        <Grid.RowDefinitions>
            <RowDefinition Height="70"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Margin="5" FontSize="48" VerticalAlignment="Center" Text="Awesome Chat!"/>
        <Grid Grid.Row="1" x:Name="GridRegister" Visibility="Visible">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition Height="40"/>
                <RowDefinition Height="40"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="1" Margin="5" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" Text="Your name please:"/>
            <Grid Grid.Row="2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="100"/>
                </Grid.ColumnDefinitions>
                <TextBox x:Name="TextBoxName" Margin="5" VerticalAlignment="Center"/>
                <Button Grid.Column="1" x:Name="ButtonLetsGo" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Let's Go!" Click="ButtonLetsGo_Click_1"/>
            </Grid>
        </Grid>
        <Grid Grid.Row="1" x:Name="GridChat" Visibility="Collapsed">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="50"/>
                    <RowDefinition Height="40"/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="75"/>
                    </Grid.ColumnDefinitions>
                    <TextBox Grid.Column="0" Name="TextBoxMessage" Margin="5" MinWidth="100"/>
                    <Button Grid.Column="1" Name="ButtonContent" Content="Send" HorizontalAlignment="Center" Click="ButtonContent_Click_1"/>
                </Grid>
                <TextBlock Grid.Row="1"  Text="Chat Messages:" Margin="5" VerticalAlignment="Center" FontSize="16" FontWeight="Bold"/>
                <TextBox x:Name="TextBoxChatMessages" Grid.Row="2" AcceptsReturn="True" Margin="5" TextWrapping="Wrap"/>
            </Grid>
        </Grid>
    </Grid>
</Page>

Implement CRUD operations in your code

Since we’re doing a simple chat application where all we are doing is inserting the data, we only implemented “insert” functionality. Depending on your application requirements, you may need to implement read / update / delete functionality as well.


        private void ButtonContent_Click_1(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(TextBoxMessage.Text))
            {
                var chatMessage = new ChatMessage()
                {
                    From = name,
                    Content = TextBoxMessage.Text.Trim(),
                    DateTime = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
                    Channel = App.CurrentChannel.Uri
                };
                InsertChatMessage(chatMessage);
            }
            ReadyForNextMessage();
        }

        private async void InsertChatMessage(ChatMessage message)
        {
            TextBoxChatMessages.Text += string.Format("{0}\n", message.ToString());
            await chatTable.InsertAsync(message);
            ReadyForNextMessage();
        }

        private void ReadyForNextMessage()
        {
            TextBoxMessage.Text = "";
            TextBoxMessage.Focus(Windows.UI.Xaml.FocusState.Programmatic);
        }

Implement CRUD operations in server side scripts

Head back to Windows Azure Portal and select the table, and then click on “SCRIPT” tab. From the dropdown, you can select the operation for which you wish to create the script. Windows Azure Portal gives you the basic script to start with and then you can add more functionality if you like.

image

Currently this is how “insert” script of our application look like:

function insert(item, user, request) {
    request.execute({
        success: function() {
            request.respond();
            sendNotifications();
        }
    });


function sendNotifications() {
    var channelTable = tables.getTable('Channel');
    channelTable.read({
        success: function(channels) {
            channels.forEach(function(channel) {
                push.wns.sendToastText04(channel.uri, {
                text1: item.DateTime,
                text2: item.From,
                text3: item.Content,
                }, {
                    success: function(pushResponse) {
                        console.log("Sent push:", pushResponse);
                    }
                });
            });
        }
    });
}
}

Run the application

That’s pretty much it! Really. At this time, your application will be able to insert records into the database. You won’t be able to chat with anybody but that’s what we’ll cover next. To view the data, just click on “BROWSE” tab for your table.

image

Enable push notification

Before we enable push notification, we need to sign up for Windows Store first. You may find this link useful for that purpose: http://www.windowsazure.com/en-us/develop/mobile/tutorials/get-started-with-push-dotnet/. I followed the steps as is and was able to get push notifications for my messages.

Enable push notification for all users

Again, check out this tutorial: http://www.windowsazure.com/en-us/develop/mobile/tutorials/push-notifications-to-users-dotnet/. Follow this to a “T” and your code will work just fine. After this you should be able to receive notifications from other users and other users be able to receive notifications from you as well.

Code

I actually downloaded ToDoSample from the website and then modified the code for that. Here’re the modified files.

App.xaml.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.WindowsAzure.MobileServices;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
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;
using Windows.Networking.PushNotifications;
using System.Globalization;
using System.Collections.ObjectModel;

namespace awesome_chat
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    sealed public partial class App : Application
    {
        // This MobileServiceClient has been configured to communicate with your Mobile Service's url
        // and application key. You're all set to start working with your Mobile Service!
        public static MobileServiceClient MobileService = new MobileServiceClient(
            "https://<yourapplicationname>.azure-mobile.net/",
            "<your application's secret key>"
        );

        public static ObservableCollection<ChatMessage> Messages = null;

        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            this.InitializeComponent();
            this.Suspending += OnSuspending;
            Messages = new ObservableCollection<ChatMessage>();
        }

        public static PushNotificationChannel CurrentChannel { get; private set; }


        private async void AcquirePushChannel()
        {
            CurrentChannel =
                await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();


            IMobileServiceTable<Channel> channelTable = App.MobileService.GetTable<Channel>();
            var channel = new Channel { Uri = CurrentChannel.Uri };
            await channelTable.InsertAsync(channel);

            CurrentChannel.PushNotificationReceived += CurrentChannel_PushNotificationReceived;

        }

        void CurrentChannel_PushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
        {
            lock (this)
            {
                var messageContent = args.ToastNotification.Content;
                var textElements = messageContent.GetElementsByTagName("text");
                var xml = textElements.ElementAt(0).GetXml();
                var msgDateTime = textElements.ElementAt(0).InnerText;
                var msgFrom = textElements.ElementAt(1).InnerText;
                var msgContent = textElements.ElementAt(2).InnerText;
                var chatMessage = new ChatMessage()
                {
                    Content = msgContent,
                    From = msgFrom,
                    DateTime = msgDateTime,
                };
                Messages.Add(chatMessage);
                //args.Cancel = true;
            }
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used when the application is launched to open a specific file, to display
        /// search results, and so forth.
        /// </summary>
        /// <param name="args">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs args)
        {
            AcquirePushChannel();
            // Do not repeat app initialization when already running, just ensure that
            // the window is active
            if (args.PreviousExecutionState == ApplicationExecutionState.Running)
            {
                Window.Current.Activate();
                return;
            }

            if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
            {
                //TODO: Load state from previously suspended application
            }

            // Create a Frame to act navigation context and navigate to the first page
            var rootFrame = new Frame();
            if (!rootFrame.Navigate(typeof(MainPage)))
            {
                throw new Exception("Failed to create initial page");
            }

            // Place the frame in the current Window and ensure that it is active
            Window.Current.Content = rootFrame;
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            //TODO: Save application state and stop any background activity
            deferral.Complete();
        }
    }
}

MainPage.xaml

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

    <Grid Background="White">

        <Grid.RowDefinitions>
            <RowDefinition Height="70"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Margin="5" FontSize="48" VerticalAlignment="Center" Text="Awesome Chat!"/>
        <Grid Grid.Row="1" x:Name="GridRegister" Visibility="Visible">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition Height="40"/>
                <RowDefinition Height="40"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="1" Margin="5" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" Text="Your name please:"/>
            <Grid Grid.Row="2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="100"/>
                </Grid.ColumnDefinitions>
                <TextBox x:Name="TextBoxName" Margin="5" VerticalAlignment="Center"/>
                <Button Grid.Column="1" x:Name="ButtonLetsGo" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Let's Go!" Click="ButtonLetsGo_Click_1"/>
            </Grid>
        </Grid>
        <Grid Grid.Row="1" x:Name="GridChat" Visibility="Collapsed">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="50"/>
                    <RowDefinition Height="40"/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="75"/>
                    </Grid.ColumnDefinitions>
                    <TextBox Grid.Column="0" Name="TextBoxMessage" Margin="5" MinWidth="100"/>
                    <Button Grid.Column="1" Name="ButtonContent" Content="Send" HorizontalAlignment="Center" Click="ButtonContent_Click_1"/>
                </Grid>
                <TextBlock Grid.Row="1"  Text="Chat Messages:" Margin="5" VerticalAlignment="Center" FontSize="16" FontWeight="Bold"/>
                <TextBox x:Name="TextBoxChatMessages" Grid.Row="2" AcceptsReturn="True" Margin="5" TextWrapping="Wrap"/>
            </Grid>
        </Grid>
    </Grid>
</Page>

MainPage.xaml.cs


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using Microsoft.WindowsAzure.MobileServices;
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;
using System.Text;

namespace awesome_chat
{

    public class ChatMessage
    {
        public int Id
        {
            get;
            set;
        }

        public string From
        {
            get;
            set;
        }

        public string Content
        {
            get;
            set;
        }

        public string DateTime
        {
            get;
            set;
        }

        [DataMember(Name = "channel")]
        public string Channel
        {
            get;
            set;
        }

        public override string ToString()
        {
            return string.Format("[{0}] {1} Says: {2}", DateTime, From, Content);
        }
    }

    public class Channel
    {
        public int Id { get; set; }


        [DataMember(Name = "uri")]
        public string Uri { get; set; }


    }

    public sealed partial class MainPage : Page
    {
        private string name = string.Empty;

        private IMobileServiceTable<ChatMessage> chatTable = App.MobileService.GetTable<ChatMessage>();

        StringBuilder messages = null;

        public MainPage()
        {
            this.InitializeComponent();
            App.Messages.CollectionChanged += Messages_CollectionChanged;
            messages = new StringBuilder();
        }

        void Messages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                foreach (var item in e.NewItems)
                {
                    var chatMessage = item as ChatMessage;
                    App.Messages.Remove(chatMessage);
                    if (chatMessage.From != name)
                    {
                        this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                        {
                            TextBoxChatMessages.Text += string.Format("{0}\n", chatMessage.ToString());
                        });
                    }
                }
            }
        }

        
        private void ButtonLetsGo_Click_1(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(TextBoxName.Text))
            {
                name = TextBoxName.Text.Trim();
                GridRegister.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                GridChat.Visibility = Windows.UI.Xaml.Visibility.Visible;
                ReadyForNextMessage();
            }
        }

        private void ButtonContent_Click_1(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(TextBoxMessage.Text))
            {
                var chatMessage = new ChatMessage()
                {
                    From = name,
                    Content = TextBoxMessage.Text.Trim(),
                    DateTime = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
                    Channel = App.CurrentChannel.Uri
                };
                InsertChatMessage(chatMessage);
            }
            ReadyForNextMessage();
        }

        private async void InsertChatMessage(ChatMessage message)
        {
            TextBoxChatMessages.Text += string.Format("{0}\n", message.ToString());
            await chatTable.InsertAsync(message);
            ReadyForNextMessage();
        }

        private void ReadyForNextMessage()
        {
            TextBoxMessage.Text = "";
            TextBoxMessage.Focus(Windows.UI.Xaml.FocusState.Programmatic);
        }
    }
}

Summary

As you saw in this blog post, it is really easy to build an application for Windows 8 which makes use of Windows Azure Mobile Service for performing heavy duty server side stuff. Obviously there are many things which we need to have but remember that at the time of writing this blog, it is still in “Preview” phase. I’m actually quite excited about the possibilities it opens up. For more reading, please visit Windows Azure Website at http://www.windowsazure.com/en-us/develop/mobile/. I hope you’ve found this information useful.

Acknowledgement

My sincere thanks to Glenn Block for being such a sport and playing a cameo in my 24 🙂 and also for the early feedback on the post. It’s much appreciated. I sincerely wish to have you visit India sometime soon.


[This is the latest product I'm working on]