Поделиться через


How to use an app bar at different sizes

[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]

By default, an app bar button is 100 device-independent pixels (DIPs) wide and has a text label below its icon. When a user makes an app narrower, there isn't as much horizontal space to show commands. If you have more than a few buttons in the app bar, you must make some adjustments to make the buttons show correctly at narrower widths.

Note  If your app bar content is hosted in a CommandBar control, the CommandBar handles layout and resizing of its child app bar button controls. The techniques in this tutorial are only necessary when your app bar content is hosted in an AppBar control.

 

Roadmap: How does this topic relate to others? See:

What you need to know

Technologies

Prerequisites

Instructions

Step 1: Hide labels and reduce width (Windows 8.1)

Windows 8.1: This step applies only to Windows 8.1. For Windows 8, see step 3.

The code in these examples uses the AppBar defined in this Extensible Application Markup Language (XAML).

<Page.BottomAppBar>
    <AppBar x:Name="bottomAppBar" IsSticky="True">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            
            <StackPanel x:Name="leftAppBarPanel" Orientation="Horizontal">
                <AppBarButton Label="Save" Icon="Save"/>
                <AppBarButton Label="Discard" Icon="Delete"/>
                <AppBarButton Label="Edit" Icon="Edit"/>
                <AppBarButton Label="Undo" Icon="Undo"/>
                <AppBarButton Label="Redo" Icon="Redo"/>
            </StackPanel>
            
            <StackPanel x:Name="rightAppBarPanel" 
                        Orientation="Horizontal" HorizontalAlignment="Right">
                <AppBarButton Label="Skip Back" Icon="Previous"/>
                <AppBarButton Label="Skip Ahead" Icon="Next"/>
                <AppBarButton Label="Play" Icon="Play"/>
                <AppBarButton Label="Pause" Icon="Pause"/>
                <AppBarButton Label="Stop" Icon="Stop"/>
            </StackPanel>
        </Grid>
    </AppBar>
</Page.BottomAppBar>

This app bar looks like this in an app that's 1024 pixels wide.

When the app is 320 pixels wide, the app bar looks like this by default.

The app bar button controls (AppBarButton and AppBarToggleButton) have two sizes; normal and compact. By default, they have a text label and full padding. When the IsCompact property is set to true, the text label is hidden and the padding around the buttons is reduced. The AppBarSeparator also has a compact state in which the padding is reduced. To make the app bar buttons use less space, set the IsCompact property to true.

When an app bar button isn't hosted in a CommandBar, you must manage it's IsCompact property in your code. To do this, you listen for the page's SizeChanged event, and then set the IsCompact property for each app bar button as appropriate for the new window size.

To resize buttons

  1. Register for the page's SizeChanged event.

    public MainPage()
    {
        this.InitializeComponent();
    
        // Register for the SizeChanged event.
        this.SizeChanged += MainPage_SizeChanged;
    }
    
    Public Sub New()
        InitializeComponent()
    
        ' Register for the SizeChanged event.
        AddHandler SizeChanged, AddressOf MainPage_SizeChanged
    End Sub
    
  2. In the SizeChanged event handler, check if the page width has changed. If it has, update the layout of the AppBar.

    void MainPage_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (e.NewSize.Width != e.PreviousSize.Width)
        {
            UpdateBottomAppBarLayout(e.NewSize.Width);
        }
    }
    
    Private Sub MainPage_SizeChanged(sender As Object, e As SizeChangedEventArgs)
        If e.NewSize.Width <> e.PreviousSize.Width Then
            UpdateAppBarButtons(e.NewSize.Width)
        End If
    End Sub
    
  3. To update the AppBar layout, set each button's IsCompact property according to the page width. In this example, there are 10 AppBarButtons, and each is 100 DIPs wide, so if the page width is less than 1000, you need to make the buttons compact.

    private void UpdateAppBarButtons(double newWidth)
    {
        if (newWidth < 1000)
        {
            ToggleIsCompact(true);
        }
        else
        {
            ToggleIsCompact(false);
        }
    }
    
        Private Sub UpdateAppBarButtons(newWidth As Double)
            If newWidth < 1000 Then
                ToggleIsCompact(True)
            Else
                ToggleIsCompact(False)
            End If
        End Sub
    

    If you have a fixed number of buttons, you can set the IsCompact property individually on each one in code or using VisualStateManager. Or, you can loop through them, as shown here. This code assumes the app bar uses a layout with a root Panel that contains additional Panels that host the buttons.

    private void ToggleIsCompact(bool isCompact)
    {
        // Get the app bar's root Panel.
        Panel root = bottomAppBar.Content as Panel;
        if (root != null)
        {
            // Get the Panels that hold the controls.
            foreach (Panel panel in root.Children)
            {
                foreach (ICommandBarElement child in panel.Children)
                {
                    child.IsCompact = isCompact;
                }
            }
        }
    }
    
        Private Sub ToggleIsCompact(isCompact As Boolean)
            ' Get the app bar's root Panel.
            Dim root As Panel = TryCast(bottomAppBar.Content, Panel)
    
            If root IsNot Nothing Then
                ' Get the Panels that hold the controls.
                For Each panel As Panel In root.Children
                    For Each child As ICommandBarElement In panel.Children
                        child.IsCompact = isCompact
                    Next
                Next
            End If
        End Sub
    

