June 2012

Volume 27 Number 06

Windows Phone - Behind the Scenes: A Windows Phone Feed-Reader App

By Matt Stroshane | June 2012

I’m a feed addict. I love the magic of RSS and Atom feeds, how news comes to me rather than the other way around. But with convenient access to so much information, consuming it in a meaningful way has become a challenge. So when I learned that some Microsoft interns were developing a Windows Phone feed-reader app, I was excited to find out how they approached the problem.

As part of their internship, Francisco Aguilera, Suman Malani and Ayomikun (George) Okeowo had 12 weeks to develop a Windows Phone app that included some new Windows Phone SDK 7.1 features. Being new to Windows Phone development, they were good test subjects for our platform, tools and documentation.

After considering their options, they decided on a feed-reader app that would demonstrate a local database, Live Tiles and a background agent. They demonstrated a lot more than that! In this article, I’m going to walk you through how they used those features. So install the Windows Phone SDK 7.1, download the code and get it up on your screen. Let’s get going!

Using the App

The central hub of the app is the main page, MainPage.xaml (Figure 1). It consists of four panorama panels: “what’s new,” “featured,” “all” and “settings.” The “what’s new” panel shows the latest updates to the feeds. “Featured” displays six articles it thinks you’d like based on your reading history. The “all” panel lists all of your categories and feeds. To download articles only via Wi-Fi, use the setting on the “settings” panel.

The Main Page of the App After Creating a Windows Phone News Category
Figure 1 The Main Page of the App After Creating a Windows Phone News Category

The “what’s new” and “featured” panels provide a way to navigate directly into an article. The “all” panel provides a list of categories and feeds. From the “all” panel, you can navigate to a collection of articles that are grouped by feed or category. You can also use the application bar on the “all” panel to add a new feed or category. Figure 2shows how the main page relates to the other eight pages of the app.

The Page Navigation Map, with Auxiliary Pages in Gray
igure 2 The Page Navigation Map, with Auxiliary Pages in Gray

Similar to pivoting, you can navigate horizontally on the Category, Feed or Article pages. When you’re on one of these pages, arrows appear in the application bar (see Figure 3). The arrows allow you to display the data for the previous or next category, feed or article in the database. For example, if you’re viewing the Business category on the Category page, tapping the “next” arrow displays the Entertainment category on the Category page.

The Category, Feed and Article Pages with Their Application Bars Expanded
Figure 3 The Category, Feed and Article Pages with Their Application Bars Expanded

However, the arrow keys don’t actually navigate to another Category page. Instead, the same page is bound to a different data source. Tapping the phone’s Back button returns you to the “all” panel without the need for any special navigation code.

From the Article page, you can navigate to the Share page and send a link via messaging, e-mail or a social network. The application bar also provides the ability to view the article in Internet Explorer, “add to your favorites” or remove it from the database.

Under the Hood

When you open the solution in Visual Studio, you’ll see that it’s a C# app divided into three projects:

  1. FeedCast: The part the user sees—the foreground app (View and ViewModel code).
  2. FeedCastAgent: The background agent code (periodic scheduled task).
  3. FeedCastLibrary: The shared networking and data code.

The team used the Silverlight for Windows Phone Toolkit (November 2011) and the Microsoft Silverlight 4 SDK. Controls from the toolkit—Microsoft.Phone.Controls.Toolkit.dll—are used in most pages of the app. For example, HubTile controls are used to display articles in the “featured” panel of the main page. To help with networking, the team used System.ServiceModel.Syndication.dll from the Silverlight 4 SDK. This assembly isn’t included in the Windows Phone SDK and isn’t specifically optimized for phone apps, but the team members found it worked well for their needs.

The foreground app project, FeedCast, is the largest of the three in the solution. Again, this is the part of the app that the user sees. It’s organized into nine folders:

  1. Converters: Value converters that bridge the gap between data and UI.
  2. Icons: Icons used by the application bars.
  3. Images: Images used by HubTiles when articles have no images.
  4. Libraries: The Toolkit and Syndication assemblies.
  5. Models: Data-related code not used by the background agent.
  6. Resources: Localization resource files in English and Spanish.
  7. Themes: Customizations for the HeaderedListBox control.
  8. ViewModels: ViewModels and other helper classes.
  9. Views: Code for each page in the foreground app.

