feat: allow drag selection to start from rows and empty space - #417
Draft
w-ahmad wants to merge 11 commits into
Draft
feat: allow drag selection to start from rows and empty space#417w-ahmad wants to merge 11 commits into
w-ahmad wants to merge 11 commits into
Conversation
CS1591 build error — publicly visible override was missing an inheritdoc comment. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR enhances TableView’s drag-selection so selection gestures can start from row containers and empty list space (not only cells), while centralizing selection/drag logic into TableView and introducing coordinate-based range helpers to support rectangular cell selection.
Changes:
- Introduces
TableViewCellSlotRange(+ range extensions) and index-range helpers to model rectangular cell selections and perform range arithmetic. - Reworks pointer/drag-selection handling in
TableViewto support starting drags from rows and empty space, and refactors selection state updates. - Updates and adds tests to cover range-based selection APIs (
SelectCellRange/DeselectCellRange) and drag-selection behaviors.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/TableViewSelectionUnitTests.cs | Updates selection tests to use range-based APIs. |
| tests/DragSelectionRectangleTests.cs | Adds tests for SelectCellRange / DeselectCellRange event behavior and selection results. |
| src/TableViewRow.cs | Removes row-level pointer handling now owned by TableView. |
| src/TableViewCellSlotRange.cs | Adds new public range type representing rectangular cell-slot selections. |
| src/TableViewCellSlot.cs | Adds ToString() for improved diagnostics/logging. |
| src/TableViewCell.cs | Removes selection/drag logic from cell and adjusts pointer/edit handling. |
| src/TableView.Properties.cs | Migrates internal selection storage to HashSet<TableViewCellSlotRange>. |
| src/TableView.Events.cs | Adds debug tracing for cell selection changed notifications. |
| src/TableView.cs | Major drag-selection + hit-testing rework; adds range-based selection/deselection APIs and selection-state dispatching. |
| src/Helpers/TableViewTrace.cs | Adds DEBUG-only tagged tracing helper. |
| src/Helpers/IndexRangeHelper.cs | Adds helper to build contiguous ItemIndexRange sequences from indexes. |
| src/Extensions/TableViewCellSlotRangeExtensions.cs | Adds range operations (contains/intersects/subtract/merge, slot enumeration, validity). |
| src/Extensions/ItemIndexRangeExtensions.cs | Adds Contains/Subtract and refines IsValid signature for selection logic. |
…logic - Normalize FromCoordinates() to use Math.Min for firstRow/firstCol so ranges with start > end (e.g. reverse drag) compute correct bounds. - Guard TableView_SelectionChanged against zero visible columns before creating a full-row cell slot range (avoids ArgumentOutOfRangeException). - Fix SelectRowsInDragRect: remove invalid null-conditionals on the non-nullable ItemIndexRange parameter; use .Value on the nullable field. - Fix DeselectCell to subtract only the 1x1 cell range from each containing selection range instead of removing the whole range. - Fix EndDragSelection: clear _pointerCaptureElement after releasing captures and remove the dead _tableViewDragPointer cleanup block. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
ItemIndexRange is a class, not a struct, so .Value does not exist on a nullable reference. Revert to calling Contains/Subtract directly on the already-null-checked _lastDragSelectionRowRange. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (4)
src/TableView.cs:325
- During row drag-selection,
CurrentRowIndex(andSelectedIndex) are never updated when selection is driven bySelectRange/DeselectRange. Keyboard navigation usesCurrentRowIndexwhenLastSelectionUnitis Row, so after a drag-select the arrow keys can start navigating from a stale row index.
if (LastSelectionUnit is not TableViewSelectionUnit.Cell && GetRowIndexAtCanvasPoint(_lastDragCanvasPoint.Value) is int row)
{
SelectionStartRowIndex ??= row;
var minRow = Math.Min(SelectionStartRowIndex.Value, row);
var maxRow = Math.Max(SelectionStartRowIndex.Value, row);
src/TableView.cs:1635
OnCellSelectionChangedsets_cellStateDispatchPending = trueand then ignores the return value ofDispatcherQueue.TryEnqueue. If enqueue fails (e.g., dispatcher shutting down),_cellStateDispatchPendingstays true and pending row visual updates will never run again.
if (!_cellStateDispatchPending)
{
_cellStateDispatchPending = true;
DispatcherQueue.TryEnqueue(ApplyPendingCellStates);
}
src/Extensions/ItemIndexRangeExtensions.cs:62
Subtract()returns an empty sequence when the ranges don’t overlap. For subtraction semantics, “no overlap” should normally return the originalrangeunchanged; returning nothing can make callers accidentally drop the entire range.
// No overlap.
if (otherEnd < start || otherStart > end)
{
yield break;
}
src/TableView.cs:235
- Local variable names in
OnAnyPointerPressedcontain typos (isShiftkey,orignalSoruce). This makes the code harder to read and propagates the typo into subsequent checks.
var ctrlKey = KeyboardHelper.IsCtrlKeyDown();
var isShiftkey = KeyboardHelper.IsShiftKeyDown();
var orignalSoruce = e.OriginalSource as FrameworkElement;
…er invalid item indexes - Release pointer captures immediately if StartDragSelection returns early (SelectionMode not Multiple/Extended), preventing a stuck capture. - Filter out Items.IndexOf() results of -1 in the Ctrl SelectionChanged path to avoid passing invalid row indexes to TableViewCellSlotRange. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
w-ahmad
marked this pull request as draft
August 3, 2026 12:18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Improves the drag-selection experience in
TableViewthat was added in PR #344 by allowing selection gestures to begin from row containers and empty space — not just individual cells. This makes the control feel much more natural and consistent with other data-grid implementations.Recording.2026-07-30.034511.mp4
Changes
New Types
TableViewCellSlotRangeEquals,GetHashCode, equality operators, and factory helpers (FromCoordinates,FromSlots).TableViewCellSlotRangeExtensionsTableViewCellSlotRange:IsInRange,IsRowInRange,IsColumnInRange,IsValid,GetSlots,Contains,IntersectsWith,Subtract,Merge.IndexRangeHelperTableViewTrace[Conditional("DEBUG")]trace helper that writes tagged messages toDebug.WriteLine.Core Changes
TableView.cs– Major rework of drag-selection hit-testing so that pointer-down events onTableViewRowcontainers and blank list areas correctly initiate a selection rectangle, with full row/column range tracking.TableViewCell.cs– Selection-related logic extracted out of the cell and centralised inTableView, reducing the cell to its display responsibilities.TableViewRow.cs– Removed redundant pointer-event handling that is now owned byTableView.TableViewCellSlot.cs– Minor adjustments to align with the new range types.ItemIndexRangeExtensions.cs– Extended to support the new selection model.Tests
DragSelectionRectangleTests.cs– New tests covering drag-selection rectangle geometry.TableViewSelectionUnitTests.cs– Updated to cover selection scenarios that start outside individual cells.Sample App
OverviewPageandExampleModelColumnsHelperupdated to demonstrate the improved drag-selection behaviour.Checklist
TableViewTrace(no production overhead)