Skip to content

Perf/smooth column resize - #420

Merged
w-ahmad merged 6 commits into
mainfrom
perf/smooth-column-resize
Aug 9, 2026
Merged

Perf/smooth column resize#420
w-ahmad merged 6 commits into
mainfrom
perf/smooth-column-resize

Conversation

@w-ahmad

@w-ahmad w-ahmad commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Description

This is primarily a performance improvement for column resizing, plus one issue fix.

Performance improvement (the main change): dragging a column divider used to relayout every visible row's cell on every pointer-move frame, capping out around 5fps regardless of row count — the bottleneck is the number of Measure/Arrange calls crossing into the WinUI layout pipeline per frame, not extra work inside each call. This PR adds a ColumnResizeMode toggle with two strategies:

  • Preview — a composition-only illusion during the drag: each resized cell gets its own Clip/RenderTransform that reveals/hides already-rendered content and shifts downstream cells, so cells still visibly resize live, but no Measure/Arrange runs until the drag ends. The real width is committed once, in a single layout pass, on release.
  • Live — the original, fully real-time behavior (every frame is a real layout pass), now with three unrelated per-row-per-frame costs trimmed: a forced row-header remeasure, a drag-selection position cache, and a CellsHorizontalOffset recompute — none of which ever depended on a data column's width, so they were pure waste during a drag. Live is the default.

The fix: a recycled TableViewRow container could keep a stale cell Width if it missed a Column.ActualWidth change while off-screen (e.g. an auto-width recalculation triggered by a sort), leaving cells misaligned with headers after scrolling both directions then sorting. Fixed by re-syncing width on every container reuse.

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 📝 Documentation update
  • ♻️ Refactor
  • 🧪 Test
  • 🔧 Chore / maintenance

Checklist

  • This PR is not from my main branch
  • Tested with WinUI target
  • Tested with Uno Platform target
  • Unit / integration tests added or updated
  • Documentation updated to reflect changes
  • Code follows the project's coding conventions

Screenshots / Recordings

w-ahmad and others added 5 commits August 8, 2026 03:19
…eview

- Widen the resize hit zone (4px -> 6px, 8px touch) so the cursor is easier
  to trigger near a column header edge.
- Track resize drags via PointerMoved instead of ManipulationDelta for lower
  latency, and keep the resize cursor asserted for the whole drag instead of
  reverting mid-drag.
- Fix cell/header misalignment after horizontal+vertical scroll followed by
  a sort: a recycled row container could keep a stale cell Width if it
  missed a Column.ActualWidth change while off-screen.
- Replace real per-frame relayout during a resize drag (which capped out
  around 15fps regardless of row count) with a composition-only preview:
  cells get a per-cell Clip/RenderTransform that reveals/hides
  already-rendered content and shifts downstream cells, with the single
  real Width/ActualWidth commit happening once when the drag ends. Cells
  still visibly resize live, but no Measure/Arrange runs during the drag.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add TableViewColumnResizeMode with two options: Live (real relayout
  every frame, the default) and Preview (the composition-only illusion
  from the previous commit). TableViewColumnHeader now dispatches to
  whichever mode was active at the start of each resize drag.
- Reintroduce per-frame optimizations that hadn't survived the earlier
  clean rebuild: skip the forced row-header remeasure, the drag-selection
  position cache update, and the CellsHorizontalOffset recompute while a
  resize is in progress — none of them depend on a data column's width,
  so recomputing them on every row on every drag frame was pure waste.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Document the new Live/Preview ColumnResizeMode in column-sizing.md,
  and point to it from performance.md's guidance on resize-drag cost.
- Add a Column Resize Mode combo box to the sample app's Column Sizing
  page, matching the existing Column Auto Width Mode picker.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Keep the mouse resize hit zone at its original 4px; only the touch hit
zone stays widened at 8px.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lumn

GetColumnDesiredWidth used to force its own unconstrained header
Measure on every CalculateHeaderWidths() pass, duplicating the measure
TableViewColumnHeader.MeasureOverride already does. It now reuses a
cached CachedDesiredWidth from that measure instead, falling back to a
forced measure only when a header hasn't been measured yet (e.g. one
just added). Halves the per-Auto-column cost of every recalculation.

Also skip updating the frozen-column scrollbar margin entirely when
there are no frozen columns.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds configurable column-resize behavior, improving drag performance while preserving Live mode and fixing recycled-cell width alignment.

Changes:

  • Adds Live and composition-based Preview resize modes.
  • Removes unnecessary per-frame layout work and resynchronizes recycled cells.
  • Adds tests, documentation, and a sample selector.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/TableViewColumnResizingTests.cs Adds resize and alignment tests.
src/TableViewRowPresenter.cs Skips redundant resize-time work.
src/TableViewRow.cs Resynchronizes recycled cells and optimizes arrangement.
src/TableViewHeaderRow.cs Caches header measurements.
src/TableViewColumnResizeMode.cs Defines Live and Preview modes.
src/TableViewColumnHeader.cs Implements resize gesture orchestration.
src/TableViewCell.cs Implements clip/transform previews.
src/TableView.Properties.cs Exposes the resize-mode property.
src/TableView.cs Coordinates preview and live resizing.
src/Columns/TableViewColumn.cs Tracks active resize state.
samples/WinUI.TableView.SampleApp/Pages/ColumnSizingPage.xaml Demonstrates mode selection.
docs/docs/performance.md Documents resize performance.
docs/docs/column-sizing.md Documents mode behavior and usage.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/TableView.cs Outdated
{
if (_scrollViewer is null) return;
var frozenColumns = Columns.VisibleColumns.Where(c => c.IsFrozen);
if (_scrollViewer is null || !frozenColumns.Any()) return;
Comment thread src/TableView.cs Outdated

foreach (var cell in _resizingPreviewCells)
{
cell.UpdateResizePreviewClip(liveWidth, _resizingClipHeight);
Comment thread src/TableViewCell.cs
desiredWidth += _selectionBorder?.BorderThickness.Right ?? 0;
desiredWidth += _v_gridLine?.ActualWidth ?? 0d;

_resizePreviewWidth = Math.Min(maxPreviewWidth, Math.Max(ActualWidth, desiredWidth));
Comment thread tests/TableViewColumnResizingTests.cs Outdated
Comment on lines +417 to +419
var boundColumn = tableView.Columns.OfType<TableViewBoundColumn>().First();
tableView.SortDescriptions.Add(
new ColumnSortDescription(boundColumn, boundColumn.PropertyPath, SortDirection.Ascending));
… border arrangement, sort test

Co-authored-by: w-ahmad <17172092+w-ahmad@users.noreply.github.com>
@w-ahmad
w-ahmad marked this pull request as ready for review August 9, 2026 12:10
@w-ahmad
w-ahmad merged commit cde19f8 into main Aug 9, 2026
11 checks passed
@w-ahmad
w-ahmad deleted the perf/smooth-column-resize branch August 9, 2026 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants