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

Windows 8 Metro: Discovering the Application Bars

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

There are many analogies between Windows Phone and Windows 8 metro-style applications, and the Application Bars are for sure one of these. Windows Phone introduced the application bar in his first release and, no matter if it's rough and limited, it's an important surface that adds space to the the main phone canvas. Windows 8 reprises and extends the application bar's concept making it so much flexible and extensible that requires a specific guidelines to rule the ways it may be used.

Giving a name to the bars

If the Windows Phone application bar is positioned to the bottom of the screen and may only contains four buttons and a limited number of menu items, in Windows 8 the application bars are two, one on the top of the screen and the other on the bottom, and they can host complex layouts, not limited to a small set of components, but completely integrated into the Visual Tree.

From the development point of view, you can think at the application bars like an extension of the screen that is usually hidden but they appear when the user swipe on the top or bottom. This area can host any content, from a simple StackPanel with a number of buttons to a complex grid or a scrollable list of items.

Usually the space of these bars is ruled to host elements that are uniform across different apps. So the top application bar is usually named "navigation bar" and is used to contain elements, categories, document thumbnails and everything else that can support the user when he has to navigate between different contents. A perfect example of "navigation bar" is the browser's bar that is used to display the current opened pages and allow the user to switch between one and the other. This application bar also hosts the commands that are related to the navigation, like the button used to open a new tab.

The bar on the bottom side instead is named "command bar" and is usually targeted to host global or contextual commands. When I talk about "global commands" I mean all the actions that are valid in the whole application. These are usually hosted in the leftmost part of the command bar. On the rightmost side they are hosted the commands that are specific to the page, action or items that are currently focused by the user interaction. You can make these command appear or disappear based on the elements that the user are interacting with. For example, when an item in a list is selected you can make it appear a "delete" button that disappears when the list hasn't a selection.

Bars basics

As for Windows Phone, also in Windows 8 the Page exposes two properties, one for each app-bar. These properties allow you to use an element of type AppBar that is the layout root for each of the application bars.

   1: <Page.TopAppBar>
   2:     <AppBar>
   3:         <!-- Place here layout for top appbar -->
   4:     </AppBar>
   5: </Page.TopAppBar>
   6:  
   7: <Page.BottomAppBar>
   8:     <AppBar>
   9:         <!-- Place here layout for bottom appbar -->
  10:     </AppBar>
  11: </Page.BottomAppBar>

Just before to discuss about the layout of each bar it is better to be in touch with some properties that apply to the container. There are two important properties you have to deal with. These are additional to the normal properties that usually apply to a ContentControl. They are the IsOpen and IsSticky.

Since it is easy to understand that IsOpen handles the status of the application bars, allowing to programmatically open or close the bar itself, without a direct request by the user, the IsSticky is not intuitive to understand. It rules the way the application bar is dismissed after the user clicks outside of it. Normally the action of clicking-away automatically imply that the application bar hides itself behind the border where it is attached. When you set to true the IsSticky property, the application bar does not honour the click away action, but it requires that the closing action is determined by the developer by setting to false the IsOpen property. This is useful, as I will show in a few, when you attach a context menu or a flyout to a command and you handle the closing of the bar when an item of the menu is clicked.

Inside an application bar you can put whatever you need for your application and, as far as I know there is not any limitation for the size of the bar. As an example you may want to use a set of buttons in the command bar so, you can use a horizontal StackPanel to organize the buttons:

   1: <AppBar Background="Orange">
   2:     <StackPanel Orientation="Horizontal">
   3:         <Button Style="{StaticResource SkipBackAppBarButtonStyle}" />
   4:         <Button Style="{StaticResource PlayAppBarButtonStyle}" />
   5:         <Button Style="{StaticResource PauseAppBarButtonStyle}" IsEnabled="False" />
   6:         <Button Style="{StaticResource SkipAheadAppBarButtonStyle}" />
   7:     </StackPanel>
   8: </AppBar>

Differently you may need to divide the buttons in two groups with different meaning so, the better hosting panel would be a two-column grid with a StackPanel inside each part:

   1: <AppBar Background="Orange">
   2:     <Grid>
   3:         <Grid.ColumnDefinitions>
   4:             <ColumnDefinition Width="*" />
   5:             <ColumnDefinition Width="*" />
   6:         </Grid.ColumnDefinitions>
   7:         <StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Left">
   8:             <Button Style="{StaticResource SkipBackAppBarButtonStyle}" />
   9:             <Button Style="{StaticResource PlayAppBarButtonStyle}" />
  10:             <Button Style="{StaticResource PauseAppBarButtonStyle}" IsEnabled="False" />
  11:             <Button Style="{StaticResource SkipAheadAppBarButtonStyle}" />
  12:         </StackPanel>
  13:         <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
  14:             <Button Style="{StaticResource EditAppBarButtonStyle}" />
  15:             <Button Style="{StaticResource SaveAppBarButtonStyle}" />
  16:             <Button Style="{StaticResource DeleteAppBarButtonStyle}" />
  17:         </StackPanel>
  18:     </Grid>
  19: </AppBar>

