| Comments

In a previous post I talked about removing the dependency on StandardStyles.xaml from your Windows 8.1 projects.  One of those tips noted that a popular use of the styles in that helper file was the AppBarButton styles and the roughly 200 different glyphs used for AppBarButtons.  I thought I’d dig a little deeper quickly on the use of AppBarButton since I received a few pieces of email about it.

Typical Scenario – Symbol Icons

The AppBarButton class is new in Windows 8.1 and primarily intended for use the CommandBar scenario, providing the UI, behavior and accessibility needs to the control. 

NOTE: CommandBar is new in Windows 8.1 and meant to serve as the best-practices control for AppBars that are non-custom and use only command buttons.  It provides automatic sizing logic (meaning commands will drop as the window gets smaller) built-in as well as better keyboarding support for navigating through the commands in the AppBar.  If you are using AppBar and only have the ‘standard’ button UI, you should be using CommandBar instead in your Windows 8.1 app.

It allows you to rapidly create the UI by setting two properties: Label and Icon.  Here’s a quick example:

<AppBarButton Label="Reply via Email" Icon="MailReply" />

which produces:

Reply via Email icon image

That’s it.  The functionality still remains to be a button and the API surround that type.  This just gives you a quick way to set the label and visual.  All the visual states (pressed/hover/etc) are taken care of for you.  We’ve also pushed through the Label property to be the AutomationProperties.Name value for the button for you, providing accessibility by default here.  This Icon property method is the shortcut version of this more verbose method:

<AppBarButton Label="Reply via Email">
    <AppBarButton.Icon>
        <SymbolIcon Symbol="MailReply" />
    </AppBarButton.Icon>
</AppBarButton>

As you can see the attached property for ButtonBase is where the Icon property lives.  Because we have this Icon property we can use other methods as well.  We also expose an IsCompact property on the AppBarButton types that will shrink the margins and drop the labels automatically for you for a smaller view.  This is what the CommandBar sets when the Window size gets too small to fit the elements.

Using other custom fonts as Icon sources

If one of the 190 included Icon types does not suit your needs you can still use the AppBarButton with your own custom icons.  I HIGHLY recommend using the Font approach as it provides a great automatic scaling method for your icons and seems to be the direction of a lot of apps on the web as well.  While you can make your own Symbol font, there are also many great providers out there that you can use.

NOTE: I am not a lawyer and you should consult the font provider’s license for acceptable use policies/rights/restrictions on using fonts that are embedded in your application.  Don’t assume that every font is acceptable to use in this manner just because it is TTF/OTF – be sure to consult the license if you are not the font author.

Let’s say I want to add some social media commands to my app.  I can use the popular Modern UI Icons library, which happens to provide a font download of the social icons created.  Once unzipped I include the TTF file in my project (I could have used OTF but since TTF is provided I’ll use that – also note I renamed the file in my VS project) and then can use in my app:

<AppBarButton Label="Tweet This">
    <AppBarButton.Icon>
        <FontIcon FontFamily="ms-appx:///modernuiicons.ttf#Modern-UI-Icons---Social" 
            Margin="0,2,0,0" Glyph="&#xe045;" FontSize="37.333" />
    </AppBarButton.Icon>
</AppBarButton>
<AppBarButton Label="Review on Yelp">
    <AppBarButton.Icon>
        <FontIcon FontFamily="ms-appx:///modernuiicons.ttf#Modern-UI-Icons---Social" 
            Margin="0,2,0,0" Glyph="&#xe04f;" FontSize="37.333" />
    </AppBarButton.Icon>
</AppBarButton>

which produces:

Modern UI Icon sample usage

Pretty cool.  Now when using Fonts sometimes it is tricky to figure out the right value to set after the “#” symbol in the FontFamily path.  The trick I use on Windows is to open the TTF/OTF file and it will show the font preview as well as the “Font name” in the very first.  This is the value you want to specify after the path to the actual file.  The Glyph value you supply is totally dependent upon the font you choose.  Most authors use Unicode value ranges for symbols, but I’ve seen some that would be as simple as “B” that you could put there.  This is something you should consult the font provider for a mapping (Modern UI Icons provides a nice web page preview with all the values next to the icons).  Also notice that some font information in the font may vary and you may have to do some adjustments.  For me, I found I had to nudge the Modern UI Icons 2px down as well as size up the FontSize value to my liking.

Other variations of Icon

There are two other variations of Icons you can use.  I recommend using the default Symbol method first as almost always you’ll find something you need in that 190 set.  Second I would recommend looking at the Font approach.  These last two have some caveats that aren’t as “drop in” simple to use usually and greatly depend on the graphic data provided.

  • PathIcon – this is for providing vector data using the XAML Path data format.  This is vector and will scale nicely.  You must, however, provide the vector data already sized to 40px so it fits within the template.  For some this can be difficult if just provided some data.  Using tools like Inkscape or Snycfusion Metro Studio may help.
  • BitmapIcon – this works for providing PNG based images.  Note that these will NOT automatically scale.  You would want to provide the various scale-factor images (100,140,180) for each image provided here.  This can be a cumbersome task and if not done won’t look well on higher resolution displays.  Additionally BitmapIcon doesn’t yield great fidelity for non-rectangular items.

Using Visual Studio

In Visual Studio 2013 some great tooling around this experience was provided so you can easily pick out visuals for your AppBar buttons.  In the property pane for the designer you can see the various properties you can set:

Visual Studio AppBarButton Icon tooling image

There is also tooling for changing to the other types of icons mentioned above as well.

Summary

We wanted to make some things simpler, reduce VS template-provided code and improve app usability/accessibility.  While this feature may seem simple, it is useful and can help you focus on the core of your application rather than some fundamental pieces.  If you are using other button styles and some of the more standard icons, I encourage you to move to AppBarButton with the default Icon types provided by the framework.  Be sure to check out the AppBar Windows SDK Sample for playing around with the concepts discussed in this post.

Hope this helps!

| Comments

This is another post in my series of providing migration tips from certain Callisto controls to using Windows 8.1 features. I previously demonstrated probably the most popular Callisto control, the SettingsFlyout. Coming in a very close second in popularity is the Flyout control. The Flyout is a concept of a non-modal small dialog for information and commands.

Flyout sample image

The primary use case for a lot of Flyouts was something from Button areas, namely the AppBar. Getting the experience right was not intuitively easy using a Popup primitive as you had to handle the right UI guidelines for animation, positioning and dismiss logic. Callisto provided most of this in the Flyout class but also left some people wanting a bit more flexibility. This post will aim to help you migrate existing Callisto Flyout code to the new Windows 8.1 Flyout class.

API Differences

As I did with showing some of the more prominent API differences in SettingsFlyout, I’m presenting some of the important differences for Flyout here. You should read this table as Callisto API==old, Windows 8.1 API==new and what you should use.

Callisto APIWindows 8.1 APIComments
HorizontalOffset/VerticalOffsetn/aNot really needed in almost all default cases. If you really needed to adjust the default logic, you could provide a template for FlyoutPresenter and change the margin there.
HostMarginn/aNot needed
HostPopupn/aNot needed
IsOpenShowAttachedFlyoutSee explanation below
PlacementPlacementSame concept, different enum
PlacementTargetShowAttachedFlyoutSee explanation below
ClosedClosed
n/aOpenedNew event
n/aOpeningNew event

Changing your code – an example

Similar to previous examples, I’m going to use the Callisto test app examples here. Flyout in Callisto was another control that didn’t work well in markup. This section will show how you would change your existing code if you were using the Callisto method to use the new Flyout in Windows 8.1. After this section I’ll explain a better way to do this for most cases and the preferred way to use the control. However again I want to provide a “least code change” mechanism to migrate and will do so here.

In Callisto, you most likely wrote code to trigger opening a Flyout. Here’s an example taken from the test app (this was from a Button click event):

private void ShowFlyoutMenu2(object sender, RoutedEventArgs e)
{
    Callisto.Controls.Flyout f = new Callisto.Controls.Flyout();
 
    // content code removed for brevity
    // assume "b" variable here represents a visual tree or a user control
    f.Content = b;
 
    f.Placement = PlacementMode.Top;
    f.PlacementTarget = sender as UIElement;
    
    f.IsOpen = true;
}

The code here basically requires you to wire this up in an event handler and provide the UIElement as the PlacementTarget so it knows where to position the Flyout.

To change this here is what it would look like in Windows 8.1:

private void ShowFlyoutMenu2(object sender, RoutedEventArgs e)
{
    Flyout f = new Flyout();
 
    // again for brevity sake assume "b" here represents content
    f.Content = b;
 
    Flyout.SetAttachedFlyout(sender as FrameworkElement, f);
    Flyout.ShowAttachedFlyout(sender as FrameworkElement);
}

The key here is the SetAttachedFlyout/ShowAttachedFlyout method calls. You must first attach the Flyout to the FrameworkElement (again in this case this is on a Button). Then you show it. I’ve omitted the Placement here to allow for the new default (top) to occur. You could have also added Placement to the change as well. Placement will attempt to fit in this order: Top, Bottom, Left, Right.

The above is meant to demonstrate how to quickly change from Callisto code with minimal impact. The next section actually shows a preferred way of using the control and what a lot of Callisto users were actually asking for.

A better way to use Flyout

As demonstrated the Flyout can still be called from code after attaching it to a FrameworkElement. You are then responsible for calling the ShowAttachedFlyout method to open it. The Windows 8.1 Flyout was designed for the primary use case of ButtonBase elements and will automatically show for you when used in those cases. Let’s assume an example where I have a Button in my AppBar (btw, you should use the new AppBarButtons in Windows 8.1) and I want to show a Flyout when the user clicks on the button. I could do the event model above, but my MVVM friends are cringing a bit. Here’s an alternate and the most likely way you would use Flyout:

<AppBarButton Icon="Add" Label="Add File">
    <AppBarButton.Flyout>
        <Flyout>
            <!-- content here -->
        </Flyout>
    </AppBarButton.Flyout>
</AppBarButton>

As you can see here, on Button we’ve provided an attached property to put the Flyout element and its content. The framework will handle the showing of the Flyout when automatically attached to the Button like this. If you don’t want to use the Button method and perhaps you have to launch a Flyout from some other UIElement, you would use the ShowAttachedFlyout method as demonstrated above.

Summary

As one of the more popular controls this migration may take you down the quick route first and then give you more time to think about using the declarative way to really change later. I recommend using the new Flyout here as you will get all the proper behavior as well as better performance, accessibility and interaction with the software keyboard. We also have a new Windows 8.1 Flyout SDK Sample that walks through this usage and some other scenarios you can examine for your needs.

Hope this helps!

| Comments

I was helping a friend today doing some over-the-shoulder code review and suggestions for his Windows app he’s writing for the store.  In doing this I asked a question about how to perform a certain action.  He indicated that he put those functions in the AppBar and was it not obvious I was supposed to use them.  I looked at the AppBar again and found out why I wasn’t drawn to them.  First, the labels he used weren’t descriptive to me and relied too much on me looking at the actual icon.  Second, however, is that when I hovered over them I received no visual feedback I was expecting and immediately thought they were just disabled (the color was slightly gray as well which probably didn’t help this).

I mentioned these to him and noted he should use the AppBarButtonStyle base definition that comes in the Visual Studio templates as a guide and just set the appropriate content.  He proceeded to let me know that he received the vector data from Syncfusion’s Metro Studio product.  We then began to examine the vector data.  Metro Studio is doing probably too much than it needs to for AppBar button definitions.  To be fair, it serves a goal to get Windows UI style icons for the developers in XAML form.  This is great!  However, if my intention is to use them for AppBar buttons, then it is doing too much.  It led me down a path to see how easy it was to use vector data with the default AppBarButtonStyle definition.  Turns out it wasn’t as simple as I thought.  Let me first explain the core issue then walk through a step on how to do this with vector data.

Fill versus Foreground

The main culprit is Fill vs. Foreground.  The default AppBarButtonStyle has the different visual states for setting the correct default/inverted colors for your icon within the ellipse.  These visual states however set these colors based on changing the Foreground property of the ContentPresenter.  When your content is text – as is the case with the default styles that are commented out – this works fine.  However the Path element in XAML understands Fill and Foreground doesn’t really apply.  So while the vector data could be represented in the icon location, it wasn’t working on the different states because the Foreground value changing had no effect!

Let’s see how we can accomplish this…

Step 1: Get your vector data

I mentioned that we discovered this using Syncfusion’s Metro Studio, but this is one way where you can get vector-based artwork for XAML.  Another is http://xamlproject.com which is similar in how it does things.  Both will give you vector data based on a selected icon.

NOTE: The one thing I don’t like about The XAML Project site is the sizer doesn’t tell me what size I’m using.  Metro Studio gave me the option to set a specific size.

When we launch Metro Studio we can search for different items based on keywords.  Since the Segoe UI Symbol font includes a ton of these for us (all of which are represented in a commented-out style definition in StandardStyles.xaml you can just uncomment and use) I will pick an obscure one called “wash” from Metro Studio.  When I select to edit it I’m presented with a default UI to modify the data:

Metro Studio user interface

Now notice how it gives me options for background shapes, etc.?  For an AppBar button you do not these shapes so you can uncheck that option.  Secondarily the ideal size is 20px with no padding.  The colors (background color and icon color) don’t really matter here as we aren’t going to use them to get the default AppBar button style.  My final options look like this:

