C# Corner

Building a Windows 8 Metro App, Part 3: Putting it Together

Eric Vogel covers how to use the Windows 8 local data storage APIs to cache application data.

Welcome to the third and final installment in the building a Windows 8 Metro App series. In Part 1, I covered how to create a basic RSS reader application. In Part 2, I went over how to implement a two-way share contract. Today I'll cover how to use local storage to persist data across application sessions and suspensions. I'll be updating the RSS reader application from the previous two articles to be able to cache an RSS feed locally. When the user loads up the application, the cached version will be retrieved and loaded if it exists. When the user loads up a new feed the application will cache it.

Windows 8 Local Storage Basics
Before I dive too deep into updating the RSS Reader application, let's go over the basics of the Windows 8 local storage story. WinRT includes several local storage methods, which are available in the Windows.Storage namespace. The most simple type of storage is the LocalSettings data store, which is well suited for storing application configuration settings and other very simple data. For example, you could store a user preference named "EnablePush" for enabling push notifications as a bool value.

Windows.Storage.ApplicationData.Current.LocalSettings.Values["EnablePush"]  = true;

The Values property is a Dictionary<string,Object> so you can store more complex objects as well. But I've run into issues trying to store, for example, a Uri in the LocalSettings.Values dictionary and would advise using it only for simple objects and value types.

To remove a local setting, you can call LocalSettings.Values.Remove and pass the key of the setting to be removed. You can also store a group of settings, referred to as a container. For example, you could create a "General" settings container with "Version" and "Registered" settings.

LocalSettings.CreateContainer("General",  ApplicationDataCreateDisposition.Always);
LocalSettings.Containers["General"].Values["Version"] = 1.0;
LocalSettings.Containers["General"].Values["Registered"] = true;

The Windows.Storage namespace also includes asynchronous file input and output operations as I'll cover later when updating the RSS reader. On to the main show.

Adding Local Storage to RSS Reader Application
To get started, open up the RSS Daily Reader application solution (found in the code download from Part 2 of this article). Add a new project to the solution and name it "VSMWinRTDemo.Storage". Now add a new class file named SettingsStorage.

SettingsStorage will be a small utility class for quickly accessing the application's local settings. It contains two methods: GetLocalSetting and SetLocalSetting, that get and set a local setting for the application. The GetLocalSetting method allows you to retrieve a type-safe version of a local setting. The SetLocalSetting method simply stores a local setting value indexed by the given key. See Listing 1 for the full code.

Now to add the LocalStorage class, which will allow storage of more complex objects across the application life-cycle. The LocalStorage class asynchronously stores a string indexed dictionary of objects to a local file. In the case of the RSS reader application, it will be used to store a cached copy of an RSSFeed object, which contains a collection of RSS feed items. First you need to add the necessary using statements for the Windows Storage, IO, Serialization and Streams access.

 using Windows.Storage;
using System.IO;
using System.Runtime.Serialization;
using Windows.Storage.Streams;

Now to add the backing string-Object dictionary to store data in memory before dumping it to file storage.

static private Dictionary<string, object>  _data = new Dictionary<string, object>();
private const string filename = "localSession.xml";
static public Dictionary<string, object> Data {
get { return _data; }
}

The GetItem<T> method allows for retrieval for a type-converted value from the local storage dictionary, similar to the GetLocalSetting method in the SettingsStorage class.

static public T GetItem<T>(string key)
{
T result = default(T);
if (_data.ContainsKey(key))
{
result = (T)_data[key];
}
return result;
}
The ContainsItem method simply checks for the presence of a given item in the data dictionary.
 static public bool ContainsItem(string key)
{
return _data.ContainsKey(key);
}

The SaveAsync method, shown in Listing 2, asynchronously creates a file and writes a serialized version of the data dictionary to the file.

RestoreAsync (Listing 3) is the opposite of SaveAsync -- it restores the data dictionary from the locally-saved file asynchronously.

The Save method calls the SaveAsync method on a background thread.

static async public Task Save()
{
    await Windows.System.Threading.ThreadPool.RunAsync((sender) =>
    {
        LocalStorage.SaveAsync().Wait();
    }, Windows.System.Threading.WorkItemPriority.Normal);
}

The Save and Restore wrapper methods are needed for the developer preview release of WinRT to get around some threading issues. Kudos to Microsoft for their excellent sample code that demonstrates the work-around fix. See Listing 4 for the full LocalStorage class code.

It's almost time to use the LocalStorage class. First, it's important to understand the Windows 8 application lifecycle. There are two application events you'll use for most applications: OnLaunched and OnSuspending. The OnLaunch event is fired when the application first loads, and when the application is resumed from a suspension. An application is usually suspended after it's been idle for a prolonged duration.

In the OnSuspending event, it's good practice to save any of your needed application state and notify the OS when you're finished. In the OnLaunched event you can check the previous application state and restore any needed state. Luckily you have the LocalStorage class ready to use, and can subscribe to the events and be done with it. Open up Application code-behind file (App.xaml.cs). Now subscribe to the Suspending event of the application in the constructor.

public App()
  {
      InitializeComponent();
      this.Suspending += new SuspendingEventHandler(App_Suspending);
  }
Next, locate the OnLaunched event and add the following code to restore the LocalStorage session if the application had been terminated previously.
  if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
 {
         await LocalStorage.Restore();
 }

Now, in the App_Suspending event, save the current application state to local storage and notify the OS.

async protected void OnSuspending(object sender, SuspendingEventArgs args)
 {
     SuspendingDeferral deferral = args.SuspendingOperation.GetDeferral();
     await LocalStorage.Save();

     deferral.Complete();
 }

See Listing 5 for the completed code.

Updating the RSS Application

Now to update the UI (see Figure 1) for the application to allow the user to set a default RSS feed, as well as save the number of RSS items to display. Open up MainPage.xaml and update the LayoutHeader grid with the markup shown in Listing 6. The new additions include a "Set as default button", a "# Items" label and associated text box, as well as a label that displays "Cached" when a cached RSS feed is loaded.

 


[Click on image for larger view.]
Figure 1. The updated Daily Reader UI

You're in the home stretch. Add a reference to the VSMWinRTDemo.Storage project to the UI project. Next, open up MainPage.xaml.cs and get ready. First add a using statement for the new Storage project.

using VSMWinRTDemo.Storage;

Then, add the local setting key constants for the default feed URL and the maximum number of feeds.

const string  DEFAULT_FEED_KEY = "DefaultFeedUri";
const string MAX_FEED_ITEMS_KEY = "MaxFeedItems";

Next, subscribe to the LostFocus event for the NumFeeds field in the MainPage class constructor.

public MainPage()
{
    ..........................
    NumFeeds.LostFocus += new RoutedEventHandler(NumFeeds_LostFocus);
}

The LostFocus event gets the current user-entered value for the NumFeeds field and stores it into the LocalSettings object through the SettingsStorage class.

void NumFeeds_LostFocus(object sender, RoutedEventArgs e)
 {
     if (!String.IsNullOrWhiteSpace(NumFeeds.Text))
     {
         _maxFeeds = int.Parse(NumFeeds.Text);
         SettingsStorage.SetLocalSetting(MAX_FEED_ITEMS_KEY, _maxFeeds);
     }
 }

Next, update the MainPage_Loaded (Listing 7) event to load the maximum number of feeds and the default URL from the SettingsStorage class. If there's a default URL, I set the FeedUrl field as well as load up the RSS feed in the application via GetFeeds.

In the DefaultButton click, set the LocalSetting for the currently entered feed URL from the FeedUrl field.

private void SetDefaultButton_Click(object sender, RoutedEventArgs e)
 {
     SettingsStorage.SetLocalSetting(DEFAULT_FEED_KEY, FeedUrl.Text);
 }

Next, add a new method named BindFeeds that sets the DataContext for the Page to the given RSSFeed. The method also caches the feed to local file storage through the LocalStorage Data dictionary indexed via the Feed URL.

private void BindFeeds(RSSFeed feeds, string url)
 {
     this.DataContext = feeds;
     LocalStorage.Data[url] = feeds;
 }

Finally, I update the GetFeeds method to see if there's a cached version of the feed first. If there is a cached copy, it's loaded; if not, a live version of the feed is retrieved. If a cached version is displayed, the Cached label is set to be displayed. If a live copy is displayed, the Cached label is hidden.

private async Task GetFeeds(string url)
 {
     RSSFeed feeds = null;

     if (LocalStorage.ContainsItem(url))
     {
         feeds = LocalStorage.GetItem(url);
         Cached.Visibility = Windows.UI.Xaml.Visibility.Visible;
     }
     else
     {
         Cached.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
         feeds = await _client.GetFeeds(url, _maxFeeds);
     }

     BindFeeds(feeds, url);
 }

See Listing 8 for the full MainPage.xaml.cs code, which should look like Figure 2.

 


[Click on image for larger view.]
Figure 2. Loading a Cached RSS Feed.

Limited Options
As you can see, there are a few local storage options available in WinRT. For storing configuration settings, using the LocalSettings store is preferred. In the current development preview of WinRT, the options for storing more complex data objects is limited to file storage options. I'm thinking that as WinRT gets closer to release that relational local data stores will become available as well. All code samples are available in the code drop.

About the Author

Eric Vogel is a Sr. Software Developer at Kunz, Leigh, & Associates in Okemos, MI. He is the president of the Greater Lansing User Group for .NET. Eric enjoys learning about software architecture and craftsmanship, and is always looking for ways to create more robust and testable applications. Contact him at vogelvision@gmail.com.

comments powered by Disqus

Reader Comments:

Sun, Jan 27, 2013 Darnesha cheap auto insurance :-) variable life insurance =-OO affordable car insurance esqbse

http://www.onlinecheapautoinsurance.net/ DOT :-) http://www.lifeinsuranceshopping.net/ DOT =-OO http://www.bestautoinsurancepolicies.net/ DOT esqbse

Thu, Jan 17, 2013 Hippie car insurance 62871 auto insurance 34814 to buy propecia %-O

http://www.aboutcarinsurancerates.com/ DOT 62871 http://www.bestautoinsurancepolicies.net/ DOT 34814 http://www.treatpatternhairloss.com/ DOT %-O

Thu, Jan 17, 2013 Bobbi car insurance vycqvl viagra 79057 online auto insurance quotes slq

http://www.aboutcarinsurancerates.com/ DOT vycqvl http://edmedsinfo.com/ DOT 79057 http://www.bestautoinsurancepolicies.net/ DOT slq

Thu, Jan 17, 2013 Nettie cheap auto insurance 8[[[ life insurance %-[ viagra afag

http://www.onlinecheapautoinsurance.net/ DOT 8[[[ http://www.mylifeinsuranceguide.net/ DOT %-[ http://edmedsinfo.com/ DOT afag

Thu, Jan 17, 2013 Finch cheap car insurance 7522 car insurance 48722 genericviagra 208

http://www.carinsuranceiseasy.com/ DOT 7522 http://www.aboutcarinsurancerates.com/ DOT 48722 http://edmedsinfo.com/ DOT 208

Thu, Jan 17, 2013 Robinson life insurance rate 778 eastwood auto insurance 114988 propecia =-D

http://www.mylifeinsuranceguide.net/ DOT 778 http://www.bestautoinsurancepolicies.net/ DOT 114988 http://www.treatpatternhairloss.com/ DOT =-D

Tue, Jan 15, 2013 Lidia cheap auto insurance dxdq life insurance 8[[[ auto insurance online xwe

http://cheapautoinsurer.net/ DOT dxdq http://www.mylifeinsuranceguide.net/ DOT 8[[[ http://www.carinsuranceiseasy.com/ DOT xwe

Thu, Feb 16, 2012 Overwhelmed and confused Out in the cold

I'm a bit confused about these Metro apps. Is this supposed to be capable of replacing the millions of complex business applications out there? Portability and mobility are obviously important for now and the future but not all corporations are capable of running their business off a [insert cleverly unique identifier here]-pad.

Thu, Feb 16, 2012 Keith Ward

Durgesh: The link's now fixed. Thanks for alerting us!

Wed, Feb 15, 2012 Durgesh Nayak India

The link for Part 2 of this series points to Part 3 i.e. the same article.

Add Your Comments Now:

Your Name:(optional)
Your Email:(optional)
Your Location:(optional)
Comment:
Please type the letters/numbers you see above

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.