Now, when the user make the app narrower, the buttons resize and the app bar looks like this.

Step 2: Rearrange and hide buttons (Windows 8.1)

Windows 8.1: This step applies only to Windows 8.1. For Windows 8, see step 4.

Note  In this step, we assume that the app bar buttons are in the compact state as described in the previous step, with no text label and reduced width.

 

At it's narrowest, the app bar is wide enough to show only 5 AppBarButtons in a row. If you have more than 5 buttons in the app bar, you must arrange the buttons in 2 rows, hide some buttons, or do both.

To move buttons into 2 rows

  1. To rearrange buttons into 2 rows, use a layout like this for your app bar. Use a root Grid with 2 rows, and 2 panels, typically StackPanels, to hold the buttons.

    <AppBar>
        <Grid>
            <Grid.RowDefinitions>
               <RowDefinition Height="Auto"/>
               <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
    
            <StackPanel x:Name="leftAppBarPanel" Orientation="Horizontal">
                <!-- Buttons -- >
            </StackPanel>
    
            <StackPanel x:Name="rightAppBarPanel" Orientation="Horizontal" 
                        HorizontalAlignment="Right">
                <!-- Buttons -- >
            </StackPanel>
        </Grid>
    </AppBar>
    
  2. When the app gets too narrow to show all the buttons in one row, change the Grid.Row property value for the rightAppBarPanel element to 1 and change the HorizontalAlignment property of the panel to Left.

    This is useful if you need to keep both the primary (right) and secondary (left) buttons visible. If you don't need to keep the secondary commands visible, you can hide them as shown next.

    When the app is at its narrowest, the buttons on the right move to the bottom row. The app bar looks like this.

    In this example, there are 10 AppBarButtons, and each is 60 DIPs wide when its compact. If the page width is less than 600, you need to make the buttons compact and move them into 2 rows.

    private void UpdateBottomAppBarLayout(double newWidth)
    {
        if (newWidth < 600)
        {
            ToggleIsCompact(true);
            rightAppBarPanel.SetValue(Grid.RowProperty, 1);
            rightAppBarPanel.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Left);
        }
        else if (newWidth < 1000)
        {
            ToggleIsCompact(true);
            rightAppBarPanel.SetValue(Grid.RowProperty, 0);
            rightAppBarPanel.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Right);
        }
        else
        {
            ToggleIsCompact(false);
            rightAppBarPanel.SetValue(Grid.RowProperty, 0);
            rightAppBarPanel.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Right);
        }
    }
    
        Private Sub UpdateAppBarButtons(newWidth As Double)
            If newWidth < 600 Then
                ToggleIsCompact(True)
                rightAppBarPanel.SetValue(Grid.RowProperty, 1)
                rightAppBarPanel.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Left)
            ElseIf newWidth < 1000 Then
                ToggleIsCompact(True)
                rightAppBarPanel.SetValue(Grid.RowProperty, 0)
                rightAppBarPanel.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Right)
            Else
                ToggleIsCompact(False)
                rightAppBarPanel.SetValue(Grid.RowProperty, 0)
                rightAppBarPanel.SetValue(HorizontalAlignmentProperty, HorizontalAlignment.Right)
            End If
        End Sub
    

