-
Notifications
You must be signed in to change notification settings - Fork 59
Add ListView-like hotkeys support to TableView #251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -170,6 +170,16 @@ protected override DependencyObject GetContainerForItemOverride() | |||||||||||||||||||
| return row; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Gets a value indicating whether keyboard input should be handled in row context, | ||||||||||||||||||||
| /// i.e. when <see cref="UseListViewHotkeys"/> is enabled and the effective selection unit is a row. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| private bool IsRowKeyboardContext => | ||||||||||||||||||||
| UseListViewHotkeys && | ||||||||||||||||||||
| (SelectionUnit == TableViewSelectionUnit.Row || | ||||||||||||||||||||
| (SelectionUnit == TableViewSelectionUnit.CellOrRow && | ||||||||||||||||||||
| LastSelectionUnit == TableViewSelectionUnit.Row)); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /// <inheritdoc/> | ||||||||||||||||||||
| protected override void OnKeyDown(KeyRoutedEventArgs e) | ||||||||||||||||||||
| { | ||||||||||||||||||||
|
|
@@ -182,9 +192,92 @@ protected override 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.) | ||||||||||||||||||||
| 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; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
|
||||||||||||||||||||
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition check is redundant. The index value is already validated in lines 208-218 where it's either set from GetFocusedRowIndex() (which returns -1 if invalid) or from CurrentRowIndex/SelectedIndex (which are validated against the same bounds). If index passes the earlier checks, it cannot be less than 0 or greater than or equal to Items.Count at this point.
| if (index < 0 || index >= Items.Count) { return; } |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an unnecessary blank line at the end of this method. Remove it to maintain consistency with the coding style used elsewhere in the codebase.
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GetFocusedRowIndex method is missing XML documentation. Add a summary explaining that this method retrieves the index of the currently focused row, or returns -1 if no row has focus or if XamlRoot is not available.
| /// <summary> | |
| /// Retrieves the index of the currently focused row, or returns -1 if no row has focus or if XamlRoot is not available. | |
| /// </summary> | |
| /// <returns> | |
| /// The index of the currently focused row, or -1 if no row has focus or if XamlRoot is not available. | |
| /// </returns> |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GetRowFromElement method is missing XML documentation. Add a summary and param documentation explaining that this method traverses the visual tree upward from the given element to find the containing TableViewRow, returning null if no row is found.
| /// <summary> | |
| /// Traverses the visual tree upward from the specified element to find the containing <see cref="TableViewRow"/>. | |
| /// </summary> | |
| /// <param name="element">The starting element from which to search up the visual tree for a containing <see cref="TableViewRow"/>.</param> | |
| /// <returns> | |
| /// The <see cref="TableViewRow"/> that contains the provided element, or <c>null</c> if no containing row is found. | |
| /// </returns> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation for this property should be more specific and descriptive. It should explain what "ListView like hotkeys" means in practical terms, such as: "Gets or sets a value indicating whether the TableView uses ListView-style keyboard navigation in Multiple selection mode, where Up/Down move focus, Enter toggles selection, and Shift+Up/Down extends selection without deselecting other items." This helps developers understand when and why they would use this property.