Implement opt-in weak reference handling for ElementProxy in automation peers to mitigate memory leaks in virtualized ItemsControls#11638
Conversation
…on peers to mitigate memory leaks in virtualized ItemsControls
|
@dotnet-policy-service agree company="Microsoft" |
There was a problem hiding this comment.
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.ShouldUseWeakReferenceFromElementProxyhook (defaultfalse). - Overrides the hook in
ItemAutomationPeerto 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.
| // 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; } | ||
| } |
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
|
Closing in favor of #11657, which takes a different approach - actively disconnecting stale peers from UIA via |
Fixes #11337
Main PR
Description
UIA Core retains
ElementProxyinstances longer than WPF expects. SinceElementProxyheldItemAutomationPeervia a strong reference, this pinned the data item, the container, and the parentItemsControlAutomationPeer's caches - manifesting as large managed-heap growth in virtualizedItemsControls (DataGrid,TreeView, etc.).This PR extends the existing
WeakReferencelifetime model — already used forUIElement,ContentElement, andUIElement3DAutomationPeer- toItemAutomationPeerand its subclasses. Peer re-discovery after collection is preserved by the parentItemsControlAutomationPeer'sWeakRefElementProxyStorage, so the UIA tree remains correct: UIA clients receiveElementNotAvailableExceptionand re-walk as they already do forUIElement-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.
ElementProxystill holdsItemAutomationPeerstrongly. The only added cost is one virtual property read in theElementProxyconstructor, which returnsfalseon the baseAutomationPeerand is hit only whenAutomationInteropReferenceType == ReferenceType.Weak.Opt-in behavior (switch on)
ItemAutomationPeeradopts the same lifetime model already used byUIElement,ContentElement, andUIElement3DAutomationPeer. Specifically:ItemsControlAutomationPeer'sWeakRefElementProxyStorage- the same mechanism that already supports theUIElement-derived peers under the existingWeakreference mode.ElementNotAvailableExceptionand re-walk, which is documented and expected UIA behavior.Scope of change
AutomationPeer(defaultfalse).ItemAutomationPeerthat reads the switch.ElementProxyconstructor.No public API changes. All new members are
internal.Residual risks considered
WeakRefElementProxyStorage; requires validation, but the recovery path is the same oneUIElementAutomationPeeralready relies on.ItemAutomationPeerderivatives (DataGridItemAutomationPeer, DataGridCellItemAutomationPeer,DateTimeAutomationPeer,TreeViewDataItemAutomationPeer`, etc.) automatically inherit the new behavior, which is the intent.Microsoft Reviewers: Open in CodeFlow