This app follows the Model-View-ViewModel (MVVM) pattern. Code in the Views folder focuses primarily on the UI. The logic and data associated with individual pages is defined by code in the ViewModels folder. Although the Models folder contains some data-related code, the data objects are defined in the FeedCastLibrary project. The “Model” code there is reused by the foreground app and background agent.

The FeedCastLibrary project contains the data and networking code used by the foreground app and the background agent. This project contains two folders: Data and Networking. In the Data folder, the FeedCast “Model” is described by partial classes in four files: LocalDatabaseDataContext.cs, Article.cs, Category.cs and Feed.cs. The DataUtils.cs file contains the code that performs common database operations. A helper class for using isolated storage settings is in the Settings.cs file. The Networking folder of the FeedCastLibrary project contains the code used to download and parse content from the Web, the most significant of which are the Download methods in the WebTools.cs file.

There’s only one class in the FeedCastAgent project, Scheduled­Agent.cs, which is the background agent code. The OnInvoke method is called when it runs and the SendToDatabase method is called when downloads complete. I’ll discuss more about downloading later.

Local Database

For maximum productivity, each of the team members focused on a different area of the app. Aguilera focused on the UI, Views and ViewModels in the foreground app. Okeowo worked on the networking and getting data out of the feeds. Malani worked on the database architecture and operations.

In Windows Phone you can store your data in a local database. The local part is because it’s a database file residing in isolated storage (your app’s on-device storage bucket, isolated from other apps). Essentially, you describe your database tables as Plain Old CLR Objects, with the properties of those objects representing the database columns. This allows each object of that class to be stored as a row in the corresponding table. To represent the database, you create a special object referred to as a data context that inherits from System.Data.Linq.DataContext.

The magical ingredient of the local database is the LINQ to SQL runtime—your data butler. You call the data context CreateDatabase method, and LINQ to SQL creates the .sdf file in isolated storage. You create LINQ queries to specify the data you want, and LINQ to SQL returns strongly typed objects that you can bind to your UI. LINQ to SQL allows you to focus on your code while it handles all the low-level database operations.

Rather than type up all of the classes, Malani used Visual Studio 2010 Ultimate to take a different approach. She created the database tables visually, using the Server Explorer Add Connection dialog to create a SQL Server CE database and then the New Table dialog to build the tables.

Once Malani had her schema designed, she used SqlMetal.exe to generate a data context. SqlMetal.exe is a command-line utility from desktop LINQ to SQL. Its purpose is to create a data context class based on a SQL Server database. The code it generates is quite similar to a Windows Phone data context. Using this technique, she was able to build the tables visually and generate the data context quickly.

The database Malani built is shown in Figure 4. The three primary tables are Category, Feed and Article. Also, a linking table, Category_Feed, is used to enable a many-to-many relationship among categories and feeds. Each category can be associated with multiple feeds, and each feed can be associated with multiple categories. Note that the app’s “favorite” feature is just a special category that can’t be deleted.

The Database Schema
Figure 4 The Database Schema

However, the data context generated by SqlMetal.exe still contained some code that isn’t supported on Windows Phone. After Malani added the data context code file to the Windows Phone project, she compiled the project to locate which code wasn’t valid. She remembers having to remove one constructor, but the rest compiled fine.

Upon inspection of the data context file, LocalDatabase­DataContext.cs, you might notice that all of the tables are partial classes. The rest of the code associated with these tables (that wasn’t autogenerated by SqlMetal.exe) is stored in the code files Article.cs, Category.cs and Feed.cs. By separating the code this way, Malani could make changes to the database schema without affecting the extensibility method definitions that she wrote by hand. Had she not done this, she would have had to re-add the methods each time she autogenerated LocalDatabaseDataContext.cs (because SqlMetal.exe overwrites all code in the file).

Maintaining Concurrency

As with most Windows Phone apps that aim to provide a responsive, fluid experience, this one uses multiple concurrent threads to do its work. In addition to the UI thread, which accepts user input, multiple background threads might be dealing with downloading and parsing the RSS feeds. Each of these threads will ultimately need to make changes to the database.

While the database itself offers robust concurrent access, the DataContext class is not thread-safe. In other words, the single global DataContext object used in this app can’t be shared across multiple threads without adding some form of concurrency model. To address this problem, Malani used the LINQ to SQL concurrency APIs and a mutex object from the System.Threading namespace.

