Skip to content

Implement opt-in weak reference handling for ElementProxy in automation peers to mitigate memory leaks in virtualized ItemsControls#11638

Open
amarinov-msft wants to merge 2 commits into
dotnet:mainfrom
amarinov-msft:amarinov-fix/itemautomationpeer-weakref
Open

Implement opt-in weak reference handling for ElementProxy in automation peers to mitigate memory leaks in virtualized ItemsControls#11638
amarinov-msft wants to merge 2 commits into
dotnet:mainfrom
amarinov-msft:amarinov-fix/itemautomationpeer-weakref

Conversation

@amarinov-msft
Copy link
Copy Markdown

@amarinov-msft amarinov-msft commented May 13, 2026

Fixes #11337

Main PR

Description

UIA Core retains ElementProxy instances longer than WPF expects. Since ElementProxy held ItemAutomationPeer via a strong reference, this pinned the data item, the container, and the parent ItemsControlAutomationPeer's caches - manifesting as large managed-heap growth in virtualized ItemsControls (DataGrid, TreeView, etc.).

This PR extends the existing WeakReference lifetime model — already used for UIElement, ContentElement, and UIElement3DAutomationPeer - to ItemAutomationPeer and its subclasses. Peer re-discovery after collection is preserved by the parent ItemsControlAutomationPeer's WeakRefElementProxyStorage, so the UIA tree remains correct: UIA clients receive ElementNotAvailableException and re-walk as they already do for UIElement-derived peers.

The behavior is gated by a new opt-in AppContext switch (Switch.System.Windows.Automation.Peers.UseWeakReferenceFromElementProxyToItemPeer), default off, so no existing application is affected unless it explicitly opts in.

Customer Impact

Currently it is not possible to test WPF/WinForms application with UI Automation in a leak-free way. This prevents early detection of memory leaks.

Regression

No

Testing

Risk

Low overall, because the change is opt-in and default-off behind a new AppContext switch
(Switch.System.Windows.Automation.Peers.UseWeakReferenceFromElementProxyToItemPeer).

Default behavior (switch off)

No code path changes. ElementProxy still holds ItemAutomationPeer strongly. The only added cost is one virtual property read in the ElementProxy constructor, which returns false on the base AutomationPeer and is hit only when AutomationInteropReferenceType == ReferenceType.Weak.

Opt-in behavior (switch on)

ItemAutomationPeer adopts the same lifetime model already used by UIElement, ContentElement, and UIElement3DAutomationPeer. Specifically:

  • Peer re-discovery is handled by the parent ItemsControlAutomationPeer's WeakRefElementProxyStorage - the same mechanism that already supports the UIElement-derived peers under the existing Weak reference mode.
  • UIA clients that race a GC and observe a collected peer receive ElementNotAvailableException and re-walk, which is documented and expected UIA behavior.

Scope of change

  • One new virtual property on AutomationPeer (default false).
  • One override on ItemAutomationPeer that reads the switch.
  • One extra clause in the ElementProxy constructor.
  • One new AppContext switch definition.

No public API changes. All new members are internal.

Residual risks considered

  • Accessibility regressions (Narrator/NVDA losing focus on a virtualized item) — mitigated by re-discovery via WeakRefElementProxyStorage; requires validation, but the recovery path is the same one UIElementAutomationPeer already relies on.
  • Subclass coverage — all ItemAutomationPeer derivatives (DataGridItemAutomationPeer, DataGridCellItemAutomationPeer, DateTimeAutomationPeer, TreeViewDataItemAutomationPeer`, etc.) automatically inherit the new behavior, which is the intent.
  • Servicing risk — none for shipped apps unless they explicitly opt in.
Microsoft Reviewers: Open in CodeFlow

…on peers to mitigate memory leaks in virtualized ItemsControls
@amarinov-msft
Copy link
Copy Markdown
Author

@dotnet-policy-service agree company="Microsoft"

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an opt-in AppContext switch to allow ElementProxy to hold ItemAutomationPeer (and subclasses) via WeakReference, reducing managed-heap retention/leaks in virtualized ItemsControl automation scenarios when UIA Core retains ElementProxy instances longer than expected.

Changes:

  • Introduces a new internal virtual AutomationPeer.ShouldUseWeakReferenceFromElementProxy hook (default false).
  • Overrides the hook in ItemAutomationPeer to read a new opt-in AppContext switch.
  • Extends ElementProxy’s weak-reference selection logic to include peers opting in via the new hook.

Reviewed changes

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

File Description
src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Automation/Peers/ItemAutomationPeer.cs Overrides the new hook to opt ItemAutomationPeer into weak-referencing based on the AppContext switch.
src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/FrameworkAppContextSwitches.cs Defines the new AppContext switch used to gate the behavior.
src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Automation/Peers/AutomationPeer.cs Adds the internal virtual hook to allow peers to opt in.
src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementProxy.cs Uses the new hook to decide when to store the peer as a WeakReference.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +158 to +164
// Gated by FrameworkAppContextSwitches.UseWeakReferenceFromElementProxyToItemPeer. When on,
// a long-lived UIA ElementProxy no longer pins the data item, container, or parent
// ItemsControlAutomationPeer caches; re-discovery goes via WeakRefElementProxyStorage on the parent.
internal override bool ShouldUseWeakReferenceFromElementProxy
{
get { return FrameworkAppContextSwitches.UseWeakReferenceFromElementProxyToItemPeer; }
}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

@amarinov-msft amarinov-msft added the PR metadata: Label to tag PRs, to facilitate with triage label May 13, 2026
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Included in test pass PR Proposed PR metadata: Label to tag PRs, to facilitate with triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WPF leaks ElementProxy instances when UI Automation is used

3 participants