Sunday, March 10, 2013

Displaying Status with Live Tiles

Brain Dead Simple Windows RT App Part 4

In Windows Phone, Microsoft introduced the idea of Tiles that could provide the use with updates. Your tile is important because it is the one thing that is always there whispering to the user “run me” day and night. There are four types of updates, Local, Scheduled, Periodic and Push. Because this is a basic level intro article, I’m going to ignore Scheduled, Periodic and Push and work on Local (in the wild, you will probably need to use one of the other three).

Windows 8 offers a total of 46 tile templates that are enumerated in TileTemplateType. To update a tile you:

  1. Get the Template with TileUpdateManager.GetTemplateContent
  2. Create a notification or a new TileNotification object with a reference to the template you retrieved on Step 1 and set any ExpirationTime
  3. Set notification text on the Template XML you retrieved on Step 1
  4. Update Live Tile Create a TileUpdater and call its Update with a the TileNotification created in Step 1

Back to the App

I’m lazy, so I don’t even want to open my app to know how excited I am. I can use the apps tile to remind me. Now I only need to open the app to use the more advanced features like changing my excitement level.

NOTE: In this app, the settings roam but the Live Tile does not. Since this is a really simple demo, I’m OK with that, I would never ship an app like this.

public static void SetLiveTile(string title, string body, int? seconds = null)
{
    // Get tile template.
    var tileTemplate = TileTemplateType.TileSquareBlock;
    XmlDocument tileXml = TileUpdateManager.GetTemplateContent(tileTemplate);

    // Create notification.
    var notification = new TileNotification(tileXml);
    if (seconds.HasValue)
    {
        notification.ExpirationTime = DateTime.Now + 
        TimeSpan.FromSeconds(seconds.Value);
    }

    // Set notification text.
    XmlNodeList nodes =    tileXml.GetElementsByTagName("text");
    nodes[0].InnerText = title;
    nodes[1].InnerText = body;

    // Update Live Tile.
    var upd = TileUpdateManager.CreateTileUpdaterForApplication();
    upd.Update(notification);
}

And I add the following line to the end of saveSettings()

SetLiveTile(string.Format("{0}!", _exlamationPointCount), HelloMessage, 3600);

Complete Code


using System;
using System.ComponentModel;
using Windows.Data.Xml.Dom;
using Windows.Storage;
using Windows.UI.Notifications;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace HelloWindows
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private const string BASE_MESSAGE = "Hello Windows";

        public MainPage()
        {
            this.InitializeComponent();

            // Load Settings here:
            loadSettings();
            HelloMessage = setMessge(_exlamationPointCount);
            this.DataContext = this;
        }


        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new  PropertyChangedEventArgs(propertyName));
            }
        }

        public string HelloMessage { get; set; }


        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  
        /// The Parameter property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private int _exlamationPointCount = 0;

        private string setMessge(int exlamationPointCount)
        {
            if (exlamationPointCount > 0)
            {
                return _baseMessage + new string('!', exlamationPointCount);
            }
            else
            {
                return _baseMessage;
            }
        }

        private void addPoint_Click(object  sender, RoutedEventArgs e)
        {
            HelloMessage = setMessge(++_exlamationPointCount);
            RaisePropertyChanged("HelloMessage");
            RaisePropertyChanged("IsRemovePossible");
            // This is overkill
            saveSettings();
        }

        private void removePoint_Click(object sender, RoutedEventArgs e)
        {
            if (_exlamationPointCount >= 1)
            {
                HelloMessage = setMessge(--_exlamationPointCount);
                RaisePropertyChanged("HelloMessage");
                RaisePropertyChanged("IsRemovePossible");
                // This is overkill
                saveSettings();
            }
        }

        // Make it possible to disable Remove Point button when there
        // is nothing left to remove
        public bool IsRemovePossible
        {
            get { return _exlamationPointCount > 0; }
        }
        // I replace most refrence to BASE_MESSAGE to _baseMessage
        private string _baseMessage = BASE_MESSAGE;

        // Ensure that RoamingSettings has been loaded
        private static ApplicationDataContainer _roamingSettings;
        public static ApplicationDataContainer RoamingSettings
        {
            get
            {
              if (_roamingSettings == null)
              {
                  // If I don't want roaming, use 
                  // ApplicationData.Current.LocalSettings;
                  _roamingSettings = ApplicationData.Current.RoamingSettings;
              }
              return _roamingSettings;
            }
        }

        // I know that saving every time the value is changed,
        // in real life I'd save the data in App.OnSuspending()
        private void saveSettings()
        {
            // I combine both values into a ApplicationDataCompositeValue
            // and save them both to "HighPriority"
            var composite = new ApplicationDataCompositeValue();
            composite["baseMessage"] = _baseMessage;
            composite["exlamationPointCount"] = _exlamationPointCount;
            RoamingSettings.Values["HighPriority"] = composite;

           SetLiveTile(string.Format("{0}!" , _exlamationPointCount),
           HelloMessage, 3600);
        }
        // To be called from the constructor
        private void loadSettings()
        {
            // I get "HighPriority" cast it to ApplicationDataCompositeValue
            // and extract the values.
            var composite = (ApplicationDataCompositeValue)
            RoamingSettings.Values["HighPriority"];
            if (composite != null) // the first time it will be null
            {
                _baseMessage = (string)composite["baseMessage"];
                _exlamationPointCount = (int)composite["exlamationPointCount"];
          }
      }


      public static void SetLiveTile(string     title, string body, int? seconds = null)
      {
          // Get tile template.
          var tileTemplate = TileTemplateType.TileSquareBlock;
          XmlDocument tileXml = TileUpdateManager.GetTemplateContent(tileTemplate);

          // Create notification.
          var notification = new TileNotification(tileXml);
          if (seconds.HasValue)
          {
              notification.ExpirationTime = DateTime.Now +
              TimeSpan.FromSeconds(seconds.Value);
          }

          // Set notification text.
          XmlNodeList nodes = tileXml.GetElementsByTagName("text");
          nodes[0].InnerText = title;
          nodes[1].InnerText = body;

          // Update Live Tile.
          var upd = TileUpdateManager.CreateTileUpdaterForApplication();
          upd.Update(notification);
      }
    }
}

My Brain Dead Simple Windows RT App: The Series

Thursday, March 07, 2013

Roaming data with ApplicationData

Brain Dead Simple Windows RT App Part 3

In Window 8, you should be able to move between your devices and your apps should follow you. In the Microsoft documentation, there is a lot of talk about some office worker who likes to start some task on her office computer (possibly when her boss’s back is turned) and finish them on the bus with her tablet. The feature that they are pushing with these examples is Roaming.

So by the time that Emily (we can call her that) turns off her computer at work, the app needs to save some state data to the cloud, when she opens the same app on her tablet, it goes out and gets the that data from the cloud and uses it to remember where she left off.

All the stuff you need to do this is in Windows.Storage.ApplicationData. You have a choice of where you want to save it (Roaming or Local) and how (ApplicationDataContainer or StorageFolder).

Roaming data is first stored locally and then pushed to Microsoft servers on the cloud; there is a limit to how much data can be roamed, you can use RoamingStorageQuota to find out how much (note that if you exceed the quota, it will just fail without any errors at all).

Local stores the data locally so no roaming occurs but you have more space to work with and can be used for things that aren’t appropriate to roam.

ApplicationDataContainer (used for RoamingSettings & LocalSettings) contains a property called Values which is a dictionary that contains the settings. Each value in the dictionary is handled separately, so one value may be roamed before the other. You can use ApplicationDataCompositeValue to stick values together and they will be treated as a single unit. Oh, yea, there is a special value in the dictionary called “HighPriority” that will be roamed before everything else.