In the DataUtils.cs file, the mutex WaitOne and ReleaseMutex methods are used to synchronize access to data in cases where there could be contention between the DataContext classes. For example, if multiple concurrent threads (from the foreground app or the background agent) were to call the SaveChangesToDB method at about the same time, whichever code executes WaitOne first gets to continue. The WaitOne call from the other doesn’t complete until the first code calls ReleaseMutex. For this reason, it’s important to put your ReleaseMutex call in the finally statement when using try/catch/finally for database operations. Without a call to ReleaseMutex, the other code will wait at the WaitOne call until the owning thread exits. From the user’s perspective, it could take “forever.”

Rather than a single global DataContext object, you can also design your app to create and destroy smaller DataContext objects on a per-thread basis. However, the team members found that the global-DataContext approach simplified development. I should also note that because the app only needed to protect against cross-thread access, not cross-process access, they could’ve also used a lock instead of the mutex. The lock might have offered better performance.

Consuming Data

Okeowo focused his efforts on bringing data into the app. The WebTools.cs file contains the code where most of the action happens. But the WebTools class isn’t only used for downloading feeds—it’s also used on the new-feed page to search for new feeds on Bing. He accomplished this by creating a common interface, IXmlFeedParser, and abstracting the parsing code into different classes. The SynFeedParser class parses the feeds and the SearchResultParser class parses the Bing search results.

However, the Bing query doesn’t actually return articles (despite the collection of Article objects returned by the IXmlFeedParser interface). Instead, it returns a list of feed names and URIs. What gives? Well, Okeowo realized that the Article class already had the properties he needed to describe a feed; he didn’t need to create another class. When parsing search results, he used ArticleTitle for the feed name and ArticleBaseURI for the feed URI. See SearchResultParser.cs in the accompanying code download for more.

The code in the new page ViewModel (NewFeedPageViewModel.cs in the sample code) shows how the Bing search results are consumed. First, the GetSearchString method is used to piece together the Bing search string URI based on the search terms that the user enters on NewFeedPage, as shown in the following code snippet:

private string GetSearchString(string query)
{
  // Format the search string.
  string search = "http://api.bing.com/rss.aspx?query=feed:" + query +
    "&source=web&web.count=" + _numOfResults.ToString() +
    "&web.filetype=feed&market=en-us";
  return search;
}

The _numOfResults value limits how many search results are returned. For more about accessing Bing through RSS, see the MSDN Library page, “Accessing Bing Through RSS,” at bit.ly/kc5uYO.

The GetSearchString method is called in the GetResults method, where the data is actually retrieved from Bing (see Figure 5). The GetResults method looks a little backward because it lists a lambda expression that handles the AllDownloadsFinished event “inline,” before the code to initiate the download is actually called. When the Download method is called, the WebTools object queries Bing per the URI that was constructed with GetSearchString.

Figure 5 The GetResults Method in NewFeedPageView­Model.cs Queries Bing for New Feeds

public void GetResults(string query, Action<int> Callback)
{
  // Clear the page ViewModel.
  Clear();
  // Get the search string and put it into a feed.
  Feed feed = new Feed { FeedBaseURI = GetSearchString(query) };
  // Lambda expression to add results to the page
  // ViewModel after the download completes.
  // _feedSearch is a WebTools object.
  _feedSearch.AllDownloadsFinished += (sender, e) =>
    {
      // See if the search returned any results.
      if (e.Downloads.Count > 0)
      {
        // Add the search results to the page ViewModel.
        foreach (Collection<Article> result in e.Downloads.Values)
        {
          if (null != result)
          {
            Deployment.Current.Dispatcher.BeginInvoke(() =>
              {
                foreach (Article a in result)
                {
                  lock (_lockObject)
                  {
                    // Add to the page ViewModel.
                    Add(a);
                  }
                }
                Callback(Count);
              });
          }
        }
      }
      else
      {  
        // If no search results were returned.
        Deployment.Current.Dispatcher.BeginInvoke(() =>
          {
            Callback(0);
          });
      }
    };
  // Initiate the download (a Bing search).
  _feedSearch.Download(feed);
}

