For the toast notifications walkthrough, you will
implement a Windows Phone 7 client application that will create a
notification channel, and a Windows Forms application that can send the
notifications to the Windows Phone 7 application via that channel. The
Windows Phone 7 client application will be a single screen application
with one button and one text box, as shown in Figure 17-4. The Windows Forms application will consist of the single form shown in Figure 17-5. You will follow this order of program implementation:
You
will create the Windows Phone 7 Notification client application. This
application will establish a notification channel and print the URI of
that communication channel into the Output window.
Create
and execute the Windows Forms application that will send notifications.
You will take the URI of the notification channel that you established
in Step 1, paste it into the "Push Notifications URL" field of this
application, and submit the notification.
Verify that you are able to receive toast notifications in the Windows Phone 7 application.
1. Creating a Client Application
The Windows Phone 7
Notification client application will consist of a command button that
will create a push notification channel and print its URI into the Debug
window. You will also add a text box to the application to display the
URI for visual confirmation. Follow these steps to create the
application.
Launch Visual Studio 2010 Express for Windows Phone and create a new Windows Phone Application project. Name it "PNClient."
From
the Toolbox, drag and drop a text box on the design surface. Rename the
text box to txtURI, adjust its width to be the full width of the
screen, and adjust its height to be about a quarter of the screen's
height. Set the text box's TextWrapping property to "Wrap" and clear out
its Text property.
From
the Toolbox, drag and drop a button on the design surface. Rename the
button to btnCreateChannel and set the Content property to "Create
Channel." Your Windows Phone 7 design surface should now look like Figure 1.
Add the following using directives to the top of the MainPage.xaml.cs file.
using Microsoft.Phone.Notification;
using System.Diagnostics;
You
need to add code that will capture the URI of the notification channel
in the Output window. At the top of the code page, right underneath the
public partial class MainPage : PhoneApplicationPage
{
add the following code:
Uri channelUri;
public Uri ChannelUri
{
get { return channelUri; }
set
{
channelUri = value;
OnChannelUriChanged(value);
}
}
private void OnChannelUriChanged(Uri value)
{
Dispatcher.BeginInvoke(() =>
{
txtURI.Text = "changing uri to " + value.ToString();
});
Debug.WriteLine("changing uri to " + value.ToString());
}
The last bit of code above will
print the URI of the push notification channel opened by the
application into the Visual Studio Output window, which will allow you
to copy and paste that URI into the Windows Forms application that you
will be building in the next section. Certainly, in the real-world
application, this copy and paste method is not appropriate; a more
robust method of exchanging that URI, such as passing it to a web
service, would be more appropriate.
Open MainPage.xaml in Design view (right-click MainPage.xaml in Solution Explorer and select View Designer), double-click the "Create Channel" button, and make the btnCreateChannel_Click event handler look like the following:
private void btnCreateChannel_Click(object sender, RoutedEventArgs e)
{
SetupChannel();
}
Paste the following SetupChannel function into the btnCreate_Click event handler . Here, you use the HttpNotificationChannel class to try to find an existing push notification channel or open a new channel with a given channel name.
private void SetupChannel()
{
HttpNotificationChannel httpChannel = null;
string channelName = "DemoChannel";
try
{
//if channel exists, retrieve existing channel
httpChannel = HttpNotificationChannel.Find(channelName);
if (httpChannel != null)
{
//If we cannot get Channel URI, then close the channel and reopen it
if (httpChannel.ChannelUri == null)
{
httpChannel.UnbindToShellToast();
httpChannel.Close();
SetupChannel();
return;
}
else
{
ChannelUri = httpChannel.ChannelUri;
}
BindToShell(httpChannel);
}
else
{
httpChannel = new HttpNotificationChannel(channelName);
httpChannel.ChannelUriUpdated += new
EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated);
httpChannel.ShellToastNotificationReceived+=new
EventHandler<NotificationEventArgs>(httpChannel_ShellToastNotificationReceived);
httpChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(httpChannel_ExceptionOccurred);
httpChannel.Open();
BindToShell(httpChannel);
}
}
catch (Exception ex)
{
Debug.WriteLine("An exception setting up channel " + ex.ToString());
}
}
The code in the SetupChannel()
function warrants an explanation, since there is quite a bit going on
there and this is the nucleus of creating a channel for a Windows Phone 7
Notification client application. In the first few lines, you define a
new object of type HttpNotificationChannel,
give it a name, and wire up an event to fire when an error occurs.
You'll also wire up an event to fire when the URI of the notification
channel changes. Next, you'll try to find the channel with a given name
for the application and then bind it to receive toast notifications (via
the BindToShell function shown
here). If the channel is found, you'll use an existing channel to obtain
the push notification URI and you will not need to wire various
httpChannel event handlers. If the channel is not found, you'll create a
new one and wire up appropriate httpChannel events. Notice the ShellToastNotificationReceived
event—it occurs if your application is running in the foreground when
it receives a toast notification. Normally, push notifications are
designed to alert that there is something happening when the application
is not running and when application tile needs to be updated. However,
occasionally, your application may be running when a toast notification
is received—to handle cases like that, ShellToastNotificationReceived event handler is introduced.
To handle toast notifications when the application is running in the foreground, add the code below for the ShellToastNotificationReceived event handler. This code will read the notification messages received and print them in the textbox on the screen.
void httpChannel_ShellToastNotificationReceived(object sender,
NotificationEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
txtURI.Text = "Toast Notification Message Received: ";
if (e.Collection != null)
{
Dictionary<string, string> collection =
(Dictionary<string, string>)e.Collection;
System.Text.StringBuilder messageBuilder = new System.Text.StringBuilder();
foreach (string elementName in collection.Keys)
{
txtURI.Text+= string.Format("Key: {0}, Value:
{1}\r\n", elementName, collection[elementName]);
}
}
});
}
To bind a toast notification subscription to a given HttpNotificationChannel instance, you must call the BindToShellToast method of the HttpNotificationChannel class. Underneath the SetupChannel function, paste the following code to accomplish that:
private static void BindToShell(HttpNotificationChannel httpChannel)
{
//This is a toast notification
try
{
httpChannel.BindToShellToast();
}
catch (Exception)
{
Debug.WriteLine("An exception occurred binding to shell " + ex.ToString());
}
}
In the SetupChannel function, you designated the httpChannel_ExceptionOccurred should fire in case of an error. Add this function to your code as defined here:
void httpChannel_ExceptionOccurred(object sender, NotificationChannelErrorEventArgs
e)
{
//Display Message on error
Debug.WriteLine ( e.Message);
}
You also need to add code that will fire if the ChannelUri gets updated:
void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
//You get the new Uri (or maybe it's updated)
ChannelUri = e.ChannelUri;
}
At this point, you have
finished building the Windows Phone 7 client application and are ready
to implement the Windows Forms application for sending notification
messages to the mobile device.
2. Creating an Application to Send Notifications
In the previous section, you
wrote a Windows Phone 7 client application that creates a notification
channel to the MPN Service to indicate that it wishes to receive push
notification messages. By creating a channel, the application has also
created an MPNS endpoint to which the web-based application can send
POST requests. The endpoint exists on an MPNS server operated by
Microsoft and will forward any requests it receives to the appropriate
mobile device and application to which the endpoint points.
You can create POST
requests from virtually any application environment, including web
sites, web services, and desktop applications, making this type of
notification architecture very flexible and easy to use. In this
example, you will create a Windows Forms application that packages POST
requests to the URI generated in the previous section. This application
will create POST requests and send them off to the MPNS in the cloud,
which will in turn properly route the requests to the mobile devices and
applications.
To ensure proper
message routing and successful delivery, there are two key pieces of
information that any application sending push notifications to a Windows
Phone 7 device must supply. Those key pieces of information are the
following:
The URI of the notification channel that the service must use to communicate with a Windows Phone 7 device. It is up to the Windows Phone 7 client application to request the URI and pass it to the service that will use it.
A proper XML message to POST to the URI.
While the format of the XML message is simple, it has to be followed
precisely for the notifications to succeed. To make things slightly more
confusing, Microsoft changes schema in its early product releases in an
effort to make it more uniform; those changes could break existing
functionality and make documentation not relevant.
The latest MPNS XML template for toast notifications looks like the following, where <Notification Title> and <Notification Text> are the text of the notification title and the text of the toast notification message to be sent to a Windows Phone 7 device:
<?xml version="1.0" encoding="utf-8"?>
<wp:Notification xmlns:wp="WPNotification">
<wp:Toast>
<wp:Text1><Notification Title></wp:Text1>
<wp:Text2><Notification Text></wp:Text2>
</wp:Toast>
</wp:Notification>
Now that you know the XML
template format of the expected POST request and, using cut and paste,
can quickly obtain the URI of the notification channel, as you'll see
shortly, you're ready to create an application to dispatch notifications
to the Windows Phone 7 client app. Follow these steps to accomplish
that:
Launch
Visual C# 2010 Express (or another edition of Visual Studio that will
allow you to create Windows Forms projects) and create a new Windows
Forms project. Name it "PNServer."
Form1.cs
is added by default. Double-click it to bring up the design view. From
the toolbox, drag three labels and three text boxes and make Form1.cs look like Figure 17-5. Name the text boxes txtURL, txtTitle, and txtText accordingly.
Add a Button control onto Form1. Change its text property to "Send Notification" and change its name to "btnSendNotification."
Finally, add the label control to the bottom of the form and change its name to "lblStatus."
Right-click Form1.cs in Solution Explorer and choose View Code (alternately, you can also press F7). Add the following using statements to the top:
using System.Net;
using System.IO;
After the constructor, add the following definition of the XML to be POSTed to MPNS:
string ToastPushXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>{0}</wp:Text1>" +
"<wp:Text2>{1}</wp:Text2>" +
"</wp:Toast>" +
"</wp:Notification>";
Switch back to the design view on Form1.cs (by right-clicking Form1.cs in Solution Explorer and choosing View Designer). Double-click the "Send Notification" button to bring up the btnSendNotification_Click event handler.
You'll use the btnSendNotification_Click event handler, with the help of the .NET HttpWebRequest
class, to create a POST request, to the push notification URI that the
Windows Phone 7 client has obtained. The beauty of communication with
MPNS is that once this POST request is composed and sent off, MPNS will
take care of the delivery of the notification from there. The critical
piece of information is the URI to send the POST request to, since that
URI is what uniquely identifies both a Windows Phone 7 device and an
application to send push notifications to.
Make the btnSendNotification_Click event handler look like the code here:
private void btnSendNotification_Click(object sender, EventArgs e)
{
if (txtURL.Text == string.Empty)
{
MessageBox.Show("Please enter a url");
return;
}
if (txtTitle.Text == string.Empty || txtText.Text == string.Empty)
{
MessageBox.Show("Please enter text and title to send");
return;
}
string url = txtURL.Text;
HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(url);
sendNotificationRequest.Method = "POST";
sendNotificationRequest.Headers = new WebHeaderCollection();
sendNotificationRequest.ContentType = "text/xml";
sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "toast");
sendNotificationRequest.Headers.Add("X-NotificationClass", "2");
string str = string.Format(ToastPushXML, txtTitle.Text, txtText.Text);
byte[] strBytes = new UTF8Encoding().GetBytes(str);
sendNotificationRequest.ContentLength = strBytes.Length;
using (Stream requestStream = sendNotificationRequest.GetRequestStream())
{
requestStream.Write(strBytes, 0, strBytes.Length);
}
HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse();
string notificationStatus = response.Headers["X-NotificationStatus"];
string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];
lblStatus.Text = "Status: " + notificationStatus + " : " + deviceConnectionStatus;
}
The POST request includes two headers:
The X-WindowsPhone-Target
header defines the notification type. The possible values for this
header are "toast," "token," or not defined. "Toast" defines the
notification of toast type, while "token" defines a tile notification.
If this header is not defined, then it is a raw notification.
The X-NotificationClass
header defines how soon the MPNS should deliver the notification. The
value of "2" specifies that the toast notification is to be delivered
immediately. Had you specified the value of "12," for example, the MPNS
would have been instructed to wait 450 seconds, or seven and a half
minutes before notification delivery.
Now it's time to test the application and its service.
2.1. Verifying Delivery of Push Notifications
With the Windows Phone 7
Notification client application ready to receive notification messages
and the Windows Forms application ready to send them, you are ready to
verify the proper delivery of those notifications. Follow these steps to
test push notification delivery:
First, you will need to obtain the URI of the notification channel. Open the PNClient project created in the "Creating a Client Application" section. Make sure that you have a connection to the Internet, and press F5 to run the project.
Click
the Create Channel button and, after a short while, you should see
messages (the URI of the notification channel, actually) printed in the
text box on the screen—that's a confirmation that the notification URI
is available to copy from the Output window.
In Visual Studio 2010 Express for Windows Phone, click the Debug => Windows =>
Output menu option to bring up the Output window. The URI should be
printed together with the "changing uri to ..." message, as shown in Figure 4.
Highlight the URI and press Ctrl/C to copy it into the buffer. Make
sure to leave the application running in the Windows Phone emulator,
since you will be receiving push notifications on this emulator screen.
Switch
to the PNServer Windows Forms project and press F5 to run it. In the
Push Notifications URL text box, paste the URI obtained in Step 1 by
pressing Ctrl/V. In the Push Notifications Title and Push Notifications
Text text boxes, you can enter any text—for example, "Time to buy!" and
"MSFT up $2 after WP7 release." Press the Send Notification button.
Remember that push
notifications appear on the phone only when the Windows Phone 7
application associated with these notifications is not running in the
foreground on the phone. Therefore, if the PNClient application was
running—which is likely—in the foreground when you pressed the Send
Notification button, no notifications will have appeared on the phone
screen. To enable them, do the following, otherwise skip to the next
paragraph:
Press
the Windows button on the emulator (that's the middle button on the
emulator with the Windows logo on it) to switch to the Start screen of
the phone, which shuts down PNClient application. In the PNServer app,
press the Send Notification button again.
As you can see, creating
and receiving push notifications is a somewhat involved process, with a
separate Windows Phone 7 application establishing a notification channel
and receiving notifications, and a separate application sending, or
"pushing" these notifications to that Windows Phone 7 app. Once you
understand how to work with other types of notifications—namely, tile
and raw—you will learn how to build a service that will programmatically
acquire and keep track of the connected clients. From an application
development point of view, the good news is that the process of creating
other notification types—tile notifications and raw notifications—is
very similar to the process of creating toast notifications that we have
described previously. In the next section, you will take a look at how
to create tile notifications; but instead of creating everything from
scratch, you will concentrate only on the changes needed to the toast
notifications already implemented.