diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs index 98984b5c4bc..c838ea2cbd0 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/CoreAppContextSwitches.cs @@ -177,6 +177,24 @@ public static bool UseNetFx472CompatibleAccessibilityFeatures #endregion + #region UseLegacyAutomationPeerDisconnect + + /// + /// Switch to opt-out of the automation peer disconnect behavior. + /// When true, removed automation peers are NOT disconnected from UIA (legacy behavior). + /// When false (default), removed automation peers are disconnected via UiaDisconnectProvider. + /// + public static bool UseLegacyAutomationPeerDisconnect + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return AccessibilitySwitches.UseLegacyAutomationPeerDisconnect; + } + } + + #endregion + #endregion #region ShouldRenderEvenWhenNoDisplayDevicesAreAvailable and ShouldNotRenderInNonInteractiveWindowStation diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Automation/Peers/AutomationPeer.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Automation/Peers/AutomationPeer.cs index aa24199a22a..b5afd3d1914 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Automation/Peers/AutomationPeer.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Automation/Peers/AutomationPeer.cs @@ -1499,7 +1499,7 @@ private void EnsureChildren() _childrenValid = true; // Disconnect removed peers (same logic as UpdateChildrenInternal) - if (oldChildren != null) + if (oldChildren != null && !CoreAppContextSwitches.UseLegacyAutomationPeerDisconnect) { HashSet newSet = _children != null ? new HashSet(_children) : null; @@ -2007,7 +2007,7 @@ internal void UpdateChildrenInternal(int invalidateLimit) // never drops to zero, which prevents the managed peer (and its entire // visual sub-tree) from being garbage collected. // This must happen regardless of StructureChanged event registration. - if (removedCount > 0) + if (removedCount > 0 && !CoreAppContextSwitches.UseLegacyAutomationPeerDisconnect) { foreach (AutomationPeer removedChild in hs) { diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/AccessibilitySwitches.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/AccessibilitySwitches.cs index fc79753bb75..904363455d8 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/AccessibilitySwitches.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/AccessibilitySwitches.cs @@ -147,6 +147,29 @@ public static bool ItemsControlDoesNotSupportAutomation #endregion + #region UseLegacyAutomationPeerDisconnect + + internal const string UseLegacyAutomationPeerDisconnectSwitchName = "Switch.System.Windows.Automation.Peers.UseLegacyAutomationPeerDisconnect"; + private static int _UseLegacyAutomationPeerDisconnect; + + /// + /// Switch to opt-out of the automation peer disconnect behavior. + /// When true, compatibility mode is enabled: removed automation peers are NOT + /// disconnected from UIA, preserving the legacy element lifetime semantics. + /// When false (default), removed automation peers are disconnected via + /// UiaDisconnectProvider, allowing CCWs and associated managed objects to be GC'd. + /// + public static bool UseLegacyAutomationPeerDisconnect + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return LocalAppContext.GetCachedSwitchValue(UseLegacyAutomationPeerDisconnectSwitchName, ref _UseLegacyAutomationPeerDisconnect); + } + } + + #endregion + #endregion #region Switch Functions @@ -167,6 +190,7 @@ internal static void SetSwitchDefaults(string platformIdentifier, int targetFram LocalAppContext.DefineSwitchDefault(UseLegacyAccessibilityFeatures3SwitchName, false); LocalAppContext.DefineSwitchDefault(UseLegacyToolTipDisplaySwitchName, false); LocalAppContext.DefineSwitchDefault(ItemsControlDoesNotSupportAutomationSwitchName, false); + LocalAppContext.DefineSwitchDefault(UseLegacyAutomationPeerDisconnectSwitchName, false); } /// @@ -218,6 +242,10 @@ internal static void VerifyDependencies(Dispatcher dispatcher) { DispatchOnError(dispatcher, String.Format(SR.AccessibilitySwitchDependencyNotSatisfied, ItemsControlDoesNotSupportAutomationSwitchName, UseLegacyAccessibilityFeatures3SwitchName, 3)); } + if (!UseLegacyAutomationPeerDisconnect && UseNetFx472CompatibleAccessibilityFeatures) + { + DispatchOnError(dispatcher, String.Format(SR.AccessibilitySwitchDependencyNotSatisfied, UseLegacyAutomationPeerDisconnectSwitchName, UseLegacyAccessibilityFeatures3SwitchName, 3)); + } }