The WebTools Download method is also used by the background agent (see Figure 6), but in a different way. Rather than download from just one feed, the agent passes to the method a list of several feeds. For retrieving results, the agent takes a different strategy. Rather than wait until articles from all feeds are downloaded (via the AllDownloadsFinished event), the agent saves the articles as soon as each feed download is complete (via the SingleDownloadFinished event).

Figure 6 The Background Agent Initiates a Download (Without Debug Comments)

protected override void OnInvoke(ScheduledTask task)
{
  // Run the periodic task.
  List<Feed> allFeeds = DataBaseTools.GetAllFeeds();
  _remainingDownloads = allFeeds.Count;
  if (_remainingDownloads > 0)
  {
    Deployment.Current.Dispatcher.BeginInvoke(() =>
      {
        WebTools downloader = new WebTools(new SynFeedParser());
        downloader.SingleDownloadFinished += SendToDatabase;
        try
        {
          downloader.Download(allFeeds);
        }
        // TODO handle errors.
        catch { }
      });
  }
}

The job of the background agent is to keep all of your feeds up-to-date. To do this, it passes to the Download method a list of all feeds. The background agent only has a short amount of time to run, and when its time is up, the process is stopped immediately. So as the agent downloads feeds, it sends the articles to the database one feed at a time. This way the background agent has a much higher probability of saving new articles before it’s stopped.

The single-feed and multiple-feed Download methods are actually overloads for the same code. The download code initiates an HttpWebRequest for each feed (asynchronously). As soon as the first request returns, it calls the SingleDownloadFinished event handler. The feed information and articles are then packaged into the event using the SingleDownloadFinishedEventArgs. As shown in Figure 7, the SendToDatabase method is wired up to the SingleDownloadFinshed method. When that returns, SendToDatabase takes the articles out of the event arguments and passes them to the DataUtils object named DataBaseTools.

Figure 7 The Background Agent Saves Articles to the Database (Without Debug Comments)

private void SendToDatabase(object sender, 
  SingleDownloadFinishedEventArgs e)
{
  // Ensure download is not null!
  if (e.DownloadedArticles != null)
  {
    DataBaseTools.AddArticles(e.DownloadedArticles, e.ParentFeed);
    _remainingDownloads--;
  }
  // If no remaining downloads, tell scheduler the background agent is done.
  if (_remainingDownloads <= 0)
  {
    NotifyComplete();
  }
}

Should the agent finish all downloads within its allotted time, it calls the NotifyComplete method to notify the OS that it finished. This allows the OS to allocate those unused resources to other background agents.

Following the code one step deeper, the AddArticles method in the DataUtils class checks to make sure the article is new before it adds it to the database. Note in Figure 8 how a mutex is used again to prevent contention on the data context. Finally, when the article is found to be new, it’s saved to the database with the SaveChangesToDB method.

Figure 8 Adding Articles to the Database in the DataUtils.cs File

public void AddArticles(ICollection<Article> newArticles, Feed feed)
{
  dbMutex.WaitOne();
  // DateTime date = SynFeedParser.latestDate;
  int downloadedArticleCount = newArticles.Count;
  int numOfNew = 0;
  // Query local database for existing articles.
  for (int i = 0; i < downloadedArticleCount; i++)
  {
    Article newArticle = newArticles.ElementAt(i);
    var d = from q in db.Article
            where q.ArticleBaseURI == newArticle.ArticleBaseURI
            select q;
    List<Article> a = d.ToList();
    // Determine if any articles are already in the database.
    bool alreadyInDB = (d.ToList().Count == 0);
    if (alreadyInDB)
    {
      newArticle.Read = false;
      newArticle.Favorite = false;
      numOfNew++;
    }
    else
    {
      // If so, remove them from the list.
      newArticles.Remove(newArticle);
      downloadedArticleCount--;
      i--;
    }               
  }
  // Try to submit and update counts.
  try
  {
    db.Article.InsertAllOnSubmit(newArticles);
    Deployment.Current.Dispatcher.BeginInvoke(() =>
      {
        feed.UnreadCount += numOfNew;
        SaveChangesToDB();
      });
    SaveChangesToDB();
  }
  // TODO handle errors.
  catch { }
  finally { dbMutex.ReleaseMutex(); }
}

