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

Windows Store apps in HTML and XAML: Handle settings

(3 votes)
Andrea Boschin
>
Andrea Boschin
Joined Nov 17, 2009
Articles:   91
Comments:   9
More Articles
0 comments   /   posted on Jul 01, 2013
Categories:   Windows 8

Tweet

Almost every application has some kind of settings that enable the user to customize his experience, tuning the application behavior to his preferences. This is an important matter and, also if often settings fall under a small set of known locations, at the first sight it is not easy to understand where they are for an untrained user. Tools->Options, Edit->Preferences, or some strange settings button that is presented somewhere in the user interface. These and other are common locations where we are use to see, a little bit after having installed a new software and we simply need to change a unit measure, a font size and something else.

With Windows 8 Store apps, they just simplified the user experience with settings, determining by guidelines a well-known position where these useful handles must be placed. Please note I used the term "must", underlining that this is not something under our choice, but a sort of pillar that is strictly required by Windows Store certification. So, every time you need to create a settings interface you have to rely on the right-placed flyout that is usually opened with the settings charm, or simply pressing CTRL+I (at least on my machine).

Not only for settings.

If you have already used a Windows Store app, you are aware that the settings experience is not strictly related with settings only. Tipically the interaction with settings pane is a two-step experience. First you trigger the settings by using the charms. The system answer opening a side panel where you can choose between a number of items. When you make a choice, the flyout changes, showing the settings you can manipulate. Also from the developer side, implementing the settings imply to support these two steps. First you have to populate the item in the first view, attaching a number of items. Then, answering to the click of one of these items you have to present the settings panel of do whatever you need to do related to the user choice.

As a matter of facts, the first panel not only contains the access to settings groups, but it is done also to hook up information about the application. Legal notices, privacy policies, about box and web site, contacts and support links. All these item must but set in the settings pane.

In this first part, the developer experience for html and xaml programmers is similar. The trick is made attaching an event that is raised when the user asks to open the panel with the settings charm. This event require you to pass a list of actions that becomes links. Here is the XAML code and then the HTML example:

   1: sealed partial class App : Application
   2: {
   3:     private bool hasSettings = false;
   4:  
   5:     protected override void OnWindowCreated(WindowCreatedEventArgs args)
   6:     {
   7:         if (!this.hasSettings)
   8:         {
   9:             SettingsPane.GetForCurrentView().CommandsRequested += Settings_CommandsRequested;
  10:             this.hasSettings = true;
  11:         }
  12:     }
  13:  
  14:     private void Settings_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
  15:     {
  16:         SettingsCommand aboutCommand = new SettingsCommand("aboutId", "About", HandleSettingsClick);
  17:         args.Request.ApplicationCommands.Add(aboutCommand);
  18:  
  19:         SettingsCommand helpCommand = new SettingsCommand("helpId", "Help", HandleSettingsClick);
  20:         args.Request.ApplicationCommands.Add(helpCommand);
  21:  
  22:         SettingsCommand settingsCommand = new SettingsCommand("settingsId", "Settings", HandleSettingsClick);
  23:         args.Request.ApplicationCommands.Add(settingsCommand);
  24:     }
  25:  
  26:     private void HandleSettingsClick(IUICommand command)
  27:     {
  28:  
  29:     }
  30: }

This code placed in the Application startup handles the moment when the window is created. This let the setting to be in place every time when the application is started for some reason. In the OnWindowCreated method I use a bool flag to avoid duplicated hook that may happen. When the event is raised, a number of SettingsCommand are created and added to the receiver EventArg. This event is raised a bit after the user asked for the settings panel. In HTML the things appear pretty similar:

   1: // this goes in default.js
   2:  
   3: WinJS.Application.onsettings = 
   4:     function (e) 
   5:     {
   6:         e.detail.applicationcommands =
   7:             {
   8:                 "aboutId": { title: "About", href: "/html/Help.html" },
   9:                 "helpId": { title: "Help", href: "/html/About.html" },
  10:                 "settingsId": { title: "Setting", href: "/html/Settings.html" }
  11:             };
  12:  
  13:         WinJS.UI.SettingsFlyout.populateSettings(e);
  14:     };
  15:  

The important thing in this declaration is the identifier given to the command. This is double true in Javascript and HTML because it automatically hooks up the html fragment to be displayed in the settings pane. In XAML instead this identifier is useful but the developer gets the entire IUICommand instance and is also able to attach a different event to each command.

