Table of Contents

Gamepad-friendly UI

This guide makes a screen fully operable with a gamepad (or the arrow keys): focus that starts in the right place, layouts the D-pad can walk, dialogs that keep focus, and a switch for gameplay. The screen is a loadout page: a grid of buttons, a row of toggles, a check box, a combo box and a Start button.

You need a page loaded from markup (Your first screen). How focus works is described in Input, focus and navigation.

1. A layout the D-pad can walk

Arrow keys and the D-pad move focus to the nearest focusable control in that direction, so no tab order is needed. Plain panels and grids work as they are:

<!-- A loadout screen a gamepad can fully use: a grid of buttons, a row of toggles, a check box, a combo box and a button.
     The data context is the view model with a Difficulties list. -->
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Width="360">
  <TextBlock FontSize="28" Margin="0,0,0,16">Loadout</TextBlock>

  <!-- Arrows and the D-pad move to the nearest control in that direction, so a grid needs no order. -->
  <Grid Margin="0,0,0,16">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Button x:Name="Sword" Grid.Row="0" Grid.Column="0" Margin="0,0,8,8" HorizontalAlignment="Stretch">Sword</Button>
    <Button x:Name="Bow" Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" HorizontalAlignment="Stretch">Bow</Button>
    <Button x:Name="Shield" Grid.Row="1" Grid.Column="0" Margin="0,0,8,0" HorizontalAlignment="Stretch">Shield</Button>
    <Button x:Name="Potion" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch">Potion</Button>
  </Grid>

  <StackPanel Orientation="Horizontal" Margin="0,0,0,16">
    <ToggleButton x:Name="Easy" Margin="0,0,8,0">Easy</ToggleButton>
    <ToggleButton x:Name="Normal" Margin="0,0,8,0">Normal</ToggleButton>
    <ToggleButton x:Name="Hard">Hard</ToggleButton>
  </StackPanel>

  <CheckBox x:Name="Subtitles" Margin="0,0,0,16">Subtitles</CheckBox>
  <ComboBox x:Name="Difficulty" ItemsSource="{Binding Difficulties}" SelectedIndex="1" Margin="0,0,0,16" />
  <Button x:Name="Start" Padding="24,8" HorizontalAlignment="Left">Start</Button>
</StackPanel>

Two things keep it reliable: leave a gap between controls instead of overlapping them, and keep related controls in rows and columns so "the nearest one to the right" is the one the player expects.

2. Focus the first control on open

Nothing focused means nothing for the D-pad to move. Focus the first button when the page is shown:

/// <summary>Shows a menu page and puts the focus on its first button, so the D-pad has somewhere to start.</summary>
public static void Show(Canvas canvas, UIElement menu)
{
    canvas.Add(menu);
    canvas.Focus(menu.FindRequiredControl<Button>("Start"));
}

3. Make the focus visible

A gamepad player has no pointer to follow, so the focused control must stand out. The built-in theme gives every control a FocusStates group with a blue border; a game theme can make it bolder with an implicit style, as described in Custom themes. Test it from the couch: if you can't see the focus from a metre away, strengthen it.

4. Dialogs

An open Window or Dialog is a focus scope: the D-pad stays inside it, B closes it, and focus goes back to the control that had it:

/// <summary>A dialog is a focus scope: the D-pad stays inside it, B closes it, and the focus returns to the menu.</summary>
public static async Task<bool> ConfirmQuitAsync(Canvas canvas)
{
    DialogResult result = await MessageBox.ShowAsync(canvas, "Quit to desktop?", DialogButtons.YesNo);
    return result == DialogResult.Yes;
}

For your own panel with the same behaviour, set IsFocusScope on it.

5. Lists and scrolling

A list or a scroll viewer scrolls a focused item into view, and a combo box opens with A and takes the D-pad up and down while open; B closes it.

Note

The items of a list box can't be selected with the keyboard or a gamepad yet; see Known issues.

6. Gameplay

While the player controls the game, the D-pad and the stick shouldn't also move the menu focus. Switch navigation off for gameplay and on again for menus:

/// <summary>While the player controls the game, the D-pad and sticks belong to gameplay, not to the menu focus.</summary>
public static void SetGameplay(Canvas canvas, bool playing) => canvas.IsKeyboardNavigationEnabled = !playing;

The focused control still receives arrows while navigation is off (a text box moves its caret), only moving focus to another element is disabled.

The keys

Keyboard Gamepad Does
Arrow keys D-pad Move focus to the nearest focusable element in that direction.
Tab, Shift+Tab Right / left stick click Move focus to the next / previous element.
Enter A Activate the focused element.
Escape B Close the open dialog or list.
Note

These keys aren't rebindable yet; configurable bindings are on the roadmap.

What you built

A page where every control can be reached and used with the D-pad, that starts with a focus, keeps dialogs self-contained and hands the buttons back to the game during play.

Next