* Add failing test for #7536
Reproduces the "Cannot change source while update is in progress" crash that
occurs when a SelectingItemsControl's ItemsSource is changed from within its own
SelectionChanged handler while the selection is being lost. When selection is
lost, CommitOperation bumps the operation UpdateCount before raising
LostSelection but never decrements it, so SelectionChanged is raised with
UpdateCount > 0 and SetSource throws.
Claude-Session: https://claude.ai/code/session_01FXP1ejZ9VhUwecY9QCRKVb
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Fix#7536: crash when changing source during SelectionChanged
When the selection was lost, SelectionModel.CommitOperation incremented the
operation's UpdateCount before raising LostSelection but never decremented it.
As a result the rest of the commit - including the SelectionChanged event - ran
with UpdateCount > 0, so a handler that changed the control's source (as
StructuredLogViewer's UpdateBreadcrumb does) hit the "Cannot change source while
update is in progress." guard in SetSource and threw.
Decrement UpdateCount again once the LostSelection handler has returned so that
the batching only covers the handler itself and the SelectionChanged event is
free to change the source.
Claude-Session: https://claude.ai/code/session_01FXP1ejZ9VhUwecY9QCRKVb
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Add test for changing selection from SelectionChanged when AlwaysSelected reselects
Covers the interaction between the #7536 fix and AlwaysSelected: clearing the
selection makes AlwaysSelected reselect the first item via LostSelection (which
must still fold into the current operation), and a SelectionChanged handler that
then changes the selection must be honoured rather than swallowed by the batch
update wrapping the LostSelection handler. Fails before the fix (selection stays
on the reselected item), passes after.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXP1ejZ9VhUwecY9QCRKVb
* Assert that `SelectionChanged` was called.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* Add BringIntoView pass to the LayoutManager
* Make ProcessBringIntoViewRequests part of the standard layout pass
* Clarify why TryScrollIntoViewNow's result is ignored
---------
Co-authored-by: grokys <grokys@users.noreply.github.com>
* Add failing test for #14718
* Fix for AutoScrollToSelectedItemIfNecessary
* fix failing CI build and move test to the right location
* add failing test for TabItem selection of invisble tab
* introduce a helper method to figure out which item to select
when nothing was selected beforehand and AlwaysSelected is true
* ensure selection works for invisible tabcontrol
* simplify conditions
* propose: Remove redundant logic from ColorView
The TabItem now handles the correct selection of only visible items
* fix test: Need to set SelectedIndex after adding items
* re-add unused method and make it obsolete
Otherwise API-diff will fail.
* Address review
* Update tests/Avalonia.Controls.UnitTests/TabControlTests.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update tests/Avalonia.Controls.UnitTests/TabControlTests.cs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Adress copilot review
* fix duplicate braces
* address review
* fix for failing tests on CarouselPage and TabbedPage
* address feedback
- adding more tests to avoid regressions
* Fix failing test: add UnitTestApplication.Start() to dedicated thread test
Agent-Logs-Url: https://github.com/timunie/Avalonia/sessions/fe2f1190-6d20-4982-8a03-1ae9b52ee701
Co-authored-by: timunie <47110241+timunie@users.noreply.github.com>
* Refactor SelectingItemsControl auto-scroll duplicate logic
Agent-Logs-Url: https://github.com/timunie/Avalonia/sessions/8895cb60-ef41-473c-972b-bad2483a5a77
Co-authored-by: timunie <47110241+timunie@users.noreply.github.com>
* Add new failing tests for AlwaysSelected mode
* Fix AlwaysSelected scenarios
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* fix: raise SelectionChanged event on collection Reset when items are deselected
When a collection bound to a SelectingItemsControl (e.g. ListBox) was
cleared via NotifyCollectionChangedAction.Reset, the SelectionChanged
event was not raised despite the selection being lost.
The root cause was in InternalSelectionModel.OnSourceReset: the base
SelectionModel.OnSourceReset() directly reset _selectedIndex to -1
before any Operation could capture the old selection state. The
subsequent SyncFromSelectedItems created an Operation that saw no
change (old and new both -1), so CommitOperation never fired
SelectionChanged.
The fix snapshots _writableSelectedItems before sync, diffs against
the post-sync state to find items that were actually lost (not merely
re-selected at a new index after reorder), and injects them as
DeselectedItems on the pending Operation — following the same pattern
used by OnSelectionRemoved for individual item removals.
Fixes#20897
* fix: use multiset diff to report lost duplicate selections on Reset
The Reset diff in InternalSelectionModel used a HashSet to detect
which previously-selected items were still present after sync. Selection
allows duplicates (same instance or equal items at multiple indices),
so set semantics collapsed duplicates into one entry and under-reported
deselections when only some occurrences were lost.
Track counts per item plus a null counter and decrement per match, so
RemovedItems reflects the actual number of lost selections.
Adds a duplicate-items Reset test covering the regression.
* fix: raise SelectionChanged for reset-lost selection
ListBox and other SelectingItemsControl callers did not receive SelectionChanged when a Reset cleared the selected items. The selection model reports this path through LostSelection, but the control only used that callback for AlwaysSelected recovery.
Track the last selected items at the control boundary, capture that snapshot for Reset notifications, and raise the routed SelectionChanged event when LostSelection commits during that reset. This avoids diffing reset contents while preserving the removed-items payload for clear/reset-to-empty cases.
* fix: address review feedback on SelectionChanged Reset snapshot
- Replace per-change ToArray() snapshot with persistent List<object?> to
avoid allocations on every selection change (review: MrJul).
- Read snapshot in PreCollectionChanged instead of Selection.SelectedItems
because the source is already empty by the time Reset fires.
- Align LostSelection event-raising with SelectionChanged path: use
BuildEventRoute + HasHandlers guard to avoid allocating args when
no handlers are attached (review: copilot).
- Harden existing Reset tests to Assert.Single to catch double-fire.
* fix: consolidate SelectionChanged raising and defend against stale snapshot
- Extract RaiseSelectionChanged helper so both the normal
(SelectionChanged) and reset (LostSelection) paths share the same
BuildEventRoute/HasHandlers guard and SelectionChangedEventArgs
construction (review: copilot).
- Move _selectedItemsBeforeReset clear outside the conditional in
OnSelectionModelLostSelection so the field is always nulled after
LostSelection, preventing accidental reuse (review: copilot).
- Add comment documenting the snapshot lifecycle in
OnItemsViewPreCollectionChanged.
* perf: defer SelectionChangedEventArgs allocations until handlers are confirmed
SelectionChangedEventArgs materialized arrays via ToArray() at the call site
before checking whether the routed event had any registered handlers. This
allocated needlessly in the common case of no external subscribers.
The event items (IReadOnlyList<object?>) already implement IList via
ReadOnlySelectionListBase. RaiseSelectionChanged now accepts
IReadOnlyList<object?> and casts to IList inside the HasHandlers gate,
falling back to ToArray() only for non-IList enumerables.
* Extracted IInputRoot out of TopLevel
* Move some input handling out of TopLevel
* Remove old class, make layout manager private
* Removed IRenderRoot
* Make VisualTreeAttachmentEventArgs a bit more sensible
* Move ILayoutRoot to PresentationSource
# Conflicts:
# tests/Avalonia.Controls.UnitTests/TabControlTests.cs
* Updated some VisualRoot / GetVisualRoot usages
* Updated more XxxRoot usages
* More Root usages
* Addressed review
* Hurr-durr xml
* More fixes
* Maybe fix android compilation
* API diff
* Yet another cast
* I had to use MSIL analysis to detect those casts
* Fixed automation
* Fix PointerOverPreProcessor
* Fix?
* Removed yet another cast to Visual
* The amount of random downcasts is astonishing
* Maybe fix mac
* Addressed review
* Update to xunit.v3
* Little more progress
* More fixes
* Keep VSTest supported
* Adjust Nuke
* Few fixes
* Fix for xunit 2
* Fix GetData override
* Adjust
* Use MTP for xunit 2
* Fix test
* Better fix
* --no-progress
* Few more fixes
* no progress
* Fix test
* Better fix
* TRX
* Move to Directory.Build.props
* Unify on MTP v2
* Update
* Update to stable
* 1.0.1
* 1.0.2
* Fix some warnings
* Fix more warnings
* Fix more warnings
* Add failing tests for SelectedItem/SelectedIndex without an ItemsSource
* Keep SelectedItem/SelectedIndex until ItemsSource is set
* Add failing tests for setting SelectedValue without an ItemsSource
* Keep SelectedValue until ItemsSource is set
* Added failing tests for #12733.
* Clear SkipLostSelection on batch update start.
If `Source` is changed during a collection update, then the `Clear()` operation will not be committed immediately due to `_isSourceCollectionChanging` being set. In this case, `update.Operation` will still have `SkipLostSelection == true`, meaning that `LostSelection` will not be raised, causing #12733. Clear the flag manually each time `BeginBatchUpdate` is called to avoid this.
Fixes#12733
---------
Co-authored-by: Max Katz <maxkatz6@outlook.com>
* Add SelectingItemsControl property init order tests
* Property order in SelectedItemsControl doesn't matter on init
* Fix SelectedItemsControl properties during init when Selection is set
* Fixed SelectedItemsControl.AnchorIndex after init
* Add failing unit test for scenario 1 in #11878.
* Set TabOnceActiveElement on realized container.
Fixes scenario 1 in #11878.
* Use TabOnceActiveElement to decide focused element.
Fixes scenario #3 in #11878.
- animation/layout/render cycle is now managed from a central location
- animations are now throttled if animation/layout/render pass takes longer than a frame which previously caused a soft-freeze with input not being processed
- the public API is trimmed to make sure that we can make other planned changes during the 11.x support cycle
"Changelog":
- IClock is hidden and is planned to be replaced later
- Animator classes are hidden and are planned to be refactored later
- IAnimation members are hidden, it's supposed to be a marker interface for Style.Animations collection now, to start animations manually use Animation.RunAsync
- Sealed several classes in Avalonia.Animation namespace
- Spring class is removed from the public API (it wasn't possible to use it directly in a meaningful way anyway)
- Sealed brushes, transforms, effects and drawings
- Removed separate dispatcher priorities for Layout and Composition, everything now happens from a central place with Render priority (same as WPF)
- - some private "hook" priorities are added for now, those will be removed later
- IRenderLoop is hidden and removed from locator
- IRenderer is hidden (the plan is to remove that concept later)
- - Renderer.Start/Stop exposed as StartRendering/StopRendering on the toplevel (will be on a CompositionTarget/PresentationSource-like type later)
- - Renderer.Diagnistics exposed as RendererDiagnostics (same)
- - Renderer is no longer created by the platform code and is created by TopLevel itself
- - From the user-code hit-testing should be done by VisualExtensions.GetVisual(s)At, which has the same features
- - For unit tests a separate IHitTester interface is added which can be changed for a particular toplevel
- ILayoutManager is hidden
- - LayoutManager.ExecuteLayoutPass() exposed as TopLevel.UpdateLayout()
- Custom animators now have a separate base class that only deals with interpolation
Minor improvements:
- Compositor has a mode that doesn't use DispatcherTimers, useful for unit tests
- Introduced ScopedTestBase that auto-resets the locator when test is finished
- Don't set `KeyboardNavigationMode.Once` on `ItemsPresenter`
- Instead set it on `ListBox` (more controls to come)
- Make `TabOnceActiveElement` follow `Selection.AnchorIndex` in `SelectingItemsControl` and set it on `ItemsControl` itself
One shouldn't call `ClearContainer` on a container that is an item. Adjusted `SelectingItemsControlTests` because selection is actually maintained on move with containers hold their own `IsSelected` state.
Fixes#11128
Rather than using the `ISelectable` interface to communicate container selection from the `SelectingItemsControl` to the container, use the `SelectingItemsControl.IsSelected` attached property, setting it with `SetCurrentValue` so that bindings defined in a style or item container theme can override the selection. Required an extra virtual `ContainerForItemPreparedOverride` method on `ItemsControl`.
A lot still broken, in particular virtualization is completely removed.`ItemsPresenter` now no longer has an `Items` or `ItemTemplate` property; it detects when it's hosted in an `ItemsControl`. `IItemsPresenter` interface removed.
- Removes the `IStyler` service and the `Styler` implementation
- Moves the logic for applying styles and control themes into `StyledElement`
- Removes the style `TryAttach` method from the public API
- Removes style caching for now - this will need to be added back
A few `AutoScrollToSelectedItem` improvements:
- Scroll to current selected item when it's set to true
- Scroll to current selected item when list first displayed
- Scroll to current selected item when attached to visual tree if the selection was changed while it wasn't attached
Fixes#4100