Metro Studio user interface

Notice I unchecked the Background Shapes option, chose Custom Size and entered 20 with 0 padding.

Now what I do is click the XAML button and am presented with the full definition.  You don’t need it all.  Just copy only the path Data to your clipboard or wherever you can get it later.

Metro Studio path data export

Now that we have our vector data we need to put it in our button.

Step 2: Creating the path button definition

Because of the Fill versus Foreground issue we can’t just simply use the AppBarButtonStyle as the button style and just set our vector data.  We will not receive the correct state visuals when it is used.  So we first need to re-define a base that we can use.  Since I may use other vector data in the future, I’ll encapsulate this in a new base style I’ll call PathBasedAppBarButtonStyle.  This will allow me to remap Foreground to my Fill property to achieve the desired outcome.  The resulting style is this:

   1: <Style x:Key="PathBasedAppBarButtonStyle" BasedOn="{StaticResource AppBarButtonStyle}" TargetType="ButtonBase">
   2:     <Setter Property="ContentTemplate">
   3:         <Setter.Value>
   4:             <DataTemplate>
   5:                 <Path Width="20" Height="20" 
   6:             Stretch="Uniform" 
   7:             Fill="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}" 
   8:             Data="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
   9:             </DataTemplate>
  10:         </Setter.Value>
  11:     </Setter>
  12: </Style>

Notice that for Fill and Data I’m using Binding with the RelativeSource being the TemplatedParent…the actual Button.  This allows me to essentially get at those properties that will be set and put them in my Path element that makes up the container for the vector data.

Step 3: Setting our custom vector data

Using the exported definition from my “wash” icon above, I can now use the style in my AppBar resulting in something like this:

   1: <Page.BottomAppBar>
   2:     <AppBar IsOpen="True">
   3:         <Button Style="{StaticResource PathBasedAppBarButtonStyle}" 
   4:                 AutomationProperties.Name="Wash" AutomationProperties.AutomationId="WashAppBarButton"
   5:                 Content="M9.6803506,4.8474294C10.209365 ... 3.5652149,0z" />
   6:     </AppBar>
   7: </Page.BottomAppBar>

Please note that I trimmed the Path data so as not to take up so much space for your reading.  I set my Button to use the base style, then set my label (via AutomationProperties.Name) and the Content is the exported vector data from the tool.

NOTE: AutomationProperties.Name is used here in the base AppBarButtonStyle to enable us to expose the Content property as the icon and still have a label.  By using this property you also get accessibility for free in using these styles for this particular instance.  Screen readers will read out the name you set when using this.

The resulting visual is correct in the AppBar UI guidelines:

Wash AppBar Icon displayed

Now when my users mouse-hover or press this button the correct visuals and state changes will happen.

Summary

The default AppBarButtonStyle base definition provided from the Visual Studio templates is optimized for the 150+ styles provided in StandardStyles.xaml and font-based vector data.  For most, this is likely more than enough to use the same icons that are used throughout the operating system to give a consistent feel.  If you find one in the 150+ styles provided you should use that.  However if there is something entirely custom or not available in those, you can use your own vector data to provide your AppBar icon.  Using fonts and vectors make it easy to scale up to the different resources without having to provide different images if you used that method.  Defining a new base for vector data isn’t difficult and provides us the flexibility when we need it!

Hope this helps!

| Comments

If you’ve been playing around with the Windows 8 Consumer Preview then hopefully you’ve seen the hundreds of samples provided and downloaded some apps form the store.  In a lot of those applications you’ll notice the common theme of the use of an AppBar…the command bar that shows when you swipe from the top or bottom of the screen.  You can also invoke the AppBar by right-clicking or using the Windows 8 keyboard shortcut of Windows key + ‘z’ to bring it up as well.

Bing Maps AppBar
Picture of the AppBar in the Bing Maps application

Most of the applications use the standard AppBar button concepts that clicking invokes some action for the app.  There may be times, however, where that button serves as a “toggle” to something in your app.  Using the Bing Maps app as an example, the “Show Traffic” command is actually a toggle command that, well, shows traffic.  You want to give your users some visual indication whether that toggle selection is on/off.  This can be accomplished in XAML by using 2 buttons and toggling their visibility, but that may be overkill for what you simple want to do.

The Visual Studio project templates provide a set of styles for the AppBar for you, but more importantly a base style AppBarButtonStyle that serves as the core design.  Altering this just slightly and you can have a ToggleAppBarButtonStyle for your project.  Here’s the style for that base:

   1: <Style x:Key="ToggleAppBarButtonStyle" TargetType="ToggleButton">
   2:     <Setter Property="Foreground" Value="{StaticResource AppBarItemForegroundBrush}"/>
   3:     <Setter Property="VerticalAlignment" Value="Stretch"/>
   4:     <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
   5:     <Setter Property="FontWeight" Value="Normal"/>
   6:     <Setter Property="FontSize" Value="21.333"/>
   7:     <Setter Property="AutomationProperties.ItemType" Value="AppBar ToggleButton"/>
   8:     <Setter Property="Template">
   9:         <Setter.Value>
  10:             <ControlTemplate TargetType="ToggleButton">
  11:                 <Grid Width="100" Background="Transparent">
  12:                     <StackPanel VerticalAlignment="Top" Margin="0,14,0,13">
  13:                         <Grid Width="40" Height="40" Margin="0,0,0,5" HorizontalAlignment="Center">
  14:                             <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0" Foreground="{StaticResource AppBarItemBackgroundBrush}"/>
  15:                             <TextBlock x:Name="BackgroundCheckedGlyph" Visibility="Collapsed" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0" Foreground="{StaticResource AppBarItemForegroundBrush}"/>
  16:                             <TextBlock x:Name="OutlineGlyph" Text="&#xE0A7;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0"/>
  17:                             <ContentPresenter x:Name="Content" HorizontalAlignment="Center" Margin="-1,-1,0,0" VerticalAlignment="Center"/>
  18:                         </Grid>
  19:                         <TextBlock
  20:                         x:Name="TextLabel"
  21:                         Text="{TemplateBinding AutomationProperties.Name}"
  22:                         Margin="0,0,2,0"
  23:                         FontSize="12"
  24:                         TextAlignment="Center"
  25:                         Width="88"
  26:                         MaxHeight="32"
  27:                         TextTrimming="WordEllipsis"
  28:                         Style="{StaticResource BasicTextStyle}"/>
  29:                     </StackPanel>
  30:                     <Rectangle
  31:                         x:Name="FocusVisualWhite"
  32:                         IsHitTestVisible="False"
  33:                         Stroke="{StaticResource FocusVisualWhiteStrokeBrush}"
  34:                         StrokeEndLineCap="Square"
  35:                         StrokeDashArray="1,1"
  36:                         Opacity="0"
  37:                         StrokeDashOffset="1.5"/>
  38:                     <Rectangle
  39:                         x:Name="FocusVisualBlack"
  40:                         IsHitTestVisible="False"
  41:                         Stroke="{StaticResource FocusVisualBlackStrokeBrush}"
  42:                         StrokeEndLineCap="Square"
  43:                         StrokeDashArray="1,1"
  44:                         Opacity="0"
  45:                         StrokeDashOffset="0.5"/>
  46:  
  47:                     <VisualStateManager.VisualStateGroups>
  48:                         <VisualStateGroup x:Name="CommonStates">
  49:                             <VisualState x:Name="Normal"/>
  50:                             <VisualState x:Name="PointerOver">
  51:                                 <Storyboard>
  52:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
  53:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemHoverBackgroundBrush}"/>
  54:                                     </ObjectAnimationUsingKeyFrames>
  55:                                 </Storyboard>
  56:                             </VisualState>
  57:                             <VisualState x:Name="Pressed">
  58:                                 <Storyboard>
  59:                                     <DoubleAnimation
  60:                                     Storyboard.TargetName="OutlineGlyph"
  61:                                     Storyboard.TargetProperty="Opacity"
  62:                                     To="0"
  63:                                     Duration="0"/>
  64:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
  65:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundBrush}"/>
  66:                                     </ObjectAnimationUsingKeyFrames>
  67:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
  68:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundBrush}"/>
  69:                                     </ObjectAnimationUsingKeyFrames>
  70:                                 </Storyboard>
  71:                             </VisualState>
  72:                             <VisualState x:Name="Disabled">
  73:                                 <Storyboard>
  74:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Foreground">
  75:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundBrush}"/>
  76:                                     </ObjectAnimationUsingKeyFrames>
  77:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
  78:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundBrush}"/>
  79:                                     </ObjectAnimationUsingKeyFrames>
  80:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Foreground">
  81:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemDisabledForegroundBrush}"/>
  82:                                     </ObjectAnimationUsingKeyFrames>
  83:                                 </Storyboard>
  84:                             </VisualState>
  85:                         </VisualStateGroup>
  86:                         <VisualStateGroup x:Name="FocusStates">
  87:                             <VisualState x:Name="Focused">
  88:                                 <Storyboard>
  89:                                     <DoubleAnimation
  90:                                         Storyboard.TargetName="FocusVisualWhite"
  91:                                         Storyboard.TargetProperty="Opacity"
  92:                                         To="1"
  93:                                         Duration="0"/>
  94:                                     <DoubleAnimation
  95:                                         Storyboard.TargetName="FocusVisualBlack"
  96:                                         Storyboard.TargetProperty="Opacity"
  97:                                         To="1"
  98:                                         Duration="0"/>
  99:                                 </Storyboard>
 100:                             </VisualState>
 101:                             <VisualState x:Name="Unfocused" />
 102:                             <VisualState x:Name="PointerFocused" />
 103:                         </VisualStateGroup>
 104:                         <VisualStateGroup x:Name="CheckStates">
 105:                             <VisualState x:Name="Checked">
 106:                                 <Storyboard>
 107:                                     <DoubleAnimation
 108:                                     Storyboard.TargetName="OutlineGlyph"
 109:                                     Storyboard.TargetProperty="Opacity"
 110:                                     To="0"
 111:                                     Duration="0"/>
 112:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
 113:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundBrush}"/>
 114:                                     </ObjectAnimationUsingKeyFrames>
 115:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundCheckedGlyph" Storyboard.TargetProperty="Visibility">
 116:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
 117:                                     </ObjectAnimationUsingKeyFrames>
 118:                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
 119:                                         <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundBrush}"/>
 120:                                     </ObjectAnimationUsingKeyFrames>
 121:                                 </Storyboard>
 122:                             </VisualState>
 123:                             <VisualState x:Name="Unchecked"/>
 124:                             <VisualState x:Name="Indeterminate"/>
 125:                         </VisualStateGroup>
 126:                     </VisualStateManager.VisualStateGroups>
 127:                 </Grid>
 128:             </ControlTemplate>
 129:         </Setter.Value>
 130:     </Setter>
 131: </Style>

