diff --git a/docs/docs/accessibility.md b/docs/docs/accessibility.md index b028f5e0..20b611e1 100644 --- a/docs/docs/accessibility.md +++ b/docs/docs/accessibility.md @@ -119,6 +119,13 @@ Uses `AutomationProperties.Name` if set on the `TableViewRowHeader`, otherwise d | **Ctrl+Shift+A** | Deselect all | | **Ctrl+C** | Copy to clipboard | +The column header row is one tab stop: **Tab** to it, then move between headers with the **Left** / **Right** arrow keys. + +| Key (on a column header) | Action | +|---|---| +| **Enter** / **Space** | Sort by the column (cycles like a click); **Ctrl** adds to the existing sort, like **Ctrl**+click | +| **Alt+Down**, **Shift+F10** or the **Menu** key | Open the column's options (sort, group, filter) | + ## Screen reader behavior When navigating with a screen reader such as Narrator: diff --git a/src/TableViewColumnHeader.cs b/src/TableViewColumnHeader.cs index 17dd08ac..8ace1530 100644 --- a/src/TableViewColumnHeader.cs +++ b/src/TableViewColumnHeader.cs @@ -61,6 +61,7 @@ public TableViewColumnHeader() ManipulationMode = ManipulationModes.TranslateX; RegisterPropertyChangedCallback(WidthProperty, OnWidthChanged); RightTapped += OnRightTapped; + ContextRequested += OnContextRequested; } /// @@ -312,6 +313,52 @@ internal void HideFlyout() _optionsFlyout?.Hide(); } + /// + /// Keyboard access to what a click does: Enter or Space sorts (Ctrl adds to the existing sort, as Ctrl+click does), + /// and Alt+Down opens the column's options. Shift+F10 and the Menu key open them too (see OnContextRequested). + /// + protected override void OnKeyDown(KeyRoutedEventArgs e) + { + if (e.Key is VirtualKey.Enter or VirtualKey.Space && CanSort && Column is not null) + { + DoSort(GetNextSortDirection(), !IsKeyDown(VirtualKey.Control)); + e.Handled = true; + } + else if (e.Key is VirtualKey.Down && IsKeyDown(VirtualKey.Menu) && ShowOptions()) + { + e.Handled = true; + } + + base.OnKeyDown(e); + } + + /// + /// Opens the column's options for a keyboard context request (Shift+F10, the Menu key). A right-click also + /// raises this, with a pointer position; those keep going through . + /// + private void OnContextRequested(UIElement sender, ContextRequestedEventArgs e) + { + if (!e.TryGetPosition(this, out _) && ShowOptions()) + { + e.Handled = true; + } + } + + /// Opens the options flyout (sort, group, filter), if the column has one. + private bool ShowOptions() + { + if (_optionsButton?.Flyout is not { } flyout || !CanFilter) + { + return false; + } + + flyout.ShowAt(_optionsButton); + return true; + } + + private static bool IsKeyDown(VirtualKey key) => + InputKeyboardSource.GetKeyStateForCurrentThread(key).HasFlag(CoreVirtualKeyStates.Down); + /// protected override void OnTapped(TappedRoutedEventArgs e) { diff --git a/src/Themes/TableViewColumnHeader.xaml b/src/Themes/TableViewColumnHeader.xaml index fcc8c6f2..1e0b2916 100644 --- a/src/Themes/TableViewColumnHeader.xaml +++ b/src/Themes/TableViewColumnHeader.xaml @@ -33,7 +33,7 @@ + Value="True" /> diff --git a/src/Themes/TableViewHeaderRow.xaml b/src/Themes/TableViewHeaderRow.xaml index a7e6ab1f..6cd52787 100644 --- a/src/Themes/TableViewHeaderRow.xaml +++ b/src/Themes/TableViewHeaderRow.xaml @@ -15,6 +15,8 @@