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:
- Roadmap for Windows Runtime apps using C# or Visual Basic
- Roadmap for Windows Runtime apps using C++
What you need to know
Technologies
Prerequisites
- Quickstart: Adding controls and handling events
- Quickstart: Adding app bars
- Quickstart: Adding app bar buttons
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
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
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
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
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>
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
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"/>
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
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
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
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
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 Snapped
VisualState 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
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>
In the
Snapped
VisualState, change the Grid.Row property value for therightAppBarPanel
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
Snapped
VisualState, change the Visibility property value for theleftAppBarPanel
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.