This post details features used by Earthquake Explorer, a Windows 8 app that displays earthquake information on Bing maps. Earthquake Explorer was based off of the Earthquakes mashup starter kit on github. Creating a mashup is pretty easy in Windows 8 – creating a great one requires a bit more work. In this case, I'm working with the USGS earthquake GeoJSON feeds, a very easy-to-use feed that contains earthquake information for either the past hour, day, week, or month. Overall, it’s a pretty ideal feed. But, if you search for and install virtually any earthquake app in the store, you’ll see the limitations quickly. While some apps allow some lightweight filtering of the data, there really isn’t a concept of search, pagination, and server-side filtering. To solve these limitations, we’d typically need to build out a fairly robust back-end server; fortunately, Windows Azure Mobile Services makes this really easy so let’s get started. First thing you need is a Windows Azure account (here’s a link to get a free trial). To get the pricing stuff out of the way first: Windows Azure Mobile Services currently offers a free tier (even beyond your trial), however, quite likely you’ll want to use a database behind the scenes, and that starts at $4.95/mo. EDIT: Good news! SQL Databases now offers a free tier for up to 20MB databases. Multiple Mobile Services can share the same database as they are all schematized. With your account ready to go, log in to the Windows Azure portal and select Mobile Services in the left nav. Next, click the New button in the bottom left to create a new Mobile Service (which I’ll just refer to as WAMS). The New flyouts are context aware, so it should be ready to create a new WAMS but, just to verify, you should see something like this: After a few moments your service is set up and ready to go. You will need to create a new SQL Database if you don’t already have one. The default page of your WAMS should look like this, and it’s full of great info for creating new or leveraging WAMS in existing apps: Let’s click on the data link highlighted above to create a table to store our data. We’ll call it ‘earthquake’, and we’ll also make it so only scripts and admins can modify the table, all users can read the table, like so: By default, WAMS is configured with dynamic schema, which means we can start inserting right away and the columns will be added dynamically to fit the objects we pass in. This is perfect for development. The next thing we’ll want to do is go over to the scheduled jobs page: After clicking create a scheduled job, give it a name like ‘fetchquakes’ and leave it run on demand only. The job will be created and if you are looking at the details of the job by clicking on it, you’ll see a fairly empty screen – we don’t care about the configuration and schedule right now, we simply want to add a script that runs as part of our job. Click the script button: Let’s start with a simple script. Ideally we’ll go out to the service and use live data, but this will get us started: function fetchquakes() {
var eq = {place: "Los Angeles", mag: 5.3, timestamp: new Date()};
var quakesTable = tables.getTable('earthquake');
quakesTable.insert(eq);
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
If we run this script (by clicking the Run Once button near the bottom/middle of the screen) and then navigate over to our table in the Data tab, we’ll see the row in the table:
The nice part about this, too, is that it did a good job of figuring out the column types:
However, we could go into the database itself and change the column definition if we wanted to. We’ll wrap up the post here to keep this from becoming a book, but in the next post we’ll dive a bit deeper into consuming the live feed and storing that in the database.
I’m really excited to be hosting a few events in the Microsoft Raleigh office focused on Windows 8 development. The first on revolves around our APIMASH starter kits – a great way to get started building mashup style applications in Windows 8, with templates and examples in both C# and HTML/JS. The other sessions are intro to gaming, developing some simple games using Construct2, and GameMaker/other frameworks as time allows. Here are the events/times: APIMASH: Tue 6/4/2013 from 10:00am to 2:00pm and Tue 6/11/2013 from 10:00am to 2:00pm Intro to Gaming: Wed 6/5/2013 from 10:00am to 2:00pm and Wed 6/11/2013 from 10:00am to 2:00pm Here’s a more official description of each event: Game Development for Beginners In this beginner level workshop we will cover the basics of game design, programming and publication. We will build a casual game and publish it to the Windows Store. This workshop is great for students, hobbyists and professional developers who want to learn the basics of game development and publish their first app to the store as no programming skills are required! Windows 8 App Mashup Series In this workshop you will learn how to develop Windows 8 apps based on well-known web service API's such as Twitter, Meetup, ESPN, EchoNest and data from the World Health Organization WHO. Your app could entertain or even change the world. This workshop is great for students, hobbyists and professional developers who want to learn the basics of app development and publish their first app to the store. For more info, stay tuned to the MSDN Events page!
Abstract: In Part 5 of of their “Using Windows Azure to Build Back-End Services for Windows 8 apps” series Peter Laudati, Brian Hitney and Andrew Duthie show us how to quickly add the ability to implement push notifications for his GameLeader Service using Azure Mobile Services. Check out the full article here. Watch Part 1 | Part 2 | Part 3 | Part 4 After watching this video, follow these next steps: Step #1 – Try Windows Azure: No cost. No obligation. 90-Day FREE trial. Step #2 – Download the Tools for Windows 8 App Development Step #3 – Start building your own Apps for Windows 8 Subscribe to our podcast via iTunes or RSS If you're interested in learning more about the products or solutions discussed in this episode, click on any of the below links for free, in-depth information: Register for our Windows Azure Hands-on Lab Online (HOLO) events today! Windows Azure Hands-on Labs Online Blogs: Brian Hitney’s blog Peter Laudati’s blog Andrew Duthie’s Blog Videos: Microsoft DevRadio: How to Get Started with Windows Azure Microsoft DevRadio: (Part 1) What is Windows Azure Web Sites? Microsoft DevRadio: (Part 2) Windows Azure Web Sites Explained Microsoft DevRadio: How to Integrate TFS Projects with Windows Azure Web Sites Virtual Labs: MSDN Virtual Labs: Windows Azure Download MP3 (Audio only) MP4 (iPod, Zune HD) High Quality MP4 (iPad, PC) Mid Quality MP4 (WP7, HTML5) High Quality WMV (PC, Xbox, MCE)
I’ve gotten a few requests about how to do a mashup in Dark Skies … so, thought I’d do a blog post on how it was put together.
First, what’s a mashup? A mashup is an application that combines data from multiple sources into what is, hopefully, a more useful or interesting way to view the data. They are often done as web applications because the data is often pulled from online sources. But, mashups are ideal for Windows 8 and Windows Phone apps, too.
Dark Skies is simply a mashup that combines 3 main sources of data. maps (from Bing), light pollution data (from sources like these: Save The Night, NOAA, and P. Cinzano), and favorite astronomy viewing locations and looks like so:
What makes this interesting is the level of zooming, so you get really specific:
Specific is good, but add in data sharing and you, hopefully, have an ideal mashup experience.
Overlaying pins is easy as the map. In fact, there’s a small sample on putting pins on a map here. The harder part is storing the data, and for this, Windows Azure Mobile Services works really well. I’ve talked about that in previous posts.
The light pollution data is available from a variety of sources but in this case, a high res version with a color that can be made transparent (black) is ideal.
Credit: P. Cinzano, F. Falchi (University of Padova), C. D. Elvidge (NOAA National Geophysical Data Center, Boulder). Copyright Royal Astronomical Society. Reproduced from the Monthly Notices of the RAS by permission of Blackwell Science.
There are two fundamental problems in overlaying the two: first, the image should be sliced into small tiles to make it bandwidth sensitive (the uncompressed TIF file is 200MB). The second is that the image doesn’t exactly line up. It’s close, but there are subtle errors that get introduced despite both maps appearing to be Mercator projections.
There are a number of tools that can help you solve this problem, but in this case, I used MapCruncher. It’s from Microsoft, and it hasn’t been updated since 2007, but … it works.
The idea is that MapCruncher can both transform/skew an image to fit the projection of the map, and carve it up into nice, little tiles. Let’s take the following image:
If I want to overlay this image at a specific point on the map. Let’s say that the two eyes should be where Lake Superior and Lake Huron are. In MapCruncher, we’d bring in the image as an asset, and start defining matching points in the image, like so:
It’s not until we hit the lock button near the bottom left that the image is scaled/skewed/transformed to fit. The closer an image is to being the same scale, the better/few points are required to get it look right. Once you hit the Render button in the bottom left, it will go to town and slice and dice the image up, based on the zoom depth requested. More on that in a second.
In this case, it generated some 92 tiles. When we preview the results, we can see the data is overlaid nicely:
The tiles produced are laid out and named format known as quadkeys. You can read a lot more about it here, but in short, each tile is scaled to fit the appropriate dimensions based on the current zoom level:
The pattern to this approach makes it very easy to know exactly what tile you need, and at what zoom level (detail). Additionally, all of these files can be stored in a flat data structure (like a folder) which makes storage quite simple. The best part is, Bing maps already knows how to build a quad key, so all you have to do is create a tile layer, and specify the quadkey as a parameter by putting it in curly braces, like so:
MapTileLayer pollutionTileLayer = new MapTileLayer();
pollutionTileLayer.TileSource = "myurl.com/{quadley}.png";
But, this isn’t quite ideal. One problem that I ran into is that if there are a large number of quad keys you don’t have (which is the case even in my app), there are a HUGE number of 404’s because the app has no way of knowing there isn’t a tile available for a specific quad key. While the user doesn’t directly see this, it’s a lot of wasted traffic and just not clean. The way to solve that is to roll your own GetTileUri handler.
In my next post, I’ll detail the steps involved in setting that up!
Abstract: Peter Laudati, Brian Hitney and Andrew Duthie are back for part 2 of their series and in today’s episode Andrew shows us how to deploy the OData Service for his Windows 8 app to Windows Azure as well as outlines the advantages and disadvantages to building back-end services via this approach. After watching this video, follow these next steps: Step #1 – Try Windows Azure: No cost. No obligation. 90-Day FREE trial. Step #2 – Download the Tools for Windows 8 App Development Step #3 – Start building your own Apps for Windows 8 Subscribe to our podcast via iTunes or RSS If you're interested in learning more about the products or solutions discussed in this episode, click on any of the below links for free, in-depth information: Register for our Windows Azure Hands-on Lab Online (HOLO) events today! Windows Azure Hands-on Labs Online Blogs: Brian Hitney’s blog Peter Laudati’s blog Andrew Duthie’s Blog Videos: Microsoft DevRadio: How to Get Started with Windows Azure Microsoft DevRadio: (Part 1) What is Windows Azure Web Sites? Microsoft DevRadio: (Part 2) Windows Azure Web Sites Explained Microsoft DevRadio: How to Integrate TFS Projects with Windows Azure Web Sites Virtual Labs: MSDN Virtual Labs: Windows Azure Download MP3 (Audio only) MP4 (iPod, Zune HD) High Quality MP4 (iPad, PC) Mid Quality MP4 (WP7, HTML5) High Quality WMV (PC, Xbox, MCE)
Recently, a colleague of mine, Adam Hoffman, posted a great tip on serializing a generic List<t> to a Windows 8 App’s RoamingSettings. As Adam points out, taking advantage of roaming storage is so easy to do, it’s criminal not to take advantage of it. There are three basic types of data: local, roaming, and temporary. The APIs are all used similarly, and more info on them can be found here.
There are a few things to keep in mind when using roaming data. One is: the data might change after the app is running – perhaps the data hadn’t been synchronized from another machine yet, or the app is concurrently being used elsewhere. You can plug into the DataChanged event handler to be notified when the roaming data for your app changes – and if it does change, decide what you want to do. You might choose to refresh the settings, or allow the user to decide if they’d like to use these new settings.
The biggest thing to be keep in mind, however, is the RoamingStorageQuota. By design, roaming storage was intended to be for preferences or small amounts of data. At present, the quota is set to a paltry 100KB – although that seems unnecessarily small, if you have hundreds of apps taking advantage of this feature, I can see where bandwidth conversation might come into play.
Accessing the RoamingStorageQuota returns a ulong of the current quota in KB:
ulong quota = ApplicationData.Current.RoamingStorageQuota;
Perhaps at some point the 100KB number will be increased. If your app exceeds that number, the data will be persisted locally, however will not roam. This can be a difficult bug to track down, particularly if you’re roaming important data. Also, when running on a developer machine, the RoamingStorageQuota returns 0 – presumably, because the data can’t roam as it’s not a store-based app (yet). I’ve asked some people internally to confirm this, but in short, means you’ll have to anticipate the value coming back – I’m assuming that return value is 100.
Building off of Adam’s example, we could do something like:
private static string SerializeToString(object obj)
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, obj);
return writer.ToString();
}
}
public void Save()
{
ApplicationDataContainer settingsRoaming =
ApplicationData.Current.RoamingSettings;
this.LastModified = DateTime.Now;
string serializedData = SerializeToString(this);
if ((ulong)UnicodeEncoding.Unicode.GetByteCount(serializedData) <=
ApplicationData.Current.RoamingStorageQuota*1024)
{
settingsRoaming.Values["appSettings"] = serializedData;
}
else
{
//handle the situation
}
}
I realized after writing this (and using this logic myself) that it wouldn’t be a good idea: a single setting can be at most 8KB, while a composite value can be up to 64KB in size. A composite setting represents an atomic unit, and can store much more information obviously. They can be used as follows:
// Composite setting
Windows.Storage.ApplicationDataCompositeValue composite =
new Windows.Storage.ApplicationDataCompositeValue();
composite["intVal"] = 1;
composite["strVal"] = "string";
roamingSettings.Values["exampleCompositeSetting"] = composite;
.
Now, I’m making an assumption that the data is stored in Unicode, 2 bytes per character. I could be wrong. Also, I’m uncertain of the actual size of each element in the settings – a DateTime is typically 8 bytes but I’m uncertain if it gets serialized and consumes only 8 bytes of roaming storage.
In addition to all this, roaming data also can contain folders and files. While obviously limited in size, this may be preferred depending on the type of data your app is using.
The bottom line is: be cautious if trying to roam user-generated data instead of predictable application settings, and consider serializing them as a JSON object graph to a file, to conserve space (XML-based serialization is verbose). For other thoughts on this, see another one of my colleague’s posts on the subject: Windows 8 Games and Roaming Data.
A lot of apps require internet connectivity to function – like my app, Dark Skies. One thing I overlooked when first releasing Dark Skies was a graceful check for network connectivity. If the app launches and there is no connectivity, it should fail gracefully and notify the user. Ideally, we should also plug into the Network Status Changed event handler, so our app is notified when the internet goes up or down during the app’s session. There are a couple of ways to accomplish this. If you’re checking for internet connectivity occasionally, you can do something like this: public static bool IsNetworkAvailable()
{
try
{
ConnectionProfile profile = NetworkInformation.GetInternetConnectionProfile();
if (profile == null)
{
return false;
}
else
{
var networkAdapterInfo = profile.NetworkAdapter;
if (networkAdapterInfo == null)
{
return false;
}
else
{
return true;
}
}
}
catch (Exception e)
{
#if DEBUG
throw;
#endif
return false;
}
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
If you’re checking for connectivity frequently, you’ll likely want to cache that result in a variable, and simply change the cached status when the network status changes. Wiring up the event is pretty simple:
NetworkInformation.NetworkStatusChanged += NetworkInformation_NetworkStatusChanged;
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
And then the event handler can do whatever you’d like:
private async void NetworkInformation_NetworkStatusChanged(object sender)
{
// ...
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
When the app loads, we’ll display a notification of some kind that internet connectivity is required:
Because the nature of tablets and internet connectivity, the state of the connection may change frequently. Having a simple way to check for network connectivity and handling changes to the network status is a great way to ensure a smooth user experience!
Oh – it’s also a good idea to check before doing anything that might throw a network-based exception. For example, on app load, I made the mistake of always acquiring a push notification channel:
await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
If the network is unavailable, this will throw an exception. So, be sure to check for network connectivity and/or handle exceptions when setting up push notifications:
private async void AcquirePushChannel()
{
try
{
if (IsNetworkAvailable())
{
CurrentChannel =
await PushNotificationChannelManager.
CreatePushNotificationChannelForApplicationAsync();
}
}
catch (Exception ex)
{
#if DEBUG
throw;
#endif
}
}
Never too late to post! Here’s an episode of DevRadio Peter and I did on TFS Projects in Azure! Abstract: Peter Laudati and Brian Hitney are back for today’s show as they show us how we can integrate TFS (Team Foundation Server) projects with Windows Azure Web Sites. They also discuss Windows Azure’s latest price reduction for Storage as well as tee up new features in Windows Azure Mobile Services. After watching this video, follow these next steps: Step #1 – Try Windows Azure: No cost. No obligation. 90-Day FREE trial. Step #2 – Download the Tools for Windows 8 App Development Step #3 – Start building your own Apps for Windows 8 Subscribe to our podcast via iTunes or RSS If you're interested in learning more about the products or solutions discussed in this episode, click on any of the below links for free, in-depth information: Register for our Windows Azure Hands-on Lab Online (HOLO) events today! Windows Azure Hands-on Labs Online Blogs: Brian Hitney’s blog Peter Laudati’s blog Videos: Microsoft DevRadio: How to Get Started with Windows Azure Microsoft DevRadio: (Part 1) What is Windows Azure Web Sites? Microsoft DevRadio: (Part 2) Windows Azure Web Sites Explained Virtual Labs: MSDN Virtual Labs: Windows Azure Download MP3 (Audio only) MP4 (iPod, Zune HD) High Quality MP4 (iPad, PC) Mid Quality MP4 (WP7, HTML5) High Quality WMV (PC, Xbox, MCE)
Lots of great events coming up in the Carolinas: 1/18/2013 Charlotte, NC: We’re hosting a Windows 8 “Office Hours” at the Microsoft Office. More info here. Essentially, if you’re looking for time to hack on a Windows 8 solution, work on ideas, test some stuff out, enlist some testers – we’re there for you. 1/19/2013 Columbia, SC: Windows 8 DevCamp. This event is really shaping up to be great! Interesting sessions, and hands-on time in the labs. If you’re in the area and want to get up to speed on Windows 8, or finally get around to starting or finishing that idea, be sure to come out. 1/22/2013 Charlotte, NC: The developers guild is hosting Phil Japikse, in town for the Columbia Win8 DevCamp. Phil will talking about ASP.NET MVC4 – from mobile features, the Web API, templates, and more.
Those that know me know I am not a fan of javascript, in pretty much all of its forms (including node.js), however, I’m really digging Windows Azure Mobile Services (WAMS). WAMS allows you to easily provide a back end to applications for storing data, authenticating users, and supporting notifications on not just Windows and Windows Phone, but also iOS with future plans of supporting Android soon. Now, I mention javascript because WAMS provides a slick node-like powered data service that makes it really easy to store data in the cloud. The ToDoList example exercise illustrates the ease at storing user data in the cloud and hooking it up with authentication and notification support. The nice thing about the authentication is that it’s easily integrated into the backend: But, more on this later. Right now, I want to deal with notifications in WAMS. In WAMS, you have the opportunity to right custom server-side javascript to do things like send notifications on insert/update/delete/read access: In my case, I want to send a tile update if the new data meets some criteria. Let’s start all the way down the code and work our way out, starting with the notification piece. One page you MUST have bookmarked is the tile template catalog on MSDN. This page defines the XML syntax for all possible tiles your tile can have, including both small/square tiles, and large/wide tiles. All of these have a defined schema, such as this for TileSquarePeekImageAndText04: <tile>
<visual>
<binding template="TileSquarePeekImageAndText04">
<image id="1" src="image1" alt="alt text"/>
<text id="1">Text Field 1</text>
</binding>
</visual>
</tile>
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
Which produces a tile that “peeks”, such as this (which flips between the top half and bottom half):
Yes, it’s easy to laugh at the magic “04” in the template title. I like to joke that my personal favorite is TileWideSmallImageAndText03. But, there variety is crucial to creating the ideal app experience and that depends on how you want to display the data -- and that requires knowing the XML template.
Now, in WAMS, there’s a great tutorial on sending some basic notifications. In that walkthrough, a notification is sent via the server-side javascript like so:
push.wns.sendTileSquareText02(“https://bn1.notify.windows.com?[snip]”, { text1: “some text”, text2: “more text”});
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
Now, at first glance, this is very nice because WAMS will write the XML for you. However, you still must know what data the template requires. Does it need an image? One text line? Two? You get the point. Unsurprisingly, calling that method will generate XML like:
<tile>
<visual>
<binding template="TileSquareText02">
<text id="1">some text</text>
<text id="2">more text</text>
</binding>
</visual>
</tile>
You can learn more about this in the WAMS script reference. Another must-have bookmark. However, I recommend you don’t use these at all, and instead write the XML payload directly. This is for a few reasons, but primarily, it’s for control – and, really, you have to know the fields required anyway and you’ll still have the tile catalog page open for reference.
In looking at the mpns (Microsoft Push Notification Service) library a bit closer (awesome job by the guys, by the way) up on git, it has this method:
var raw = new mpns.rawNotification('My Raw Payload', options);
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
When developing my app, I realized I had no idea what tile size the user has. Some may opt to use a wide tile, others a small tile. I needed different tiles to support both. I didn’t like sending two notifications (seems wasteful, doesn’t it?) and to do this efficiently, it’s easier to just create the payload explicitly that includes all tiles. For example, this includes two completely different tiles:
var payload = "<tile><visual><binding template='TileWideImageAndText02'>" +
"<image id='1' src='" + xmlEscape(bigImage) + "' alt='map'/>" +
"<text id='1'>" + text1 + "</text>" +
"<text id='2'>" + text2 + "</text>" +
"</binding>" +
"<binding template='TileSquareImage' branding='none'>" +
"<image id='1' src='" + xmlEscape(smallImage) +
"' alt='map'/></binding></visual></tile>";
push.wns.send(channelUri, payload, 'wns/tile',
{
client_id: 'ms-app://<snip>',
client_secret: 'i will never tell',
headers: { 'X-WNS-Tag' : 'MainTile' }
}...
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
Sure, it doesn’t look as clean and (gasp!) we have to do string concatenation. But, it’s only a couple of minutes more work and just more flexible. Like I said: either way, you need to know the template. In my case, I’m sending both notifications in one payload. The first is TileWideImageAndText02, which produces a nice image with the text on the bottom describing the image. If the user has a small tile, it will use TileSquareImage, which basically just forgoes the text and just displays the image. After trying a few, I settled on this combination as the best user experience. This is an easy way, with minimal effort, to support both wide and narrow tiles.
As an aside, I recommend setting the tag (X-WNS-Tag) header, particularly if your app cycles tiles and you want to replace a specific tile. Also, it’s a good ideal to XML escape all data, which I’m doing with the long image URLs … and this, I believe, is taken right from the mpns library:
var xmlEscape = function (text) {
return text.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
If you don’t escape the data and have some illegal chars in there as a result, the notification gets sent correctly (that is, accepted), but gets ignored by the client.
Now that I’ve got the basic code to send a tile, I needed to filter some data and run a query and sort users by distance. Sound like fun? I’ll write about that next…