As another sample you may need to generate items in a navigation bar, using a property of the ViewModel as source. The better way is to use an ItemsControl or a ListView that also adds a scrollbar if items are much more bigger that the available space:

   1: <AppBar Background="Orange">
   2:     <ListView ItemsSource="{Binding Pages}"
   3:               SelectionMode="Single" 
   4:               ScrollViewer.IsHorizontalRailEnabled="True"
   5:               ScrollViewer.HorizontalScrollMode="Enabled"
   6:               ScrollViewer.HorizontalScrollBarVisibility="Visible">
   7:         <ListView.ItemsPanel>
   8:             <ItemsPanelTemplate>
   9:                 <StackPanel Orientation="Horizontal" Margin="0,0,0,16" />
  10:             </ItemsPanelTemplate>
  11:         </ListView.ItemsPanel>
  12:         <ListView.ItemTemplate>
  13:             <DataTemplate>
  14:                 <Border Width="200" Height="150" Background="White">
  15:                     <TextBlock Text="{Binding}" 
  16:                                HorizontalAlignment="Center" VerticalAlignment="Center" />
  17:                 </Border>
  18:             </DataTemplate>
  19:         </ListView.ItemTemplate>
  20:     </ListView>
  21: </AppBar>

In the first two samples you see I've used a number of StaticResource values. These values are declared in the StandardStyles.xaml file, generated by the Visual Studio 2012 template, but they needs to be enabled before you can use them in your application. Infact, these styles are normally commented because of the high number of items that may cause an unuseful waste of memory. But when you need a button, before to deal with a designer for a beautiful icon, try to open this file and most of the times it contains the icon and the title you are searching for.

Extending the app-bar space

Most of the times, also if the space in the application bar is big, you rapidly understand that it cannot host the full set of buttons you want. Also lot of times you see that a number of buttons can be easily changed in a context menu because of the relation that they have each other. Context menu are allowed to extend the application bar space. When you need a context menu you can build it by code, using the PopupMenu control and attach it to the control that causes the popup to appear. Fortunately positioning a context menu is really easy, but it needs you detect the rectangle of the element to which the positioning is relative. Let say you have a button to determine the sorting of a list. This button pop-ups a context menu with three items that are the available sorting.

   1: <Page.BottomAppBar>
   2:     <AppBar Background="Orange">
   3:         <StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Left">
   4:             <Button Style="{StaticResource SortAppBarButtonStyle}" Click="SortButton_Click" />
   5:         </StackPanel>
   6:     </AppBar>
   7: </Page.BottomAppBar>

When the SortButton_Click even is raised you have to create the PopupMenu and position it above the clicked button:

   1: private async void SortButton_Click(object sender, RoutedEventArgs e)
   2: {
   3:     FrameworkElement element = (FrameworkElement)sender;
   4:  
   5:     PopupMenu menu = new PopupMenu();
   6:     menu.Commands.Add(new UICommand("By Name", null, "name"));
   7:     menu.Commands.Add(new UICommand("By Date", null, "date"));
   8:     menu.Commands.Add(new UICommand("By Rank", null, "rank"));
   9:  
  10:     var clicked = await menu.ShowForSelectionAsync(element.GetElementRect(0,-10), Placement.Above);
  11:  
  12:     // do what you want with clicked item
  13: }

As you can see I use a GetElementRect method inside this snippet. This method is not available in the Framework but is an extension I created to easily detect the rect of a FrameworkElement. The extension method comes in two overloads so you can also specify a displacement to change the final position of the element. Here is the extensions:

   1: public static class Positioning
   2: {
   3:     public static Rect GetElementRect(this FrameworkElement element, int hOffset, int vOffset)
   4:     {
   5:         Rect rect = Positioning.GetElementRect(element);
   6:         rect.Y += vOffset;
   7:         rect.X += hOffset;
   8:         return rect;
   9:     }
  10:  
  11:     public static Rect GetElementRect(this FrameworkElement element)
  12:     {
  13:         GeneralTransform buttonTransform = element.TransformToVisual(null);
  14:         Point point = buttonTransform.TransformPoint(new Point());
  15:         return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
  16:     }
  17: }

When you pass the resulting Rect to the ShowForSelectionAsync method you also specify a Placement value. In my case it is "Above" because I'm using the bottom bar, but nothing prevents to use the same code in the top bar and put the context menu on the bottom.

Context menus are not the sole thing you can pop up from the application bar. You can also use a flyout. Flyout is an alias of "Popup" and they allows to host special contents that go beyond the simple list of clickable items. As an example you can ask for filtering options or for the confirmation of an action. Unfortunately flyouts are not directly available in XAML (afaik it is the sole limitation of XAML over HTML5 that instead has a specific control) and you should be forced to manipulate a Popup control. Thanks to "callisto", a library published by Tim Heuer, you can pop up a flyout easily as you would do with a context menu:

   1: private void FilterButton_Click(object sender, RoutedEventArgs e)
   2: {
   3:     Button button = sender as Button;
   4:  
   5:     if (button != null)
   6:     {
   7:         Flyout flyout = new Flyout();
   8:         flyout.Content = new Filters();
   9:         flyout.Placement = PlacementMode.Top;
  10:         flyout.PlacementTarget = button;
  11:         flyout.IsOpen = true;
  12:     }
  13: }

The Content property hosts a UserControl that shows a number of filter options. Please remember that Flyouts are specifically required by design guidelines because of their behavior. To show a confirmation flyout, you have to put only the confirm button because the click-away closes the flyout and implicitly deny the confirmation. The same is for filter options. If you change options they are applied but clicking away the options remains the same as before the flyout appears.

Differences from Windows Phone 7

As you have seen, application bars in Windows 8 metro-style applications are so far much more complex and articulated than the corresponding bar in Windows Phone. And they are also much more effective given that they support almost everything that is supported in every part to the Visual Tree. This is not really so in Windows Phone, where the application bar does not make part of the Visual Tree because it does not derive from DependencyObject then it does not enforces the dependency properties system. In Windows 8 instead, animations, data binding, layout and a number of different options gives you the best.


Subscribe

Comments

No comments

Add Comment

Login to comment:
  *      *       

From this series