To hide buttons

  • When the app gets too narrow to show all the buttons in one row, change the Visibility property value for the leftAppBarPanel element to Collapsed.

    When the app is at its narrowest, the buttons in the left panel are hidden. The app bar looks like this.

    In this example, there are 10 AppBarButtons, and each is 60 DIPs wide when its compact. If the page width is less than 600, you need to make the buttons compact and hide the secondary commands.

    private void UpdateBottomAppBarLayout(double newWidth)
    {
        if (newWidth < 600)
        {
            ToggleIsCompact(true);
            leftAppBarPanel.SetValue(VisibilityProperty, Visibility.Collapsed);
    
        }
        else if (newWidth < 1000)
        {
            ToggleIsCompact(true);
            leftAppBarPanel.SetValue(VisibilityProperty, Visibility.Visible);
        }
        else
        {
            ToggleIsCompact(false);
            leftAppBarPanel.SetValue(VisibilityProperty, Visibility.Visible);
        }
    }
    
        Private Sub UpdateAppBarButtons(newWidth As Double)
            If newWidth < 600 Then
                ToggleIsCompact(True)
                leftAppBarPanel.SetValue(VisibilityProperty, Visibility.Collapsed)
            ElseIf newWidth < 1000 Then
                ToggleIsCompact(True)
                leftAppBarPanel.SetValue(VisibilityProperty, Visibility.Visible)
            Else
                ToggleIsCompact(False)
                leftAppBarPanel.SetValue(VisibilityProperty, Visibility.Visible)
            End If
        End Sub
    

Step 3: Hide labels and reduce width (Windows 8)

Windows 8: This step applies only to Windows 8. For Windows 8.1, see step 1. In Windows 8.1, AppBarButtonStyle is deprecated and replaced with AppBarButton controls.

The code in these examples uses the AppBar defined in this XAML.

Note  To use the "AppBarButtonStyle" resources shown here, you must edit the StandardStyles.xaml file to make them available. For more info, see Quickstart: Adding app bar buttons.

 

<Page.BottomAppBar>
    <AppBar x:Name="bottomAppBar" IsSticky="True" 
            Loaded="appBar_Loaded" Unloaded="appBar_Unloaded">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            
            <StackPanel x:Name="leftAppBarPanel" Orientation="Horizontal">
                <Button Style="{StaticResource SaveAppBarButtonStyle}"/>
                <Button Style="{StaticResource DiscardAppBarButtonStyle}"/>
                <Button Style="{StaticResource EditAppBarButtonStyle}"/>
                <Button Style="{StaticResource UndoAppBarButtonStyle}"/>
                <Button Style="{StaticResource RedoAppBarButtonStyle}"/>
            </StackPanel>
            
            <StackPanel x:Name="rightAppBarPanel" 
                        Orientation="Horizontal" HorizontalAlignment="Right">
                <Button Style="{StaticResource SkipBackAppBarButtonStyle}"/>
                <Button Style="{StaticResource SkipAheadAppBarButtonStyle}"/>
                <Button Style="{StaticResource PlayAppBarButtonStyle}"/>
                <Button Style="{StaticResource PauseAppBarButtonStyle}"/>
                <Button Style="{StaticResource StopAppBarButtonStyle}"/>
            </StackPanel>
        </Grid>
    </AppBar>
</Page.BottomAppBar>

This app bar looks like this in an app that's 1024 pixels wide.

When the app is snapped, the app bar looks like this by default.

To make the app bar buttons use less space, the AppBarButtonStyle base Style includes VisualStates that hide the button's label and reduce its width in the Snapped and FullScreenPortrait views. The VisualStates are defined in the AppBarButtonStyle resource, but by default, controls don’t know about the app's view state. To make an app bar button change state based on the app's view state, you must provide additional code to make the Button aware of app view state changes. The code you provide depends on whether or not the page that hosts the app bar is derived from the LayoutAwarePage class.

Note  Microsoft Visual Studio page templates other than Blank Page are derived from the LayoutAwarePage class. LayoutAwarePage is an implementation of Page that enables important functionality for Windows Store app development like view state management, basic navigation, and app session state management. For more info, see C#, VB, and C++ item templates.

 

In a LayoutAwarePage, any control can invoke StartLayoutUpdates to register to receive visual state changes that correspond to app view state changes. This means that an app bar button can go to its Snapped view state when the app view state changes to Snapped.

To resize buttons in a LayoutAwarePage

  1. If your AppBar has only 1 or 2 commands, you can register and unregister them individually in their Loaded and Unloaded events, like this.

    <Button Style="{StaticResource PlayAppBarButtonStyle}"
            Loaded="StartLayoutUpdates" Unloaded="StopLayoutUpdates"/>
    
  2. If your app bar has more buttons, it’s better to register and unregister them all in code when the app bar is loaded and unloaded, as shown here.

    This code assumes the app bar uses a layout with a root Panel that contains additional Panels that host the buttons. Each button is registered to receive state changes when the AppBar is loaded, and unregistered when the app bar is unloaded.

    The Loaded and Unloaded events are registered in the AppBar XAML shown previously.

    private void appBar_Loaded(object sender, RoutedEventArgs e)
    {
        // Get the app bar's root Panel.
        Panel root = ((AppBar)sender).Content as Panel;
        if (root != null)
        {
            // Get the Panels that hold the controls.
            foreach (Panel panel in root.Children)
            {
                // Get each control and register for layout updates.
                foreach (UIElement child in panel.Children)
                {
                    base.StartLayoutUpdates(child, new RoutedEventArgs());
                }
            }
        }
    }
    
    private void appBar_Unloaded(object sender, RoutedEventArgs e)
    {
        // Get the app bar's root Panel.
        Panel root = ((AppBar)sender).Content as Panel;
        if (root != null)
        {
            // Get the Panels that hold the controls.
            foreach (Panel panel in root.Children)
            {
                // Get each control and unregister layout updates.
                foreach (UIElement child in panel.Children)
                {
                    base.StopLayoutUpdates(child, new RoutedEventArgs());
                }
            }
        }
    }
    
    Private Sub appBar_Loaded(sender As Object, e As RoutedEventArgs)
        ' Get the app bar's root Panel.
        Dim appBar = TryCast(sender, AppBar)
        Dim root As Panel = TryCast(appBar.Content, Panel)
    
        If root IsNot Nothing Then
            ' Get the Panels that hold the controls.
            For Each panel As Panel In root.Children
                ' Get each control and register for layout updates.
                For Each child As UIElement In panel.Children
                    MyBase.StartLayoutUpdates(child, New RoutedEventArgs())
                Next
            Next
        End If
    End Sub
    
    Private Sub appBar_Unloaded(sender As Object, e As RoutedEventArgs)
        ' Get the app bar's root Panel.
        Dim appBar = TryCast(sender, AppBar)
        Dim root As Panel = TryCast(appBar.Content, Panel)
    
        If root IsNot Nothing Then
            ' Get the Panels that hold the controls.
            For Each panel As Panel In root.Children
                ' Get each control and unregister layout updates.
                For Each child As UIElement In panel.Children
                    MyBase.StopLayoutUpdates(child, New RoutedEventArgs())
                Next
           Next
        End If
    End Sub
    

If the page that hosts the app bar is not derived from LayoutAwarePage, you must provide functionality in your page to change the Button's view state. To do this, you listen to app view state changes, and then call ViewStateManager.GoToState for each app bar button when the app's view state changes.

To resize buttons in a blank Page

  1. Register for the window's SizeChanged event.

    public MainPage()
    {
        this.InitializeComponent();
    
        // Register for the SizeChanged event.
        Window.Current.SizeChanged += Current_SizeChanged;
    }
    
    Public Sub New()
        InitializeComponent()
    
        ' Register for the SizeChanged event.
        AddHandler SizeChanged, AddressOf Current_SizeChanged
    End Sub
    
  2. In the SizeChanged event handler, check if the app bar is open. If it is, update each button's view state.

    This code handles the case where the app view state changes while the app bar is open.

    void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
    {
        if (bottomAppBar.IsOpen == true)
        {
            UpdateAppBarButtonsViewState();
        }
    }
    
    Private Sub Current_SizeChanged(sender As Object, e As SizeChangedEventArgs)
        If bottomAppBar.IsOpen = True Then
            UpdateAppBarButtonsViewState()
        End If
    End Sub
    
  3. Add a handler for the AppBar's Loaded event. In the Loaded event handler, update each button's view state.

    This code handles the case where the app bar is opened after the app view state changes.

    This Loaded event is registered in the AppBar XAML shown previously.

    private void bottomAppBar_Loaded(object sender, RoutedEventArgs e)
    {
        UpdateAppBarButtonsViewState();
    }
    
    Private Sub bottomAppBar_Loaded(sender As Object, e As RoutedEventArgs)
        UpdateAppBarButtonsViewState()
    End Sub
    
  4. To update each button's view state, call VisualStateManager.GoToState and pass in the app's current view state.

    If your app bar has only 1 or 2 commands, you can name them and call GoToState on them individually, like this.

    private void UpdateAppBarButtonsViewState()
    {
        string viewState = Windows.UI.ViewManagement.ApplicationView.Value.ToString();
    
        VisualStateManager.GoToState(playButton, viewState, true);
        VisualStateManager.GoToState(stopButton, viewState, true);
    }
    
    Private Sub UpdateAppBarButtonsViewState()
        Dim viewState = Windows.UI.ViewManagement.ApplicationView.Value.ToString()
    
        VisualStateManager.GoToState(playButton, viewState, True)
        VisualStateManager.GoToState(stopButton, viewState, True)            
    End Sub
    

    If your app bar has more buttons, it’s better to loop through them, as shown here. This code assumes the app bar uses a layout with a root Panel that contains additional Panels that host the buttons.

    private void UpdateAppBarButtonViewState()
    {
        string viewState = Windows.UI.ViewManagement.ApplicationView.Value.ToString();
    
        // Get the app bar's root Panel.
        Panel root = bottomAppBar.Content as Panel;
        if (root != null)
        {
            // Get the Panels that hold the controls.
            foreach (Panel panel in root.Children)
            {
                // Get each control and update its state if 
               // it's a Button or ToggleButton.
                foreach (UIElement child in panel.Children)
                {
                    if (child.GetType() == typeof(Button) || 
                        child.GetType() == typeof(ToggleButton))
                    {
                        VisualStateManager.GoToState((ButtonBase)child, viewState, true);
                    }
                }
            }
        }
    }
    
    Private Sub UpdateAppBarButtonsViewState()
        Dim viewState = Windows.UI.ViewManagement.ApplicationView.Value.ToString()
        ' Get the app bar's root Panel.
        Dim root As Panel = TryCast(bottomAppBar.Content, Panel)
    
        If root IsNot Nothing Then
            ' Get the Panels that hold the controls.
            For Each panel As Panel In root.Children
                'Get each control and update its state if 
                ' it's a Button or ToggleButton.
                For Each child As UIElement In panel.Children
                    If child.GetType() Is GetType(Button) OrElse
                       child.GetType() Is GetType(ToggleButton) Then
    
                         VisualStateManager.GoToState((TryCast(child, ButtonBase)), viewState, True)
                     End If
                Next
            Next
        End If
    End Sub
    

Now, when the app is rotated to portrait orientation and the buttons resize, the app bar looks like this.

Step 4: Rearrange and hide buttons (Windows 8)

Windows 8: This step applies only to Windows 8. For Windows 8.1, see step 2. In Windows 8.1, AppBarButtonStyle is deprecated and replaced with AppBarButton controls.

Note  In this step, we assume that the app bar buttons are in the SnappedVisualState as described in the previous step, with no text label and reduced width.

 

In the Snapped view, the app bar is wide enough to show only 5 buttons in a row. If you have more than 5 buttons in the app bar, you must arrange the buttons in 2 rows, hide some buttons, or do both.

To move buttons into 2 rows

  1. To rearrange buttons into 2 rows, use a layout like this for your app bar. Use a root Grid with 2 rows, and 2 panels, typically StackPanels, to hold the buttons.

    <AppBar>
        <Grid>
            <Grid.RowDefinitions>
               <RowDefinition Height="Auto"/>
               <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
    
            <StackPanel x:Name="leftAppBarPanel" Orientation="Horizontal">
                <!-- Buttons -- >
            </StackPanel>
    
            <StackPanel x:Name="rightAppBarPanel" Orientation="Horizontal" HorizontalAlignment="Right">
                <!-- Buttons -- >
            </StackPanel>
        </Grid>
    </AppBar>
    
  2. In the SnappedVisualState, change the Grid.Row property value for the rightAppBarPanel element to 1 and change the HorizontalAlignment property of the panel to Left.

    When the app is snapped, the buttons on the right move to the bottom row. The app bar looks like this.

    <VisualStateManager.VisualStateGroups>
        ...
            <VisualState x:Name="Snapped">
                <Storyboard>
                    ... 
    
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="rightAppBarPanel" 
                                                   Storyboard.TargetProperty="(Grid.Row)">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="rightAppBarPanel" 
                                           Storyboard.TargetProperty="HorizontalAlignment">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="Left"/>
                    </ObjectAnimationUsingKeyFrames>
                    ...
                </Storyboard>
            </VisualState>
        ...
    </VisualStateManager.VisualStateGroups>
    

To hide buttons

  • In the SnappedVisualState, change the Visibility property value for the leftAppBarPanel element to Collapsed.

    When the app is snapped, the buttons in the left panel are hidden. The app bar looks like this.

    <VisualStateManager.VisualStateGroups>
        ...
            <VisualState x:Name="Snapped">
                <Storyboard>
                    ...
    
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="leftAppBarPanel" 
                                                   Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
        ...
    </VisualStateManager.VisualStateGroups>
    

Remarks

You can hide less important commands in the Snapped view by hiding a whole panel, as shown, or by hiding individual buttons in the same way. To hide an individual button, you must name it so you can reference it in the VisualStateManager.