(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Windows 8 Metro: Add settings to your application

(6 votes)
Andrea Boschin
>
Andrea Boschin
Joined Nov 17, 2009
Articles:   91
Comments:   9
More Articles
2 comments   /   posted on Jul 31, 2012
Categories:   Windows 8 , General
Tweet

Almost every application you write needs to expose an user interface that let the user change the configuration. In common desktop applications these settings go under a preferences section, usually available from a menu item, but for sure it is not true for all the applications, so every time you install a new software you have to rely on a manual and search a place where this configuration has been placed.

As for other features, in metro-style application this now goes under a very strict guideline checked as a strong requirement by the certification team. One thing you need to be aware is that the settings contract is used not only to expose user preferences but also is a place where you have to move your licensing terms, about and other things that are not exactly settings, in the common feel of the term.
 

Hook up the settings flyout

The very first step, to implement a settings user experience in metro, is to understand how to add your settings into the charm flyout. When you hit the charm settings button, you are presented with a flyout on the right side and usually inside this space you have a number of links that are related with the application currently running. What you can do at this point is to add a number of commands that can do one of the following:

  1. replace the content of the settings flyout with your own settings UI.
  2. navigate to a page inside your application
  3. open an external link in the browser

How to add one of these in the UI? When the user asks for the setting charm the application receives the notification of a CommandRequested event. Handling this event the developer can add a number of "commands" which will be presented as additional links in the flyout. When cliced by the user, they notifies back to the application with another event. In the following snippet I show you how to add a link to the licensing terms. First of all, in the App.xaml.cs, I hook up the CommandRequested event:

SettingsPane.GetForCurrentView().CommandsRequested += App_CommandsRequested;

Once subscribed the event, your application starts to receive notifications when someone hit the settings charm button. During this event you are requested to provide a number of SettingsCommand instances:

   1: void App_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
   2: {
   3:     args.Request.ApplicationCommands.Add(
   4:         new SettingsCommand("visit", "Visit XamlPlayground",
   5:             a =>
   6:             {
   7:                 Launcher.LaunchUriAsync(new Uri("http://www.xamlplayground.org"));
   8:             }));
   9: }

A SettingsCommand is a class which directly implements the IUICommand interface but is tailored to the settings charm. The constructor of the class requires an ID - used to discriminate between commands when the event is received - a title and a callback event handler. In my example I use a lambda expression to handle the click of the item and to launch a new instance of the browser starting my website's homepage.

As for the launch of a browser, you can also navigate to a specific page of the application. This tecnique is used extensively in the store application where settings are handled by specific pages. In the box below I show how to call a profile page:

   1: args.Request.ApplicationCommands.Add(
   2:     new SettingsCommand("profile", "Your profile",
   3:         a =>
   4:         {
   5:             ((Frame)Window.Current.Content).Navigate(typeof(ProfilePage));
   6:         }));

As a result when you hit the "Your profile" link the settings UI is closed and the new page is started. The two examples above show the most simple cases to handle settings. In their simplicity they really matter because they cover important certification requirements. Infact during certification, the team checks that about pages, licensing terms and other similar thing are collected into the settings pane and not with dedicated links inside the application.

Use your own UI in settings charm

imageAlso if previous examples cover most of the setting needs, the best is to provide a specific user interface that goes inside the settings flyout. When you receive the command you can create a Popup and attach it to the charm. This Popup can host your own custom interface elements, usually made of a single UserControl.

When you receive the command you are in charge of creating the Popup element and handle events to dismiss when the user click outside of it. Here is the code to add the element:

   1: args.Request.ApplicationCommands.Add(
   2:     new SettingsCommand("settings", "Application Settings",
   3:         a =>
   4:         {
   5:             _settingsPopup = new Popup();
   6:             _settingsPopup.Closed += OnPopupClosed;
   7:             Window.Current.Activated += OnWindowActivated;
   8:  
   9:             _settingsPopup.IsLightDismissEnabled = true;
  10:             _settingsPopup.Width = SettingsWidth;
  11:             _settingsPopup.Height = Window.Current.Bounds.Height;
  12:  
  13:             AppSettingsFlyout mypane = new AppSettingsFlyout();
  14:             mypane.Width = SettingsWidth;
  15:             mypane.Height = Window.Current.Bounds.Height;
  16:             _settingsPopup.Child = mypane;
  17:             _settingsPopup.SetValue(Canvas.LeftProperty, Window.Current.Bounds.Width - SettingsWidth);
  18:             _settingsPopup.SetValue(Canvas.TopProperty, 0);
  19:             _settingsPopup.IsOpen = true;
  20:         }));

In this snipped I create a Popup instance (in a member variable), setup width, height and other properties and finally I set the IsOpen property to make it visible. Inside the popup I add an instance of AppSettingsFlyout that is a UserControl which shows the interface on the right side. Important to see is that boundaries of the popup are handled using Windows.Current.Bounds . It expose the size of the charm flyout.

The Width of the user control is set to 346 pixels in this example but you can choose between this size and 646 pixels. They are the suggested sizes for settings flyout. Since slim size is good for stacked settings as presented in the image, a larger size may be good for complex settings interfaces that needs more space.

The lifetime of the AppSettingsFlyout user control is very similar to other controls in the application. You can load items from the network, access filesystem and do whatever you need to give a reliable settings interface. But what about saving settings?

Save your settings locally or roamed

After you created your own settings interface, you need a secure place to save your own setting. Metro-styled applications give you a secure place to store application settings both local or roamed. If understanding local settings is pretty simple, roamed settings is something not immediately clear. If you ever setup two instances of Windows 8, on different machines, but accessed them with the same account, you probably already know what I mean with the term "roamed". In this case, but also if you install and reinstall Windows 8 and use the same account, the operating system is able to synchronize across multiple instances the setting like the lock screen background, windows colors and other interesting things.

As for Windows you are able to extend this "roamed experience" to the application specific settings, with a very minimal effort, really similar to access local settings. The ApplicationData class provides two properties, LocalSettings and RoamingSettings, that contains all what you need to use both the features. In my app I usually wrap settings in a utility class:

   1: public static class SettingsStore
   2: {
   3:     public static ApplicationDataContainer SettingsContainer { get; set; }
   4:  
   5:     static SettingsStore()
   6:     {
   7:         SettingsContainer = ApplicationData.Current.LocalSettings;
   8:     }
   9:  
  10:     public static string CurrentCountry
  11:     {
  12:         get { return SettingsContainer.Values["CurrentCountry"] as string; }
  13:         set { SettingsContainer.Values["CurrentCountry"] = value; }
  14:     }
  15:  
  16:     public static string CurrentTown
  17:     {
  18:         get { return SettingsContainer.Values["CurrentTown"] as string; }
  19:         set { SettingsContainer.Values["CurrentTown"] = value; }
  20:     }
  21: }

In static constructor I assign "ApplicationData.Current.LocalSettings" to a local member variable. changing this row I may change the behavior of the application and store all the settings roamed specifying "ApplicationData.Current.RoamingSettings".

Once you wrapped settings you can use this class to read and write settings to the store in a type-safe way. It suffice you assign the properties and they are stored in the "Values" section of the store you choose. In the case you choose the roamed settings they will by synchronized across different machines but I was not able to understand exactly the timing when this synchronization happen.

Interesting to say you can handle a DataChanged event to be aware of the change of local or roamed settings and act to update the application behavior according with the new settings. This may be an hard task, and in my opinion there are cases when it may be impossible because it breaks the work of the user, but for a number of cases hot-swapping settings is an amazing feature.

In the AppSettingsFlyout control I have two methods that handle the values to load and save settings:

   1: private void LoadSettings()
   2: {
   3:     var countries = DataSource.GetCountries();
   4:     Country country = countries.Where(c => c.Name == SettingsStore.CurrentCountry).FirstOrDefault();
   5:  
   6:     this.CountryComboBox.ItemsSource = countries;
   7:     this.CountryComboBox.SelectedValue = country;
   8:     this.TownComboBox.ItemsSource = country != null ? country.Towns : null;
   9:     this.TownComboBox.SelectedValue = SettingsStore.CurrentTown;
  10: }
  11:  
  12: private void SaveSettings()
  13: {
  14:     if (CountryComboBox.SelectedValue != null)
  15:         SettingsStore.CurrentCountry = ((Country)CountryComboBox.SelectedValue).Name;
  16:     else
  17:         SettingsStore.CurrentCountry = null;
  18:  
  19:     SettingsStore.CurrentTown = TownComboBox.SelectedValue as string;
  20: }

Unfortunately you can use settings only to store primitive types like strings, integer and so on, so I cannot save directly an instance of the Country object but I need to save the name and then retrieve the instance from the collection when the settings is read. This is achieved by the linq query on the "countries" variable.

Finally inside the application you can read settings whenever you need. In this example I display the favorite town testing the CurrentCountry variable. If this variable is not null is means the settings have been assigned previously.

   1: private void LoadSettings()
   2: {
   3:     if (SettingsStore.CurrentCountry != null)
   4:         this.FavoriteTownTextBox.Text = 
   5:             string.Format("{0} => {1}", SettingsStore.CurrentCountry, SettingsStore.CurrentTown);
   6:     else
   7:         this.FavoriteTownTextBox.Text = "Unknown";
   8: }

Use settings with charm

I slightly scratched the surface when I spoken about roaming settings. These are probably the most interesting feature of the settings scope. You have to be aware of two things when you deal with these settings: first of all you have a size limit that is reported by the RoamingStorageQuota variable (about 100Kb). When the quota is exceeded nothing roams. In second instance, you have a set of tools to ensure atomicity of synchronization when there are values that must be synchronized together. This is the case of the couple country/town, where a town must only coincide with a specific country. These topics are out of the settings matter and probably they will be threated in a separate article.

Back to the settings topic you have to be aware that using them correctly not only gives a better user experience to the final user but it is also matter of successful certification because there are a number of cases that requires setting to be used.


Subscribe

Comments

  • Re: Windows 8 Metro: Add settings to your application


    posted by JohnMichaelHauck on Jul 31, 2012 17:58

    Very nicely written!

    This approach recreates many of the same problems as Microsoft sample code and Callisto.
    For example: a lot of code-behind and an inability to handle screen rotations properly.
    I enumerated all the issues here: The Quest for a Settings Flyout.

    Consider employing the user control found in the Windows Dev Center:
    http://code.msdn.microsoft.com/windowsapps/CharmFlyout-A-Metro-Flyout-25fe53b6.

  • KelvinDavies

    Re: Windows 8 Metro: Add settings to your application


    posted by KelvinDavies on Oct 22, 2012 16:00

    Great article, thank you.

Add Comment

Login to comment:
  *      *       

From this series