Sending toast notifications on Windows Phone 7
Join the DZone community and get the full member experience.
Join For FreeWindows Phone 7 supports push notifications – the kind of notifications that are pushed to the device from a web service when a new event, connected to the application occurs and the end-user should be notified about it. For example, if it is an email client, it could be a notification about a new message that was downloaded to the inbox. Clicking on the notification will let the user open the application and check what’s going on.
In this article I will show how to create an application for Windows Phone 7 as well as a service emulator that will send the notification.
The Windows Phone 7 Application
First of all, create a simple Windows Phone 7 application.
I removed all UI content from the main page and left it blank – here I won’t be working with UI and no UI hooks will be present to trigger any specific action. Therefore, the XAML part of my page is pretty simple:
<phone:PhoneApplicationPage
x:Class="NotificationTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
shell:SystemTray.IsVisible="True">
</phone:PhoneApplicationPage>
Then again, this is not the case of a functional application. Now to the functional part.
Push notifications aren’t directly delivered to the phone from a web service, but rather through a ‘proxy’ – the Microsoft Push Notification Service (MPNS). To accept notifications from MPNS, the application must have a channel set up, that would deliver notifications that are bound to it. Each channel has its own unique URI, and this URI is assigned to you by Microsoft.
So the first thing I am going to do is set up a channel – declare a unique instance of HttpNotificationChannel that is a member of the Microsoft.Phone.Notification namespace. Here is what I do during the application initialization:
HttpNotificationChannel channel = HttpNotificationChannel.Find("NotificationTest_WP7");
if (channel == null)
{
channel = new HttpNotificationChannel("NotificationTest_WP7");
channel.Open();
}
channel.BindToShellToast();
channel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(channel_ChannelUriUpdated);
channel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(channel_ErrorOccurred);
Debug.WriteLine(channel.ChannelUri);
NOTE: This code is placed inside the App() constructor in App.xaml.cs.
I
am opening the channel and explicitly showing that it will be used for
toast notifications. As you can see here, I am referencing two
additional event handlers – ChannelUriUpdated and ErrorOccured.
These event handlers will display a message in the output window,
depending on the outcome of the notification channel initialization.
void channel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e)
{
Debug.WriteLine("An error occured while initializing the notification channel");
Debug.WriteLine(e.Message);
}
void channel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
Debug.WriteLine(e.ChannelUri);
}
The ChannelUriUpdated method is complimentary, but I am using it to find out the URI Micosoft will assign for my application, so I can send notifications to that specific location.
For now, you are pretty much done with the mobile application. Run it and take a peek at the Output dialog. You should see the new assigned URI for the channel, since this is the first time you ever use this application with the specified channel.
Here is what you should get (the URI should be similar to the one shown in the image below):
So you have the channel URI. Now let’s design a WCF service application that will be sending data to the above mentioned channel.
The Service Application
Before creating the application, if you are using Windows 7 or Windows Vista, make sure you launch Visual Studio 2010 as an administrator. Because the application I’m working on will be the host for a service, it will deal with system components that require administrative privileges.
Once started, create a console application. That will be enough to host a simple WCF service.
By default, the application is targeting the .NET Framework 4.0 Client Profile. Since I will be working with WCF, I need to change the target framework to .NET Framework 4.0, in order to access the System.ServiceModel.Web namespace.
Add a reference to System.ServiceModel and System.ServiceModel.Web after you change the target framework. Also, don’t forget about adding using System.ServiceModel and using System.ServiceModel.Web to the class header.
Now, create an interface that will define one base method, that the service will have, that will trigger the notification.
[ServiceContract]
interface IService
{
[OperationContract]
[WebGet]
string SendNotification(string message);
}
The actual class that implements the interface above is this:
public class WCFService : IService
{
public string SendNotification(string message)
{
string channelURI = "PUT_YOUR_CHANNEL_URI_HERE";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(channelURI);
request.Method = "POST";
request.ContentType = "text/xml";
request.Headers.Add("X-NotificationClass", "2");
request.Headers.Add("X-WindowsPhone-Target", "toast");
string notificationData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>WP7 TOAST</wp:Text1>" +
"<wp:Text2>"+ message + "</wp:Text2>" +
"</wp:Toast>" +
"</wp:Notification>";
byte[] contents = Encoding.Default.GetBytes(notificationData);
request.ContentLength = contents.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(contents, 0, contents.Length);
}
string notificationStatus;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
notificationStatus = response.Headers["X-NotificationStatus"];
}
return notificationStatus;
}
}
It basically creates a HTTP request to the channel to pass the notification data, that is structured in a XML envelope described here. The headers that are added to the request set the notification type. Once the request is executed, I am returning the notification status, to check whether it was sent or not.
Now in the main application body, I would need to somehow host the service, that will execute the SendNotification method. To do this, I am using an instance of WebServiceHost to specify the location and ServiceEndpoint (member of System.ServiceModel.Description) to specify the endpoint, where the service can be accessed.
WebServiceHost host = new WebServiceHost(typeof(WCFService), new Uri("http://localhost:1234"));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), "");
host.Open();
The port selected by me is completely random, so you can specify a different one, as long as it is accessible.
What
I want to do here is be able to send notifications as long as the user
won’t enter the exit command. Therefore, I am going to use a while loop:
string message = Console.ReadLine();
while (message != "exit")
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:1234/SendNotification?message=" + message.Trim());
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
Console.WriteLine(reader.ReadToEnd());
}
}
Console.ReadLine();
}
host.Close();
Environment.Exit(0);
The
message is passed to the WCF service via the message parameter, that
was defined in the defining interface. By sending a request to the
service and by calling the SendNotification method, I am able to pass the message to the MPNS, and that will pass it to the device with the registered channel.
NOTE: To see the notification, the application that has the proper registered channel must not be active.
Here is what you should get at the end:
A
common recommendation though, would be not to overuse toast
notifications and only pass them when an important event connected to
the application occurs, since those are a distraction and eventually a
user might become annoyed by them (in case those are passed to often)
and block them.
NOTE: You can also call the WCF service from your web browser and the result will be the same.
Opinions expressed by DZone contributors are their own.
Comments