Let your apps sell themselves!

Don't miss any opportunity to market your Windows Phone apps! Each one of your apps can serve as an ad for your other apps. Learn how to add a listing of everything you have published in Marketplace to each of your apps. Even better, it will always be up to date!

Introduction

I've written a number of Windows Phone apps, and each time I've wanted to let people know about my other apps. While I could mention the other apps somewhere, I wouldn't want to update all of my apps each time I release a new one. It finally occurred to me that I could use the listing of apps directly from Marketplace instead. This provides me with an easy-to-parse XML feed (Atom) of my apps with all of the info that I need. Armed with that, it wasn't too much work to create a user control to let me drop in the list of apps anytime I need it.

This code doesn't require a physical phone, but it isn't very useful if you don't have a Marketplace account! Ideally, you should have several published apps under your account for this to make much sense. Once you have it in place though, all of your apps will always show your complete list without any special updates.

If you don't have the software installed, go to create.msdn.com, then click Download the free tools to download the Windows Phone Developer Tools (or use the direct download link provided above this Introduction section). This code is written for the Windows Phone Developer Tools 7.1 (Mango). It’s is a mostly online install, and it’s pretty big so expect it to take some time. Even if you don’t have any development tools, this will give you Visual Studio Express, Blend, and XNA Game Studio. If you have the full-version tools already, it will add new templates.

Project Basics

The intention is to create a user control to display a list of apps from a given publisher (preferably yourself!). This user control will be implemented as a ListBox with individual apps showing up visually similar to the way they do in Marketplace. Touching an app in the list should bring the user directly to the appropriate Marketplace page. It should be as easy as adding a project reference, adding the control to a XAML page, and setting the publisher (this could potentially be set by reading the WMAppManifest.xml file).

Marketplace Data