If you need, in Javascript you can also attach the event handler to the command button. This is useful if you need to start an external link. To do this the syntax is slightly different:

   1: // this goes in default.js
   2:  
   3: WinJS.Application.onsettings = 
   4:     function (e) 
   5:     {
   6:         e.detail.e.request.applicationCommands.append(
   7:             new Windows.UI.ApplicationSettings.SettingsCommand("privacyId", "Privacy Policy", 
   8:                 function() 
   9:                 {
  10:                     // do what you want when clicked!
  11:                 }));
  12:  
  13:         WinJS.UI.SettingsFlyout.populateSettings(e);
  14:     };

Displaying the settings

After loading the choices, you may expect the user hits one of your links. This lead to a number of actions. In the most simple case you open an external browser to display a web page, as in the case of privacy policies or credits. But when the user asks for internal application settings, these must be displayed into your app following some simple rules. The opened panel may have two sizes: 346 pixels in the normal case and 646 when it is extended. The panel must reside on the right side as usual and it should have a back button, in the left top corner, to return to the previous pane.

Working with XAML this operation requires some more work than in HTML. This because in HTML the system automatically handle to construction and position of the settings panel but in XAML you are in charge of doing this operation. So in XAML, when in the setting command handler you have first to create a Popup instance and place it on the right side.

   1: private void Settings_CommandsRequested(IUICommand command)
   2: {
   3:     Rect boundaries = Window.Current.Bounds;
   4:     const double settingsWidth = 646d;
   5:  
   6:     thePopup = new Popup();
   7:     thePopup.Closed += HandlePopupClosed;
   8:     Window.Current.Activated += HandleWindowActivated;
   9:     thePopup.IsLightDismissEnabled = true;
  10:     thePopup.Width = settingsWidth;
  11:     thePopup.Height = boundaries.Height;
  12:  
  13:     // Add the proper animation for the panel.
  14:     thePopup.ChildTransitions = new TransitionCollection
  15:     {
  16:         new PaneThemeTransition() 
  17:         { 
  18:             Edge = (SettingsPane.Edge == SettingsEdgeLocation.Right) ? EdgeTransitionLocation.Right : EdgeTransitionLocation.Left
  19:         }
  20:     };
  21:  
  22:     // Create a SettingsFlyout the same dimenssions as the Popup.
  23:     SettingsFlyout mySettingsControl = new SettingsFlyout
  24:     {
  25:         Width = settingsWidth,
  26:         Height = boundaries.Height
  27:     };
  28:  
  29:     // Place the SettingsFlyout inside our Popup window.
  30:     thePopup.Child = mySettingsControl;
  31:  
  32:     Canvas.SetLeft(thePopup, SettingsPane.Edge == SettingsEdgeLocation.Right ? (boundaries.Width - settingsWidth) : 0);
  33:     Canvas.SetTop(thePopup, 0);
  34:  
  35:     thePopup.IsOpen = true;
  36: }
  37:  
  38: void HandlePopupClosed(object sender, object e)
  39: {
  40:     thePopup.Closed -= HandlePopupClosed;
  41:     Window.Current.Activated -= HandleWindowActivated;
  42: }
  43:  
  44: private void HandleWindowActivated(object sender, Windows.UI.Core.WindowActivatedEventArgs e)
  45: {
  46:     if (e.WindowActivationState == Windows.UI.Core.CoreWindowActivationState.Deactivated)
  47:     {
  48:         thePopup.IsOpen = false;
  49:     }
  50: }