Now with that style we can have a simple style that shows a toggle on and off for the same button:

   1: ...
   2: <Page.BottomAppBar>
   3:     <AppBar>
   4:         <ToggleButton Style="{StaticResource ToggleAppBarButtonStyle}" 
   5:                 AutomationProperties.Name="Search" 
   6:                 Content="&#xE11A;" />
   7:     </AppBar>
   8: </Page.BottomAppBar>
   9: ...

The result would show:

 
ToggleButtonAppBarStyle in off/on state

This way we exhibit all the same behaviors of the AppBarButtonStyle, but just applied to the ToggleButton element in XAML.  Now we could probably refactor both to have an even more derivative base style and use more BasedOn styling, but how it is currently structured we couldn’t have a ToggleButton based on a Button style.  But thanks to the style/template engine in XAML we can re-use a lot and serve our needs!

Notice that you can set the Content to be whatever you want.  Since my base style uses the Segoe UI Symbol font, I’m setting the unicode value for a glyph here for Search.  A set of some cool glyphs you can use can be found in my previous post on XAML AppBar button styles.

You can get the ToggleAppBarButtonStyle from here: https://gist.github.com/2131230.

Hope this helps!

| Comments

So you’ve started to kick the tires of the Windows 8 Consumer Preview and now you are building an app.  You’ve read all the UX design guidelines and started looking at some great apps on the store.   Perhaps you’ve also viewed the online documentation and some samples?  And you’ve likely read about the contract implementations and other charms items like custom settings. 

UPDATE: Take a look at Callisto: a XAML toolkit which has a SettingsFlyout control

What is Settings?

When I refer to Settings here I’m referring to that consistent experience in Metro style apps when the user invokes the charms bar and chooses settings.  By default every application will respond to that Settings charm.  If you do nothing you will get the default experience that you may have seen:

Default Settings experience

The text items underneath your app title are referred to as commands.  Each application will always get the Permissions command.  When the user clicks this they will get some “about” information on your app (name, version, publisher) as well as the permissions the app has requested.  As an app developer, you have to do nothing to get this experience.  In addition to that the Settings pane shows some OS-level options like volume, Wi-Fi, brightness, etc. that the user can manipulate.  But you, my fellow app developer, can also implement custom settings options in your app.

The custom SettingsCommand

The first thing you have to do to customize your experience is implement a custom SettingsCommand.  These are implemented by first listening to when the SettingsPane will request if there are any additional commands for the current view.  Settings can be global if you have something like a “master” page setup in your XAML application, but can also be specific to a currently viewed XAML page.  It is not an either/or but a both.  I’ll leave the exercise up to you and your app on when you need which (or both).