StorageFolder (used for RoamingFolder, LocalFolder & TemporaryFolder) gives you a handle to a folder in the file system that you can use to read and write files. Files in RoamingFolder will be synced with the magic Microsoft server (if the total size doesn’t exceed RoamingStorageQuota. Files in LocalFolder are not synced but will stay around for a while; the files in TemporaryFolder don’t.

Back to the App

When I run my Hello app, I can press the Exclamation Point Button 42 times but when I close it down and open it up again, if forgets. So, when I open the app again, there are NO exclamation points and that dampens my enthusiasm; I want to keep my excitement so the exclamation points need to stay. We can solve this problem with Roaming!

 

// I replace most refrence to BASE_MESSAGE to _baseMessage
private string _baseMessage = BASE_MESSAGE;

// Ensure that RoamingSettings has been loaded
private static ApplicationDataContainer _roamingSettings;
public static ApplicationDataContainer RoamingSettings
{
    get
    {
        if (_roamingSettings == null)
        {
            //If I don't want roaming, use ApplicationData.Current.LocalSettings;
            _roamingSettings = ApplicationData.Current.RoamingSettings;
        }
        return _roamingSettings;
    }
}

// I know that saving every time the value is changed,
// in real life I'd save the data in App.OnSuspending()
private void saveSettings()
{
    // I combine both values into a ApplicationDataCompositeValue
    // and save them both to "HighPriority"
    var composite = new ApplicationDataCompositeValue();
    composite["baseMessage"] = _baseMessage;
    composite["exlamationPointCount"] = _exlamationPointCount;
    RoamingSettings.Values["HighPriority"] = composite;
}
// To be called from the constructor
private void loadSettings()
{
    // I get "HighPriority" cast it to ApplicationDataCompositeValue
    // and extract the values.
    var composite = (ApplicationDataCompositeValue)
    RoamingSettings.Values["HighPriority"];
    if (composite != null) // the first time it will be null
    {
        _baseMessage = (string)composite["baseMessage"];
        _exlamationPointCount = (int)composite["exlamationPointCount"];
    }
}

And I change the constructor to look like:

public MainPage()
{
    this.InitializeComponent();

    // Load Settings here:
    loadSettings();
    HelloMessage = setMessge(_exlamationPointCount);
    this.DataContext = this;
}

And the setMessage() to the field instead of the constant:

private string setMessge(int exlamationPointCount)
{
    if (exlamationPointCount > 0)
    {
        return _baseMessage + new string('!', exlamationPointCount);
    }
    else
    {
        return _baseMessage;
    }
}

Since I haven’t changed the markup, I won’t repost that part (see my previous post), but I will repost the code behind (MainPage.xaml.cs):

using System.ComponentModel;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace HelloWindows
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private const string BASE_MESSAGE = "Hello Windows";

        public MainPage()
        {
            this.InitializeComponent();

            // Load Settings here:
            loadSettings();
            HelloMessage = setMessge(_exlamationPointCount);
            this.DataContext = this;
        }


        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new  PropertyChangedEventArgs(propertyName));
            }
        }

        public string HelloMessage { get; set; }


        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  
        /// The Parameter property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private int _exlamationPointCount = 0;

        private string setMessge(int  exlamationPointCount)
        {
            if (exlamationPointCount > 0)
            {
                return _baseMessage + new string('!', exlamationPointCount);
            }
            else
            {
                return _baseMessage;
            }
        }

        private void addPoint_Click(object      sender, RoutedEventArgs e)
        {
            HelloMessage = setMessge(++_exlamationPointCount);
            RaisePropertyChanged("HelloMessage");
            RaisePropertyChanged("IsRemovePossible");
            // This is overkill
            saveSettings();
        }

        private void removePoint_Click(object sender, RoutedEventArgs e)
        {
            if (_exlamationPointCount >= 1)
            {
                HelloMessage = setMessge(--_exlamationPointCount);
                RaisePropertyChanged("HelloMessage");
                RaisePropertyChanged("IsRemovePossible");
                // This is overkill
                saveSettings();
            }
        }

        // Make it possible to disable Remove Point button when there
        // is nothing left to remove
        public bool IsRemovePossible
        {
            get { return _exlamationPointCount > 0; }
        }
        // I replace most refrence to BASE_MESSAGE to _baseMessage
        private string _baseMessage = BASE_MESSAGE;

        // Ensure that RoamingSettings has been loaded
        private static ApplicationDataContainer _roamingSettings;
        public static ApplicationDataContainer RoamingSettings
        {
            get
            {
                if (_roamingSettings == null)
                {
                    // If I don't want roaming, use 
                    // ApplicationData.Current.LocalSettings;
                    _roamingSettings = ApplicationData.Current.RoamingSettings;
                }
                return _roamingSettings;
            }
        }

        // I know that saving every time the value is changed,
        // in real life I'd save the data in App.OnSuspending()
        private void saveSettings()
        {
            // I combine both values into a ApplicationDataCompositeValue
            // and save them both to "HighPriority"
            var composite = new  ApplicationDataCompositeValue();
            composite["baseMessage"] = _baseMessage;
            composite["exlamationPointCount"] = _exlamationPointCount;
            RoamingSettings.Values["HighPriority"] = composite;
        }
        // To be called from the constructor
        private void loadSettings()
        {
            // I get "HighPriority" cast it to ApplicationDataCompositeValue
            // and extract the values.
            var composite = (ApplicationDataCompositeValue)
            RoamingSettings.Values["HighPriority"];
            if (composite != null           ) // the first time it will be null
            {
                _baseMessage = (string)composite["baseMessage"];
                _exlamationPointCount = (int)composite["exlamationPointCount"];
            }
        }
    }
}

