Sorting reorders the assembly list in place and reports it as a Move, which
nothing downstream could act on: the tree node's handler had cases for Add,
Remove and Reset only, and neither the child collection nor the flattener had a
move at all. The rows therefore kept their pre-sort order until something else
forced a rebuild. Moving the node rather than removing and re-inserting it keeps
its identity, so an expanded subtree stays expanded and its row is not rebuilt.
A run longer than one row has to be reported the way the consumer reads it:
Avalonia's VirtualizingStackPanel applies a ranged move by removing OldItems.Count
rows and re-inserting them at NewStartingIndex - (Count - 1), so a run reported by
its final start index lands short by its own length. For a single row - a
collapsed node, and every move a sort makes - the two readings coincide.
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
NodesInserted/NodesRemoved reported a multi-node change (a node plus its visible
descendants -- a contiguous run) as a sequence of single-item Add/Remove events,
but the underlying tree updates Count by the WHOLE run before they fire. A
consumer that reconciles the change one item at a time and re-indexes the source
mid-sequence then reads against a Count that has already dropped by the full run:
Avalonia's SelectionModel does exactly this (it re-reads SelectedItems while
handling each Remove), indexing past the end and throwing ArgumentOutOfRangeException
out of TreeFlattener's indexer whenever a selection was live during a bulk reshape
(e.g. toggling UseNestedNamespaceNodes). It only surfaced under full-suite timing,
which is why it read as flaky.
Raise one ranged event for the whole run instead, so the notification matches the
tree's state in a single step and Count/indices stay in agreement. The indexer
keeps throwing on genuine out-of-range access. Avalonia 12.4's virtualization and
selection handle ranged Add/Remove (every expand/collapse exercises it); full
headless suite is green.
* Changes necessary for making SharpTreeNode cross platform by proxying System.Windows dependencies
* Add ITreeNodeImagesProvider for node icons
* Move InternalsVisibleTo to csproj (possible since net50)
* Move view models and other xplat class for SharpTreeView to ILSpyX, Windows-dependent classes to ILSpy/Controls/TreeView
* Move GetDoubleClickTime to NativeMethods