diff --git a/src/TableView.Properties.cs b/src/TableView.Properties.cs index e592e84..d720982 100644 --- a/src/TableView.Properties.cs +++ b/src/TableView.Properties.cs @@ -256,6 +256,9 @@ public partial class TableView public static readonly DependencyProperty CanReorderColumnsProperty = DependencyProperty.Register(nameof(CanReorderColumns), typeof(bool), typeof(TableView), new PropertyMetadata(true)); /// + /// Identifies the UseListViewHotkeys dependency property. + /// + public static readonly DependencyProperty UseListViewHotkeysProperty = DependencyProperty.Register(nameof(UseListViewHotkeys), typeof(bool), typeof(TableView), new PropertyMetadata(false)); /// Identifies the dependency property. /// public static readonly DependencyProperty ConditionalCellStylesProperty = DependencyProperty.Register(nameof(ConditionalCellStyles), typeof(IList), typeof(TableView), new PropertyMetadata(default)); @@ -768,6 +771,15 @@ public bool CanReorderColumns set => SetValue(CanReorderColumnsProperty, value); } + /// + /// Gets or sets whether the TableView should use ListView like hotkeys for navigation and selection. + /// + public bool UseListViewHotkeys + { + get => (bool)GetValue(UseListViewHotkeysProperty); + set => SetValue(UseListViewHotkeysProperty, value); + } + /// /// Handles changes to the ItemsSource property. /// diff --git a/src/TableView.cs b/src/TableView.cs index 8de95d1..fee9826 100644 --- a/src/TableView.cs +++ b/src/TableView.cs @@ -3,6 +3,7 @@ using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; using System; using System.Collections; using System.Collections.Generic; @@ -129,6 +130,13 @@ protected override DependencyObject GetContainerForItemOverride() _rows.Add(row); return row; } + + private bool IsRowKeyboardContext => + (UseListViewHotkeys == true) && //Not sure if I'm solving a bug or adding a feature, so I've made it apply only when the UseListViewHotkeys is set to true just in case. + (SelectionUnit == TableViewSelectionUnit.Row || + (SelectionUnit == TableViewSelectionUnit.CellOrRow && + LastSelectionUnit == TableViewSelectionUnit.Row)); + /// protected override async void OnKeyDown(KeyRoutedEventArgs e) @@ -142,9 +150,94 @@ protected override async void OnKeyDown(KeyRoutedEventArgs e) return; } + if (!IsEditing && IsRowKeyboardContext) + { + if (!shiftKey && + !ctrlKey && + SelectionMode == ListViewSelectionMode.Multiple && + e.Key == VirtualKey.Enter) + { + ToggleCurrentRowSelection(); + e.Handled = true; + return; + } + + var rowSelectionOnly = SelectionUnit == TableViewSelectionUnit.Row; + + if (e.Key is VirtualKey.Up or VirtualKey.Down or + VirtualKey.Home or VirtualKey.End or + VirtualKey.PageUp or VirtualKey.PageDown || + (rowSelectionOnly && (e.Key is VirtualKey.Left or VirtualKey.Right))) + { + int? prevCellRow = null; + if (SelectionUnit == TableViewSelectionUnit.Row && CurrentCellSlot.HasValue) + { + prevCellRow = CurrentCellSlot.Value.Row; + } + + base.OnKeyDown(e); // Let ListView move the row focus / selection + + var focusedIndex = GetFocusedRowIndex(); + if (focusedIndex >= 0) + { + CurrentRowIndex = focusedIndex; + SelectionStartRowIndex ??= focusedIndex; + } + + if (SelectionUnit == TableViewSelectionUnit.Row && + prevCellRow.HasValue && + focusedIndex >= 0 && + focusedIndex != prevCellRow.Value) + { + CurrentCellSlot = null; // will un-apply old cell's current-state border + } + return; + } + } + // Everything else (cell nav, F2 in cell mode, Space, etc.) await HandleNavigations(e, shiftKey, ctrlKey); } + + + private void ToggleCurrentRowSelection() + { + + var index = GetFocusedRowIndex(); + + if (index < 0) + { + var rowIndex = CurrentRowIndex ?? SelectedIndex; + + if (rowIndex is < 0 || rowIndex >= Items.Count) + { + return; + } + + index = rowIndex; + } + + + if (index < 0 || index >= Items.Count) { return; } + + var isSelected = SelectedRanges.Any(r => r.IsInRange(index)); + + var singleIndexRange = new ItemIndexRange(index, 1u); + + if (isSelected) + { + DeselectRange(singleIndexRange); + } + else + { + SelectRange(singleIndexRange); + } + + SelectionStartRowIndex = index; + CurrentRowIndex = index; + + } + /// /// Handles navigation keys. /// @@ -385,6 +478,34 @@ private TableViewCellSlot GetNextSlot(TableViewCellSlot? currentSlot, bool isShi return new TableViewCellSlot(nextRow, nextColumn); } + private int GetFocusedRowIndex() + { + if (XamlRoot is null) + return -1; + + var focused = FocusManager.GetFocusedElement(XamlRoot) as DependencyObject; + if (focused is null) + return -1; + + var row = GetRowFromElement(focused); + return row?.Index ?? -1; + } + + private static TableViewRow? GetRowFromElement(DependencyObject element) + { + var current = element; + + while (current is not null) + { + if (current is TableViewRow row) + return row; + + current = VisualTreeHelper.GetParent(current); + } + + return null; + } + /// /// Copies the selected rows or cells content to the clipboard. ///