My Brain Dead Simple Windows RT App: The Series

Monday, March 04, 2013

Using App Bars to Hide Chrome

Brain Dead Simple Windows RT App Part 2

In Microsoft’s UX Guidelines for Windows Store apps, they talk about reducing “chrome” and hiding it in well known locations (to anyone who uses Windows 8). A couple of those well known locations are the top and bottom App Bars. Microsoft has very specific recommendations on where commands belong on these things. I will violate those standards and put one button on the top and one on the bottom.

App Bars are hidden UI elements that can be used to hide commands that are too chromey for the main canvas. They appear when you:

  • swipe from the top or bottom of the screen
  • right click your mouse
  • press WindowKey + Z

The Changes

The app fails to express my level of enthusiasm, which can very as time goes on. So, I propose that I add the ability to add and remove exclamation points to the greeting as my mood changes.

I will put a button to add an exclamation point to the Top App Bar (in clear violation of the UX guidelines) and a button to remove exclamation point to the Bottom App Bar.

<Page.TopAppBar>
    <AppBar>
        <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
            <Button Style="{StaticResource AddAppBarButtonStyle}" 
                Click="addPoint_Click"  />
        </StackPanel>
    </AppBar>
</Page.TopAppBar>
<Page.BottomAppBar>
    <AppBar>
        <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
            <Button Style="{StaticResource RemoveAppBarButtonStyle}" 
                Click="removePoint_Click" 
                IsEnabled="{Binding Path=IsRemovePossible}"  />
        </StackPanel>
    </AppBar>
</Page.BottomAppBar>

The styles are available in \Common\StandardStyles.xaml, you need to find them and uncomment them or copy them to MainPage.xaml’s Page.Resources (like I did):

<!-- These styles along with many other can be found in \Common\StandardStyles.xaml -->
<Style x:Key="AddAppBarButtonStyle" TargetType="ButtonBase" 
    BasedOn="{StaticResource AppBarButtonStyle}">
        <Setter Property="AutomationProperties.AutomationId" 
            Value="AddAppBarButton" />
        <Setter Property="AutomationProperties.Name" Value="Add" />
        <Setter Property="Content"                 Value="&#xE109;" />
    </Style>    
    <Style x:Key="RemoveAppBarButtonStyle" TargetType="ButtonBase" 
            BasedOn="{StaticResource AppBarButtonStyle}">
        <Setter Property="AutomationProperties.AutomationId" 
            Value="RemoveAppBarButton" />
        <Setter Property="AutomationProperties.Name" Value="Remove" />
        <Setter Property="Content" Value="&#xE108;" />
    </Style>        

So here’s the additional code (I put it at the bottom of my MainPage.xaml.cs), what is does really isn’t important for this post, it just needs to do something to prove that everything is working.

private int _exlamationPointCount = 0;

private static string setMessge(int exlamationPointCount)
{
    if (exlamationPointCount > 0)
    {
        return BASE_MESSAGE + new string('!', exlamationPointCount);
    }
    else
    {
        return BASE_MESSAGE;
    }
}

private void addPoint_Click(object sender, RoutedEventArgs e)
{
    HelloMessage = setMessge(++_exlamationPointCount);
    RaisePropertyChanged("HelloMessage");
    RaisePropertyChanged("IsRemovePossible");
}

private void removePoint_Click(object sender, RoutedEventArgs e)
{
    if (_exlamationPointCount >= 1)
    {
        HelloMessage = setMessge(--_exlamationPointCount);
        RaisePropertyChanged("HelloMessage");
        RaisePropertyChanged("IsRemovePossible");
    }
}

// Make it possible to disable Remove Point button when there
// is nothing left to remove
public bool IsRemovePossible
{
    get { return _exlamationPointCount > 0; }
}

Basically, what I’m doing is keeping track of exclamation point count in _exlamationPointCount. When the buttons are click I increment or decrement the count, make a new message with setMessge() and notify that the properties have been changed.

Complete MainPage Code Listings


MainPage.xaml