At first glance, getting Marketplace data programmatically isn't an option. Sadly, there's no API for this. Fortunately, there's a solution! If you're using the Zune app to browse publishers and apps, you can watch the network traffic using Fiddler (http://fiddler2.com/fiddler2/). What's nice is that everything you do in Zune results in a simple HTTP request for the data, which is returned as an XML stream. A simple WebRequest object can do a DownloadStringAsync call to get the data, then the SyndicationFeed class can load and parse it. There are extension elements for rating, release date, and price. Even nicer, is that the image thumbnail can be resized server-side based on the URL query string. This makes it super easy to get exactly what we need.

The server of interest is catalog.zune.net, and the URL format is:

/v3.2/en-US/apps?q=PublisherName&clientType=WinMobile 7.1&store=zest

The "q" parameter just needs to be set to the publisher name of interest. Remember to URL encode yours if you have spaces in it.

Note the "clientType" parameter. If you change it to WinMobile 7.0 you will get NoDo (pre-Mango) apps only. You may or may not have anything show up for that, but it's not likely you'll want that list.

The "store" parameter is set to "zest" but we'll just have to take this one on faith! I'm not aware of any other options here. This could vary by country, but I don't have any data on that.

This will return all apps for Mango. This returns feed information starting with a header:

From this block you can get title, release date, rating average and count, category, image ID (easily converted to URL), and price (per market). The System.ServiceModel.Syndication.SyndicationFeed class can read from an XMLReader object and it handles everything for you. Dealing with the custom Marketplace namespace can lead to some slight complication, but fortunately that's simplified as well.

Syndicated Data

For standard Atom elements (the ones with the a: namespace here), you can use properties of the SyndicationItem class. For some reason, the SyndicationItem class isn't actually available in the Windows Phone libraries. I'm not sure why this is the case, but it turns out that you can use the desktop version without a problem. Just add a reference to the "C:\Program Files (x86)\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\System.ServiceModel.Syndication" assembly. You might get a warning when you add it, but it will work fine. If you download the accompanying code, you'll get all the assemblies you need in it.

The SyndicationItem class gets you properties like Title and Id. The other properties are all custom types in the "http://schemas.zune.net/catalog/apps/2008/02" namespace. From these properties, you are interested in shortDescription, userRatingCount, averageUserRating, version, releaseDate, displayPrice, and priceCurrencyCode. Instead of worrying about namespaces, since you know they are custom elements, you can use the ElementExtensions collection on the SyndicationItem and query the OuterName property. A simple extension method makes this easy:

Where it gets a little bit tricky is the "offers" block. This is an XML block within the overall Item block. This requires that you shift over to an XmlReader method of parsing. This works by reading the XML sequentially, stopping at elements of interest. You start by finding the "offers" element, but instead of using the GetObject method, you use GetReader. From there, you use a while loop to visit every node, and grab the value if it's of XmlNodeType.Element and either displayPrice or priceCurrencyCode.

Visual Basic

Dim offers = (From ee In item.ElementExtensions Where ee.OuterName = "offers").FirstOrDefault().GetReader()
' TODO: Restrict to current country's offer
If offers.ReadToFollowing("offer") Then
While offers.Read()
If offers.NodeType = XmlNodeType.Element Then
If offers.Name = "displayPrice" Then
app.DisplayPrice = offers.ReadElementContentAsString()
ElseIf offers.Name = "priceCurrencyCode" Then
app.PriceCurrencyCode = offers.ReadElementContentAsString()
End If
End If
End While
End If

Notice the "TODO" block. If you are offering your app for sale at different prices in different markets, this will only grab the first offer. Ideally, this should grab the offer for the user's location, but that's another topic!

Creating the Control

Creating a user control makes it easy to add your list of apps anywhere. I like to add it to a PivotItem in my About page. There are really three things to do:

Download the XML

Parse the XML into model objects

Bind the collection of items to a list

Of course, before you can show anything, you'll want to create a template to serve as the ItemTemplate in the control. This will use databinding to point back at the properties of the marketplace entries to display the name, price, image, etc. This can be created inline within the ListBox markup, or in the UserControl.Resources block (as it is in this case). The root element is a Grid. The Image shows the app's icon, and the StackPanel displays the title, price, and rating information.

The other elements are used to display a loading message and an error message. Switching between the list, loading, or error message is accomplished using VisualStateManager with the "Normal," "Loading," or "Error" states accordingly.

The only other methods needed in the code-behind are to handle selection and caching. When an item is selected, you create a MarketplaceDetailTask object, set the ContentIdentifier to the app's unique ID, and call Show(). Set the SelectedIndex property back to -1 to prevent a problem where a user can't press the same item twice in the row.

The caching is important so the app doesn't cause data access on every single visit to the control. It's currently set to reload once per day, but this could be changed (or better yet, made configurable). Even if it's more than a day, if there's an error downloading the data it will always fallback to cache.

Visual Basic

Dim items As IEnumerable(Of MarketplaceApp) = Nothing
If IsolatedStorageSettings.ApplicationSettings.Contains("PublisherAppsControls.Items") Then
items = DirectCast(IsolatedStorageSettings.ApplicationSettings( _
"PublisherAppsControls.Items"), IEnumerable(Of MarketplaceApp))
' Use the cache if within a day...
Dim dt = CType(IsolatedStorageSettings.ApplicationSettings( _
"PublisherAppsControls.Items.DateTime"), DateTime)
If DateTime.Now.Subtract(dt).TotalDays < 1 Then
ctrl.Apps.Clear()
For Each i In items
ctrl.Apps.Add(i)
Next
Return
End If
End If

Next Steps

I haven't tested this app against publishers with large numbers of apps. I'm not sure if they would all come back in the response or if there would be more requests to make. It would also be good to filter out the currently running app. This could be obtained from the WMAppManifest.xml file. As mentioned in the article, it would also be good to restrict the offer to the current country, but I really don't know how to do that without using the location API, which seems too heavy-handed.

It would be nice to create a general-purpose library for Zune Marketplace data. There are some good starts out there from the likes of Brandon Watson and Jesse Liberty, a CodePlex library, and even another Coding4Fun project, but much of that is based on general catalog data, or specialized for music entries. Even better would be if Microsoft can put out their own API! That would ensure a better experience if/when formats change over time.

Conclusion

XML has made the world of data so much easier to consume! As soon as I saw structured data as the foundation for Zune I knew this would be a pretty easy project. Being able to advertise all of your apps in each of your apps provides a great way to promote with a minimum of effort.

About the Author

Arian Kulp is a software developer living in Western Oregon. He creates samples, screencasts, demos, labs, and articles, speaks at programming events, and enjoys hiking and other outdoor adventures with his family.

On playing around with the API I found that you can use the 'publisher' parameter rather than the 'q' parameter to get all your own apps. This will stop other publishers' apps being shown in the results if you have common words in your publisher name.

Obviously you can also change the country tag to get ratings and prices, etc, for the market the user is in.

I had an issue where the list was not displaying my submitted apps. Originally I had placed it in a panoramaitem, inside of a stackpanel. However, once I replaced the stackpanel with a grid the list updated correctly. I highly recommend using this as it's simple to use.

Is there a way to allow the publishername to be databound? I find if I override the DataContext property things don't work so I have the name hardcoded.

Remove this comment

Remove this thread

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation,
please create a new thread in our Forums, or
Contact Us and let us know.