As you can see, positioning the flyout is matter of measuring the available space and tuning the size. The horizontal size is fixed (in this case 646pixel) but the vertical size is based on the windows boundaries. You have also to connect the dismiss events to close the popup when the user clicks away.  In HTML you only have to correctly refer to an application command id:

   1: <!doctype HTML>
   2: <html>
   3:     <head>
   4:         <title>App settings flyout</title>
   5:         <script src="/js/Settings.js"></script>
   6:     </head>
   7:     <body>
   8:  
   9:         <!-- BEGIN -->
  10:  
  11:         <div data-win-control="WinJS.UI.SettingsFlyout" id="programmaticInvocationSettingsFlyout" 
  12:             aria-label="App Settings Flyout" 
  13:             data-win-options="{settingsCommandId:'settingsId', width:'wide'}">
  14:             <div class="win-ui-dark win-header">
  15:                 <button type="button" onclick="WinJS.UI.SettingsFlyout.show()" class="win-backbutton"></button>
  16:                 <div class="win-label">Settings</div>
  17:             </div>
  18:             <div class="win-content">
  19:                 <div class="win-settings-section">
  20:                     <h3>GPS tracking</h3>
  21:                     <p>Setup GPS tracking options.</p>
  22:                     <div data-win-control="WinJS.UI.ToggleSwitch" 
  23:                          data-win-options="{title:'Enable Continuous tracking', checked:true}">
  24:                     </div>
  25:                 </div>
  26:             </div>
  27:         </div>
  28:  
  29:         <!-- END -->
  30:  
  31:     </body>
  32: </html>

In this file, it is set the data-win-options attribute with the correct settingsCommandId. This suffice because the runtime understand that it has to load the page and display it into the right placed popup. You are in charge of positioning the back button and, into the onclick event, calling the "WinJS.UI.SettingsFlyout.show()" method to dismiss the flyout. In XAML you have to create also the settings control, usually an UserControl:

   1: <Border BorderBrush="#00b2f0" BorderThickness="1,0,0,0">
   2:     <Grid Background="White" VerticalAlignment="Stretch">
   3:        
   4:         <Grid.RowDefinitions>
   5:             <RowDefinition Height="80"/>
   6:             <RowDefinition Height="*" />
   7:         </Grid.RowDefinitions>
   8:  
   9:         <Grid Background="#00b2f0" Grid.Row="0">
  10:             <Grid.ColumnDefinitions>
  11:                 <ColumnDefinition Width="*" />
  12:             </Grid.ColumnDefinitions>
  13:  
  14:             <StackPanel Orientation="Horizontal" Grid.Column="0" Margin="40, 32, 17, 13">
  15:                 <Button Click="MySettingsBackClicked" Margin="0,3,0,0" Style="{StaticResource SettingsBackButtonStyle}"/>
  16:                 <TextBlock Margin="10,0,0,0" FontFamily="Segoe UI" FontWeight="SemiLight" FontSize="24.6667" Text="Defaults" Foreground="White"/>
  17:             </StackPanel>
  18:         </Grid>
  19:  
  20:         <ScrollViewer VerticalScrollBarVisibility="Auto" Grid.Row="1">
  21:             <Grid Margin="40,33,40,39" VerticalAlignment="Top" Grid.RowSpan="3">
  22:                 <StackPanel x:Name="FlyoutContent">
  23:                     <TextBlock FontWeight="Bold" Text="GPS tracking" TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left"/>
  24:                     <TextBlock Text="Setup GPS tracking options." TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left"/>
  25:                     <ToggleSwitch Margin="-6,25, 0, 0" Header = "Enable Continuous tracking" HorizontalAlignment="Left" HorizontalContentAlignment="Left"/>
  26:                 </StackPanel>
  27:             </Grid>
  28:         </ScrollViewer>
  29:     </Grid>
  30: </Border>

In the codebehind the code to dismiss the pane is this:

   1: private void MySettingsBackClicked(object sender, RoutedEventArgs e)
   2: {
   3:     Popup parent = this.Parent as Popup;
   4:  
   5:     if (parent != null)
   6:         parent.IsOpen = false;
   7:  
   8:     if (Windows.UI.ViewManagement.ApplicationView.Value != Windows.UI.ViewManagement.ApplicationViewState.Snapped)
   9:         SettingsPane.Show();
  10: }

Programmatically open the pane.

Also if charms is the required access point to the settings pane, you can also allow another way to pop up the same pane. This is enabled calling a method thaat invokes the pane and opens it. Here is how call the method in C# and Javascript:

   1: // in C# 
   2:  
   3: SettingsPane.Show();
   4:  
   5: // in Javascript
   6:  
   7: WinJS.UI.SettingsFlyout.showSettings("settingsId", "/html/Settings.html");

The slight difference is that in Javascript you can specify the settings page to load but in C# this is not availale. You can only open the setting page and then the user have to select the command to navigate.


Subscribe

Comments

No comments

Add Comment

Login to comment:
  *      *       

From this series