First thing you have to do is listen for the event.  You would likely do this in your XAML view’s constructor:

   1: public BlankPage()
   2: {
   3:     this.InitializeComponent();
   4:  
   5:     _windowBounds = Window.Current.Bounds;
   6:  
   7:     Window.Current.SizeChanged += OnWindowSizeChanged;
   8:  
   9:     SettingsPane.GetForCurrentView().CommandsRequested += BlankPage_CommandsRequested;
  10: }

Notice the SettingsPane.GetForCurrentView().CommandsRequested event handler that I am using.  This will get triggered whenever the user invokes the Settings charm while on this view.  It is your opportunity to add more commands to that experience.  In your method for this you would create your new SettingsCommand and add them to the ApplicationCommands:

   1: void BlankPage_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
   2: {
   3:     ResourceLoader rl = new ResourceLoader();
   4:  
   5:     SettingsCommand cmd = new SettingsCommand("sample", 
   6:         rl.GetString("SoundOptionCommandText"), (x) =>
   7:         {
   8:             // more in a minute
   9:         });
  10:  
  11:     args.Request.ApplicationCommands.Add(cmd);
  12: }

You are able to add the text-based commands to the SettingsPane at this time.  The second argument I provided above will be the text that will display as the menu.  Notice how here I’m using ResourceLoader to get the string value for the text to be displayed.  This is a best practice to ensure you give your user’s the best experience.  Even though you may not localize now, setting this up in the beginning makes it way easier to just drop in localized strings and not have to change code.  The “SoundOptionCommandText” exists as a key/value pair in a file in my project located at en/Resources.resw.

Now that I have this enabled and my CommandsRequested wired up, when I invoke the charm while my app is running you see my new command:

Custom SettingsCommand

Yippee!  Your custom commands will show before the Permissions one.  The next step is to actually add something valuable to the user when they click on this new command…and that means some UI.

The custom Settings UI

When your user clicks your new shiny command you want them to see some shiny, but relevant UI.  If you were using HTML/JS you would use the WinJS.UI.SettingsFlyout control to do a lot of this for you.  There is a sample of this for comparison located in the Windows 8 developer samples.  In XAML there isn’t the literal ‘SettingsFlyout’ control equivalent, but a set of primitives for you to create the experience.  There are a few pieces you will need in place.

First I create a few member variable helpers to store some items away:

  • _windowBounds – this is the Rect of the current Window size.  I will need this for proper placement
  • _settingsWidth – The UX guidelines suggest either a 346 or 646 wide settings flyout
  • _settingsPopup – the Popup that will actually host my settings UI

The Popup is the important piece here.  It is the primitive that provides us with the “light dismiss” behavior that you see a lot in the Windows 8 experience.  This is where you have a menu/dialog and you simply tap away and it dismisses.  Popup.IsLightDismissEnabled gives us that functionality in our control that we will need in XAML.  Now let us go back to where we created our custom SettingsCommand and add back in the creation of our Popup and custom UI:

   1: SettingsCommand cmd = new SettingsCommand("sample", rl.GetString("SoundOptionCommandText"), (x) =>
   2: {
   3:     _settingsPopup = new Popup();
   4:     _settingsPopup.Closed += OnPopupClosed;
   5:     Window.Current.Activated += OnWindowActivated;
   6:     _settingsPopup.IsLightDismissEnabled = true;
   7:     _settingsPopup.Width = _settingsWidth;
   8:     _settingsPopup.Height = _windowBounds.Height;
   9:  
  10:     // more to come still
  11: });

Notice that we are creating the Popup, setting the width to the value specified in _settingsWidth and the height to whatever the current height of the active Window is at this time.  We are also listening to the Activated event on the Window to ensure that when our Window may be de-activated for something that a user may not have done via touch/mouse interaction (i.e., some other charm invocation, snapping an app, etc.) that we dismiss the Popup correctly.  here is the OnWindowActivated method definition:

   1: private void OnWindowActivated(object sender, Windows.UI.Core.WindowActivatedEventArgs e)
   2: {
   3:     if (e.WindowActivationState == Windows.UI.Core.CoreWindowActivationState.Deactivated)
   4:     {
   5:         _settingsPopup.IsOpen = false;
   6:     }
   7: }
   8:  
   9: void OnPopupClosed(object sender, object e)
  10: {
  11:     Window.Current.Activated -= OnWindowActivated;
  12: }

Notice we are also listening for the Popup.Closed event.  This is so that we can remove the OnWindowActivated method to avoid any reference leaks lying around.  Great, now let’s put some UI into our Popup.

