This article is compatible with the latest version of Silverlight for Windows Phone 7.
This article is Part 4 of the series WP7 Stock Quote Demo.
Now we have shown the development of the UX for the quoting application however the real value in this is the setting of alerts that specifies when the user wants to be notified of a change in the value of a stock. Microsoft Push Notification (MPN) is used to setup a push notification. Thus Silverlight is only one part of the development of a robust phone application as we must also be proficient in writing .Net services. Some argue that most phone applications will be Cloud application but I think that is little naive as there will always be some suited to phone only code such as a game. As this diagram below shows the phone client will setup the MPN service and send the alert data to our Quote service . At this point the phone application is still polling Yahoo for data but then as soon as the user closes the phone application by touching the Back button our phone application is terminated and the server version takes over. Overall the solution looks like:
The Notification channel is setup: Diagram 2
I will go through the implementation of this solution that adds code to the client and build the Quote Server. Let’s start with the server side where the idea here is to develop a WCF service that will host the same Yahoo retrieval code that we placed into the Phone UX. This is pretty interesting when you consider that I was able to move this Silverlight C# code almost without change to my server. This is the real power of the Microsoft solution as we have the CLR everywhere and it lets the same skills be used on client/phone and server! I love it. I base my service on the Microsoft sample found http://windowsteamblog.com/windows_phone/b/wpdev/archive/2010/05/04/understanding-how-microsoft-push-notification-works-part-2.aspx . This is WPF application that is being used to host my WCF service and this is great for a demo application like this I normally would not employ this method as I would build a IIS hosted Restful service but that is the subject of a later article.To start I setup my ServiceContract that looks like:
public interface IQuotePushService
{
[OperationContract,WebGet]
void SetQuoteInfo(string info);
[OperationContract,WebGet]
void Register(string uri);
[OperationContract, WebGet]
void Unregister(string uri);
}
The WebGet attribute is making this a restful WCF method so that we are able to make calls from the phone that look like:
http://localhost:8000/QuotePushService/Register?uri=foo.com
there is a good article on Restful WCF at, but in summary we are mapping the uri to methods on the service e.g. in this case the Register method is being called with a Uri parameter.
The SetQuoteInfo will allow the phone app to sent configuration info to the server to tell it what alerts are assigned to a given stock. Thus the server app has all the information needed to test for thresholds being exceeded.
public void SetQuoteInfo(string infoXML)
{
XDocument xmlDoc = XDocument.Parse(infoXML);
List<Quote> quotesList = (from Quote1 in xmlDoc.Descendants("Quote")
select new Quote
{
Symbol = Quote1.Element("Symbol").Value,
CompareGT = Quote1.Element("GTorLT").Value,
TargetPrice = Convert.ToDouble(Quote1.Element("Alert").Value)
}).ToList();
OnQuoteAlertRceived(quotesList.First());
}
Tis method will accept as input an XML string describing the stock symbol that we want to monitor, it’s target price and a comparison operator that I am only supporting < and > right now. I then use a little Linq to XML to hydrate a list of quotes.
Now keep in mind that this is WCF service is being hosted in a WPF application, something that I would not do for a real service but that is the subject of another tutorial. As such I fire an event to the UI thread consistent with the behavior of the demo application by calling the eventhandler, QuoteAlertReceived. This results in the QuotePush_QuoteAlertReceived handler being called that starts the background thread to retrieve the quote info almost exactly like we did in the client application. So I added the StreamStockInfo class to the WPF server project. I had to make a minor change to pass the UI thread dispatcher to the timer as follows:
t = new Timer(new TimerCallback(GetData), Dispatcher.CurrentDispatcher, 0, 3000);
I then added the same DataAccess project without change so now I have my the quoting engine on the server side. Of course I had to add MVVMLight to my WPF project for my inter-thread communications. I then created almost the same delegate to handle the received stock quotes to the constructor for MainWindow.cs that I used in the phone app:
Messenger.Default.Register<List<QuoteDTO>>(this, true,
q =>
{
q.ForEach(dto =>
{
quotesList.ToList().ForEach(model =>
{
if (dto.Symbol.Contains(model.Symbol.ToUpper()))
{
model.Time = dto.Time;
model.Trade = dto.Trade;
model.Volume = dto.Volume;
model.Ask = dto.Ask;
model.Bid = dto.Bid;
model.DayHi = dto.DayHi;
model.DayLow = dto.DayLow;
model.YrHi = dto.YrHi;
model.YrLow = dto.YrLow;
List<Uri> SubscribersForAlert = new List<Uri>();
//the quote may have multiple subsribers with different criteria
model.alertInfo.ForEach(a =>
{
bSendNotification = false;
//only send alert 5 times
if (a.numberFired <= 5000)
{
if (a.Comparer == "true")
{
if (dto.Trade > a.Threshold)
SubscribersForAlert.Add(new Uri(a.PhoneID, UriKind.Absolute));
}
else
{
if (dto.Trade < a.Threshold)
SubscribersForAlert.Add(new Uri(a.PhoneID, UriKind.Absolute));
}
a.numberFired++;
}
});
if (SubscribersForAlert.Count > 0)
{
ThreadPool.QueueUserWorkItem((unused) => notifier.SendTileNotification(SubscribersForAlert, "PushNotificationsToken", "snow.png", 2, model.Symbol, OnMessageSent));
}
}
});
});
}
);
However a Quote might have multiple subscribers and this code will look for this condition and fire 10 notifications to the phone if the condition persists.The target phone must have sent a Register command to our server which I arrange to do at the startup of the phone app.The Register method that is event labeled (4) in diagram 2.and is passed the Uri to send the push to and we just record this in a list of subscribers which will be used later to sent Alerts to.
The SendTileNotification method is contained in a PushNotificationManager.cs that the MS sample contains. This will create a formatted XML string for the Tile message (it also contains the logic for Toast). This XML looks like :
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<wp:Notification xmlns:wp=\"WPNotification\">
<wp:Tile>
<wp:BackgroundImage>{0}</wp:BackgroundImage>
<wp:Count>{1}</wp:Count>
<wp:Title>{2}</wp:Title>
</wp:Tile>
</wp:Notification>
There are 3 parameters that are filled in by the code:
1. Image for the tile background
2. A count of the alerts send
3. A description like the symbol name
The method then encodes the XML and call SendMessage that builds an Http Post payload to deliver to the MPN server. This code looks like:
//Create and initialize the request object
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(channelUri);
request.Method = WebRequestMethods.Http.Post;
request.ContentType = "text/xml; charset=utf-8";
request.ContentLength = payload.Length;
if (notificationType == NotificationType.Toast)
{
request.Headers.Add(notificationClassHeader, "2");
request.Headers.Add(targetHeader, "toast");
}
else if (notificationType == NotificationType.Tile)
{
request.Headers.Add(notificationClassHeader, "1");
request.Headers.Add(targetHeader, "token");
}
request.Headers.Add(messageIdHeader, Guid.NewGuid().ToString());
request.BeginGetRequestStream((ar) =>
{
//Once async call returns get the Stream object
Stream requestStream = request.EndGetRequestStream(ar);
//and start to write the payload to the stream asynchronously
requestStream.BeginWrite(payload, 0, payload.Length, (iar) =>
{
//When the writing is done, close the stream
requestStream.EndWrite(iar);
requestStream.Close();
//and switch to receiving the response from MPNS
request.BeginGetResponse((iarr) =>
{
using (WebResponse response = request.EndGetResponse(iarr))
{
//Notify the caller with the MPNS results
OnNotified(notificationType, (HttpWebResponse)response, callback);
}
},
null);
},
null);
},
null);
}
It sets 2 headers:
1. X-NotificationClass which is set to 1
2. X_WindowsPhone-Target which is set to toast
It then writes the XML into the requestStream and sets up a delegate to handle the response. This delegate will just display the message on the UI thread. As a result the alert is posted to the MPN server which is step (5) in diagram 2 then forwarded to the phone. We will discuss the Phone side in Part 5.
John McFetridge
jmcfet@bellsouth.net
Architect, Developer and Trainer for Sandkey Software which specializes in Silverlight and WP7 development