The foreground app uses a technique similar to that found in the background agent for consuming data with the Download method. See the ContentLoader.cs file in the accompanying code download for the comparable code.

Scheduling the Background Agent

The background agent is just that—an agent that performs work in the background for the foreground app. As you saw earlier in Figure 6 and Figure 7, the code that defines that work is a class named Scheduled­Agent. It’s derived from Microsoft.Phone.Scheduler.ScheduledTaskAgent (which is derived from Microsoft.Phone.BackgroundAgent). While the agent gets a lot of attention because it does the heavy lifting, it would never run if it weren’t for the scheduled task.

The scheduled task is the object used to specify when and how often the background agent will run. The scheduled task used in this app is a periodic task (Microsoft.Phone.Scheduler.PeriodicTask). A periodic task runs regularly for a short amount of time. To actually get that task on the schedule and query for it and so on, you use the scheduled action service (ScheduledActionService).

The scheduled task code for this app is in the BackgroundAgentTools.cs file, in the foreground app project. That code defines the StartPeriodicAgent method, which is called by App.xaml.cs in the application constructor (see Figure 9).

Figure 9 Scheduling the Periodic Task in BackgroundAgentTools.cs (Minus Comments)

public bool StartPeriodicAgent()
{
  periodicDownload = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
  bool wasAdded = true;
  // Agents have been disabled by the user.
  if (periodicDownload != null && !periodicDownload.IsEnabled)
  {
    // Can't add the agent. Return false!
    wasAdded = false;
  }
  // If the task already exists and background agents are enabled for the
  // application, then remove the agent and add again to update the scheduler.
  if (periodicDownload != null && periodicDownload.IsEnabled)
  {
    ScheduledActionService.Remove(periodicTaskName);
  }
  periodicDownload = new PeriodicTask(periodicTaskName);
  periodicDownload.Description =
    "Allows FeedCast to download new articles on a regular schedule.";
  // Scheduling the agent may not be allowed because maximum number
  // of agents has been reached or the phone is a 256MB device.
  try
  {
    ScheduledActionService.Add(periodicDownload);
  }
  catch (SchedulerServiceException) { }
  return wasAdded;
}

Prior to scheduling the periodic task, StartPeriodicAgent performs a few checks because there’s always a chance you can’t schedule the scheduled task. First of all, scheduled tasks can be disabled by users on the background tasks list in the Applications panel of Settings. There’s also a limit to how many tasks can be enabled on a device at one time. It varies per device configuration, but it could be as low as six. If you attempt to schedule a scheduled task after that limit has been exceeded, or if your app is running on a 256MB device, or if you’ve already scheduled the same task, the Add method will throw an exception.

This app calls the StartPeriodicTask method every time it launches because background agents expire after 14 days. Refreshing the agent on every launch ensures the agent can continue running even if the app isn’t launched again for several days.

The periodicTaskName variable in Figure 9, used to find an existing task, equals “FeedCastAgent.” Note that this name doesn’t identify the corresponding background agent code. It’s simply a friendly name that you can use to work with ScheduledActionService. The foreground app already knows about the background agent code because it was added as a reference to the foreground app project. Because the background agent code was created as a project of type Windows Phone Scheduled Task Agent, the tools were able to wire up things correctly when the reference was added. You can see the foreground app-background agent relationship specified in the foreground app manifest (WMAppManifest.xml in the sample code), as shown here:

<Tasks>
  <DefaultTask Name="_default" 
    NavigationPage="Views/MainPage.xaml" />
  <ExtendedTask Name="BackgroundTask">
    <BackgroundServiceAgent Specifier="ScheduledTaskAgent" 
      Name="FeedCastAgent"
      Source="FeedCastAgent" Type="FeedCastAgent.ScheduledAgent"/>
  </ExtendedTask>
</Tasks>

Tiles

Aguilera worked on the UI, the Views and ViewModels. He also worked on the localization and Tiles feature. Tiles, sometimes referred to as Live Tiles, display dynamic content and link to the app from Start. The application Tile of any app can be pinned to Start (without any work on the part of the developer). However, if you want to link to somewhere other than the main page of your app, you need to implement Secondary Tiles. These allow you to navigate the user deeper into your app—beyond the main page—to a page that you can customize for whatever the Secondary Tile represents.