<Page
        x:Class="HelloWindows.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:HelloWindows"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">

    <Page.Resources>
    <!-- These styles along with many other can be found in 
        \Common\StandardStyles.xaml -->
        <Style x:Key="AddAppBarButtonStyle" TargetType="ButtonBase" 
                BasedOn="{StaticResource AppBarButtonStyle}">
            <Setter Property="AutomationProperties.AutomationId" 
                Value="AddAppBarButton" />
            <Setter Property="AutomationProperties.Name" Value="Add '!'" />
            <Setter Property="Content" Value="&#xE109;" />
    </Style>
    <Style x:Key="RemoveAppBarButtonStyle" TargetType="ButtonBase" 
                BasedOn="{StaticResource AppBarButtonStyle}">
            <Setter Property="AutomationProperties.AutomationId" 
                Value="RemoveAppBarButton" />
            <Setter Property="AutomationProperties.Name" Value="Remove '!'" />
            <Setter Property="Content" Value="&#xE108;" />
        </Style>        
    </Page.Resources>
    <Page.TopAppBar>
        <AppBar>
            <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
                <Button Style="{StaticResource AddAppBarButtonStyle}" 
                    Click="addPoint_Click"  />
            </StackPanel>
        </AppBar>
    </Page.TopAppBar>
    <Page.BottomAppBar>
        <AppBar>
            <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
                <Button Style="{StaticResource RemoveAppBarButtonStyle}" 
                    Click="removePoint_Click" 
                IsEnabled="{Binding  Path=IsRemovePossible}"  />
            </StackPanel>
        </AppBar>
    </Page.BottomAppBar>
    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{Binding Path=HelloMessage}" 
            Style="{StaticResource    HeaderTextStyle}" 
            HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</Page>

MainPage.xaml.cs

using System.ComponentModel; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace HelloWindows { /// <summary> /// An empty page that can be used on its own or navigated to within a Frame. /// </summary> public sealed partial class MainPage : Page, INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private const string BASE_MESSAGE = "Hello Windows"; public MainPage() { this.InitializeComponent(); HelloMessage = setMessge(_exlamationPointCount); this.DataContext = this; } private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public string HelloMessage { get; set ; } /// <summary> /// Invoked when this page is about to be displayed in a Frame. /// </summary> /// <param name="e">Event data that describes how this page was reached. /// The Parameter /// property is typically used to configure the page.</param> protected override void OnNavigatedTo(NavigationEventArgs e) { } private int _exlamationPointCount = 0; private static string setMessge(int exlamationPointCount) { if (exlamationPointCount > 0) { return BASE_MESSAGE + new string('!', exlamationPointCount); } else { return BASE_MESSAGE; } } private void addPoint_Click(object sender, RoutedEventArgs e) { HelloMessage = setMessge(++_exlamationPointCount); RaisePropertyChanged("HelloMessage"); RaisePropertyChanged("IsRemovePossible"); } private void removePoint_Click(object sender, RoutedEventArgs e) { if (_exlamationPointCount >= 1) { HelloMessage = setMessge(--_exlamationPointCount); RaisePropertyChanged("HelloMessage"); RaisePropertyChanged("IsRemovePossible"); } } // Make it possible to disable Remove Point button when there // is nothing left to remove public bool IsRemovePossible { get { return _exlamationPointCount > 0; } } } }

My Brain Dead Simple Windows RT App: The Series

Sunday, March 03, 2013

Brain Dead Simple Windows RT App

Brain Dead Simple Windows RT App Part 1

This article creates a really simple base app that I will use as a starting point for at least 1 other blog post (I’m holding this one until the first one is done).

In this series I will site design standards and then violate them. My intention is to justify why you need to learn how to use things like App Bars and Charms. At the same time, I don’t want to spend too many words trying to make everything UX correct. Microsoft has committed thousands of bits to the internet about Windows 8 UX standards on MSDN and various blogs.

Build the Brain Dead Simple App

  1. Create a new Visual C# Windows Store “Blank App (XAML)”
  2. Call it “HelloWindows”
  3. Open MainPage.xaml
  4. Within the bottom most Grid, add a text block with the Text of “Hello Windows”



I added a little style and now the grid XAML looks like this:

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock Text="Hello Windows" 
        Style="{StaticResource HeaderTextStyle}"
        HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>

Add Binding for Future Proofiness

I know that I will need to make my app a little more interactive. With this in mind, I’m going to add some really simple binding code and XAML plumbing so I can separate this from the later posts.

public sealed partial class MainPage : Page, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private const string BASE_MESSAGE = "Hello Windows";
    public MainPage()
    {
        this.InitializeComponent();

        HelloMessage = setMessge(ExlamationPointCount);
        this.DataContext = this;
    }

    private void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
AND XAML
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <TextBlock Text="{Binding Path=HelloMessage}" 
        Style="{StaticResource HeaderTextStyle}" />
</Grid>

My Brain Dead Simple Windows RT App: The Series