Skip to content

Preserve behavior visual lifecycle on virtualized containers - #360

Merged
wieslawsoltes merged 2 commits into
masterfrom
agent/336-virtualized-context-drag
Aug 19, 2026
Merged

wieslawsoltes merged 2 commits into
masterfrom
agent/336-virtualized-context-drag

Conversation

@wieslawsoltes

Copy link
Copy Markdown
Owner

Summary

Keeps template-created behaviors operational when Avalonia replaces a BehaviorCollection on a control that is already attached to the visual tree. This fixes ContextDragBehavior on virtualized ListBoxItem containers after scrolling/recycling.

Reproduction analysis

The attached reproduction uses a BehaviorCollectionTemplate in a ListBoxItem style with Avalonia 11.3.13. Initially realized containers work, but newly visible items after scrolling do not start a drag.

The failure is caused by collection replacement on an already-attached item container:

  1. The old collection is detached through Interaction.BehaviorsProperty.
  2. BehaviorCollection.Detach() calls each behavior's normal detach hook, but it does not send OnDetachedFromVisualTree.
  3. The new collection is attached, but no future AttachedToVisualTree event is guaranteed for a recycled container that remains in the tree.
  4. ContextDragBehaviorBase installs/removes pointer handlers in those visual-tree hooks.
  5. The old handler can remain installed while the new drag behavior never installs its handlers.

Avalonia 12 currently realizes fresh containers in the equivalent headless scenario, so scrolling alone does not force that older recycling path. The lifecycle defect remains reproducible on current master by replacing the template-created collection on an attached, newly visible item—the exact state transition performed by the older virtualizer.

Changes

  • Detect whether the Interaction.Behaviors owner is currently attached to a visual tree.
  • Before detaching a replaced collection, send its visual-detach lifecycle callback.
  • After attaching the replacement collection and wiring owner events, send its visual-attach lifecycle callback.
  • Preserve the existing behavior for collections assigned before visual attachment; they still receive the normal future tree event.
  • Correct the drag/drop documentation to use BehaviorCollectionTemplate inside a style instead of sharing a stateful BehaviorCollection.
  • Document virtualized-container lifecycle behavior.

Tests

Core lifecycle regression

A new interactivity test shows a window, replaces a button's behavior collection while the button remains attached, and verifies:

  • the old behavior receives one visual detach before normal detachment;
  • the old behavior releases its associated object;
  • the new behavior receives normal attach plus one visual attach;
  • the new behavior remains associated with the visible control.

Virtualized ContextDrag regression

A headless test mirrors the issue's 100-item virtualized ListBox and style-based BehaviorCollectionTemplate, scrolls to a newly visible item, replaces its template collection while attached, and verifies:

  • old ContextDragBehaviorBase visual lifecycle is balanced;
  • the replacement behavior is visually attached;
  • a pointer press on the selected item is handled by the replacement drag behavior.

Validation:

  • dotnet test tests/Xaml.Behaviors.Interactivity.UnitTests/Xaml.Behaviors.Interactivity.UnitTests.csproj --no-restore
    • 94 passed, 0 failed
  • dotnet test tests/Xaml.Behaviors.Interactions.UnitTests/Xaml.Behaviors.Interactions.UnitTests.csproj --no-restore
    • 90 passed, 2 existing drag tests skipped, 0 failed

Closes #336

@wieslawsoltes
wieslawsoltes marked this pull request as ready for review July 14, 2026 22:18
@wieslawsoltes
wieslawsoltes merged commit 9cb4789 into master Aug 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ContextDragBehavior from ListBox doesn't work when scrolling

1 participant