In FeedCast, users can pin a feed or category (Secondary Tile) to Start. With a single tap, they can instantly be reading the latest articles related to that feed or category. To enable this experience, first they need to be able to pin the feed or category to Start. Aguilera used the Silverlight Toolkit for Windows Phone ContextMenu to facilitate this. Tapping and holding a feed or category in the “all” panel of the main page makes the context menu appear. From there, users can choose to remove or pin the feed or category to Start. Figure 10demonstrates the end-to-end process from the user’s perspective.


Figure 10 Pinning the Windows Phone News Category to Start and Launching the Category Page

Figure 11 shows the XAML that makes the context menu possible. The second MenuItem displays “pin to start” (when English is the display language). When that item is tapped, the click event calls the OnCategoryPinned method to initiate the pinning. Because this app is localized, the text for the context menu actually comes from a resource file. This is why the Header value is bound to LocalizedResources.ContextMenuPinToStartText.

Figure 11 The Context Menu to Remove or Pin to Start a Category

<toolkit:ContextMenuService.ContextMenu>
  <toolkit:ContextMenu>
    <toolkit:MenuItem Tag="{Binding}"
      Header="{Binding LocalizedResources.ContextMenuRemoveText,
               Source={StaticResource LocalizedStrings}}"
      IsEnabled="{Binding IsRemovable}"
      Click="OnCategoryRemoved"/>
    <toolkit:MenuItem Tag="{Binding}"
      Header="{Binding LocalizedResources.ContextMenuPinToStartText,
               Source={StaticResource LocalizedStrings}}"
      IsEnabled="{Binding IsPinned, 
      Converter={StaticResource IsPinnable}}"
      Click="OnCategoryPinned"/>
  </toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>

This app has only two resource files, one for Spanish and the other for English (the default). However, because localization is in place, it would be relatively easy to add more languages. Figure 12shows the default resource file, AppResources.resx.

The Default Resource File, AppResources.resx, Supplies the UI Text for All Languages Except Spanish
Figure 12 The Default Resource File, AppResources.resx, Supplies the UI Text for All Languages Except Spanish

Initially, the team wasn’t quite sure how it was going to determine exactly which category or feed needed to be pinned. Then Aguilera discovered the XAML Tag attribute (see Figure 11). The team members figured out that they could bind it to the category or feed objects in the ViewModel and then retrieve the individual objects later, programmatically. On the main page, the category list is bound to a MainPageAllCategoriesViewModel object. When the OnCategoryPinned method is called, it uses the GetTagAs method to obtain the Category object (bound to the Tag) that corresponds with that particular item in the list, like so:

private void OnCategoryPinned(object sender, EventArgs e)
{
  Category tappedCategory = GetTagAs<Category>(sender);
  if (null != tappedCategory)
  {
    AddTile.AddLiveTile(tappedCategory);
  }
}

The GetTagAs method is a generic method for obtaining any object that has been bound to the Tag attribute of a container. Although this is effective, it’s not actually necessary for most of its uses on MainPage.xaml.cs. The items in the list are already bound to the object, so binding them to the Tag is somewhat redundant. Rather than use Tag, you can use the DataContext of the Sender object. For example, Figure 13 shows how OnCategoryPinned would look using the recommended DataContext approach.

Figure 13 An Example of Using DataContext Instead of GetTagAs

private void OnCategoryPinned(object sender, EventArgs e)
{
  Category tappedCategory = null;
  if (null != sender)
  {
    FrameworkElement element = sender as FrameworkElement;
    if (null != element)
    {
      tappedCategory = element.DataContext as Category;
      if (null != tappedCategory)
      {
        AddTile.AddLiveTile(tappedCategory);
      }
    }
  }
}

This DataContext approach works well for all cases on MainPage.xaml.cs except for one, the OnHubTileTapped method. This is fired when you tap on a featured article in the “featured” panel of the main page. The challenge is due to the fact that the sender isn’t bound to an Article class—it’s bound to MainPageFeaturedViewModel. That ViewModel contains six articles, so it’s not clearly known from the DataContext which one was tapped. Using the Tag property, in this case, makes it really easy to bind to the appropriate Article.

Because you can pin feeds and categories to Start, the AddLiveTile method has two overloads. The objects and Secondary Tiles differ enough that the team decided not to merge the functionality into a single generic method. Figure 14 shows the Category version of the AddLiveTile method.

Figure 14 Pinning a Category Object to Start

public static void AddLiveTile(Category cat)
{
  // Does Tile already exist? If so, don't try to create it again.
  ShellTile tileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => 
    x.NavigationUri.ToString().Contains("/Category/" + 
    cat.CategoryID.ToString()));
  // Create the Tile if doesn't already exist.
  if (tileToFind == null)
  {
    // Create an image for the category if there isn't one.
    if (cat.ImageURL == null || cat.ImageURL == String.Empty)
    {
      cat.ImageURL = ImageGrabber.GetDefaultImage();
    }
    // Create the Tile object and set some initial properties for the Tile.
    StandardTileData newTileData = new StandardTileData
    {
      BackgroundImage = new Uri(cat.ImageURL, 
      UriKind.RelativeOrAbsolute),
      Title = cat.CategoryTitle,
      Count = 0,
      BackTitle = cat.CategoryTitle,
      BackContent = "Read the latest in " + cat.CategoryTitle + "!",
    };
    // Create the Tile and pin it to Start.
    // This will cause a navigation to Start and a deactivation of the application.
    ShellTile.Create(
      new Uri("/Category/" + cat.CategoryID, UriKind.Relative), 
      newTileData);
    cat.IsPinned = true;
    App.DataBaseUtility.SaveChangesToDB();
  }
}

Before adding a Category Tile, the AddLiveTile method uses the ShellTile class to look at the navigation URIs from all of the active Tiles to determine if the category has already been added. If not, it continues and gets an image URL to associate with the new Tile. Whenever you create a new Tile, the background image needs to come from a local resource. In this case, it uses the ImageGrabber class to get a randomly assigned local image file. However, after you create a Tile, you can update the background image with a remote URL. But this particular app doesn’t do that.

All of the information you need to specify to create a new Tile is contained in the StandardTileData class. That class is used to put text, numbers and background images in the Tiles. When you create the Tile with the Create method, the StandardTileData is passed as a parameter. The other important parameter that’s passed is the Tile navigation URI. This is the URI that’s used to bring users to a meaningful place in your app.

In this app, the navigation URI from the Tile only takes the user as far as the app. To go further than that, a basic UriMapper class is used to route users to the right page. The App.xaml navigation element specifies all of the URI mapping for the app. In each UriMapping element, the value specified by the Uri attribute is the incoming URI. The value specified by the MappedUri attribute is where the user will be navigated to. To maintain the context of the particular category, feed or article, the id value in the brackets, {id}, is carried over from the incoming URI to the mapped URI, like so:

<navigation:UriMapping Uri="/Category/{id}" MappedUri=
  "/Views/CategoryPage.xaml?id={id}"/>

You might have other reasons to use a URI mapper—such as search extensibility, for example—but it isn’t required to use a Secondary Tile. In this app, it was a style decision to use the URI mapper. The team felt that the shorter URIs were more elegant and easier to use. Alternatively, the Secondary Tiles could’ve specified a page-specific URI (such as the MappedUri values) for the same effect.

Regardless of the means, after the URI from the Secondary Tile is mapped to the appropriate page, the user arrives on the Category page with a list of his articles. Mission accomplished.

But Wait, There’s More!

There’s a lot more to this app than what I covered here. Be sure to take a look at the code to learn more about how the team approached these and other problems. For example, SynFeedParser.cs has a nice way of cleaning up the data from feeds that are sometimes littered with HTML tags.

Just keep in mind that this is a snapshot of the interns’ work at the end of 12 weeks, minus a little cleanup. Pro developers might prefer to code some parts differently. Nevertheless, I think the app did a great job of integrating a local database, background agent and Tiles. I hope you enjoyed this look “behind the scenes.” Happy coding!


Matt Stroshane  writes developer documentation for the Windows Phone team. His other contributions to the MSDN Library feature products such as SQL Server, SQL Azure and Visual Studio. When he’s not writing, you might find him on the streets of Seattle, training for his next marathon. Follow him on Twitter at twitter.com/mattstroshane.

Thanks to the following technical experts for reviewing this article: Francisco Aguilera, Thomas Fennel, John Gallardo, Sean McKenna, Suman Malani, Ayomikun (George) Okeowo and Himadri Sarkar