For my example here I’m using a UserControl that I created to exhibit my settings needs.  Your use may vary and you may just need some simple things.  As we know in XAML there is more than one way to implement it in this flexible framework and this is just an example.  Going back to our custom SettingsCommand we now create an instance of my UserControl and set it as the Child of the Popup, setting appropriate Width/Height values as well:

   1: SettingsCommand cmd = new SettingsCommand("sample", rl.GetString("SoundOptionCommandText"), (x) =>
   2: {
   3:     _settingsPopup = new Popup();
   4:     _settingsPopup.Closed += OnPopupClosed;
   5:     Window.Current.Activated += OnWindowActivated;
   6:     _settingsPopup.IsLightDismissEnabled = true;
   7:     _settingsPopup.Width = _settingsWidth;
   8:     _settingsPopup.Height = _windowBounds.Height;
   9:  
  10:     SimpleSettingsNarrow mypane = new SimpleSettingsNarrow();
  11:     mypane.Width = _settingsWidth;                    
  12:     mypane.Height = _windowBounds.Height;
  13:  
  14:     _settingsPopup.Child = mypane;
  15:     _settingsPopup.SetValue(Canvas.LeftProperty, _windowBounds.Width - _settingsWidth);
  16:     _settingsPopup.SetValue(Canvas.TopProperty, 0);
  17:     _settingsPopup.IsOpen = true;
  18: });

Now when the user clicks the “Sound Options” they will see my custom UI:

Custom Settings UI

And if the user taps/clicks away from the dialog then it automatically dismisses itself.  You now have the fundamentals on how to create your custom UI for settings.

Some guiding principles

While this is simple to implement, there are some key guiding principles that make this key for your user’s experience.  First and foremost, this should be a consistent and predictable experience for your users.  Don’t get crazy with your implementation and stay within the UX design guidelines to ensure your app gives the user confidence when using it.  Additionally, here are some of my other tips.

Header Elements

You’ll notice above that the header of the custom UI is specific and contains a few elements.  The title should be clear (and again be ideally localized) in what the settings is doing.  The background color would match your app’s branding and likely be the same as the value of BackgroundColor in your app’s package manifest.  Putting your logo (use the same image you use for your SmallLogo setting in your package manifest) helps re-enforce this is the setting only for this app and not for the system.  Additionally providing a “back” button so the user can navigate back to the root SettingsPane and not have to invoke the charm again if they wanted to change other app settings.  In my example, the button simply just calls the SettingsPane APIs again to show it:

   1: private void MySettingsBackClicked(object sender, RoutedEventArgs e)
   2: {
   3:     if (this.Parent.GetType() == typeof(Popup))
   4:     {
   5:         ((Popup)this.Parent).IsOpen = false;
   6:     }
   7:     SettingsPane.Show();
   8: }

You may be curious to see the XAML used for my custom UI and I’ve included that in the download at the end here as not to take up viewing area here on the key areas.

Immediate Action

Unlike some modal dialog experiences, the Settings experience should create immediate change to your application.  Don’t put confirmation/save/cancel buttons in your UI but rather have the changes take effect immediately.  For instance in my sound example, if the user invokes the Settings charm, clicks/taps on my Sound Options and toggles the Sound Effects option on/off, then the sound should immediately turn on/off.  Now implementing this philosophy may change the way you create your custom UI and/or UserControl, but take that into account when designing.

Light Dismiss

This concept of light dismiss is about honoring the user’s action and not requiring interruption.  This is why we use the Popup.IsLightDismissEnabled option as we get this capability for free.  By using this if the user taps away to another part of the application or Window, then the Popup simply dismisses.  Don’t hang confirmation dialogs in there to block the user from doing what they want, but rather honor the context change for them.

Summary

The Windows platform has afforded us developers a lot of great APIs to create very predictable and consistent user experiences for the common things our apps will need.  Settings is one of those simple, yet effective places to create confidence in your application and a consistent Windows experience for your users.  Stick to the principles:

  • Set up custom commands that make sense for the context of the view and/or for the app as a whole
  • Create and show your UI according to the UX design guidelines
  • Have your settings immediately affect the application
  • Ensure that you use the dismiss model

Combine all these and you will be set.  Everything I talk about above is supported in XAML and WinRT.  My example is in C# because I’m most proficient in that language.  But this 100% equally applies in C++ as well and should be identical in practice.

You may be saying to yourself wouldn’t this make a great custom control?  Ah, stay tuned and subscribe here :-)!

Hope this helps!

Download Sample: download removed...see Callisto