diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index d0c8d7a9dbf..9fe0ff3c60b 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -73,6 +73,13 @@ + (AvnAccessibilityElement *)acquire:(IAvnAutomationPeer *)peer if (peer->IsRootProvider()) { auto window = peer->RootProvider_GetWindow(); + + if (window == nullptr) + { + NSLog(@"IRootProvider.PlatformImpl returned null or a non-WindowBaseImpl."); + return nil; + } + auto holder = dynamic_cast(window); auto view = holder->GetNSView(); return [[AvnRootAccessibilityElement alloc] initWithPeer:peer owner:view]; @@ -284,8 +291,8 @@ - (id)accessibilityTopLevelUIElement - (id)accessibilityWindow { - id topLevel = [self accessibilityTopLevelUIElement]; - return [topLevel isKindOfClass:[NSWindow class]] ? topLevel : nil; + auto rootPeer = _peer->GetVisualRoot(); + return [AvnAccessibilityElement acquire:rootPeer]; } - (BOOL)isAccessibilityExpanded diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs index 3d3fe35d299..fb7cdd87edb 100644 --- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Avalonia.Automation.Provider; namespace Avalonia.Automation.Peers { @@ -115,9 +116,14 @@ public abstract class AutomationPeer /// /// Gets the that is the parent of this . /// - /// public AutomationPeer? GetParent() => GetParentCore(); + /// + /// Gets the that is the root of this 's + /// visual tree. + /// + public AutomationPeer? GetVisualRoot() => GetVisualRootCore(); + /// /// Gets a value that indicates whether the element that is associated with this automation /// peer currently has keyboard focus. @@ -247,6 +253,21 @@ protected virtual AutomationControlType GetControlTypeOverrideCore() return GetAutomationControlTypeCore(); } + protected virtual AutomationPeer? GetVisualRootCore() + { + var peer = this; + var parent = peer.GetParent(); + + while (peer.GetProvider() is null && parent is not null) + { + peer = parent; + parent = peer.GetParent(); + } + + return peer; + } + + protected virtual bool IsContentElementOverrideCore() { return IsControlElement() && IsContentElementCore(); diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs index c19d8872307..69f267a605f 100644 --- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs @@ -120,6 +120,13 @@ protected override IReadOnlyList GetOrCreateChildrenCore() return _parent; } + protected override AutomationPeer? GetVisualRootCore() + { + if (Owner.GetVisualRoot() is Control c) + return CreatePeerForElement(c); + return null; + } + /// /// Invalidates the peer's children and causes a re-read from . /// diff --git a/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs index db16bf0a538..64727c43c5a 100644 --- a/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs @@ -28,7 +28,7 @@ protected virtual IScrollProvider? Scroller if (!_searchedForScrollable) { if (Owner.GetValue(ListBox.ScrollProperty) is Control scrollable) - _scroller = GetOrCreate(scrollable) as IScrollProvider; + _scroller = GetOrCreate(scrollable).GetProvider(); _searchedForScrollable = true; } diff --git a/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs index aea91b5e268..dab8c455671 100644 --- a/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs @@ -22,7 +22,7 @@ public ISelectionProvider? SelectionContainer if (Owner.Parent is Control parent) { var parentPeer = GetOrCreate(parent); - return parentPeer as ISelectionProvider; + return parentPeer.GetProvider(); } return null; diff --git a/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs index 9ec65592fa2..ceb695422de 100644 --- a/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.Globalization; using Avalonia.Automation.Provider; using Avalonia.Controls; using Avalonia.Input; @@ -32,7 +33,21 @@ protected override AutomationControlType GetAutomationControlTypeCore() public AutomationPeer? GetPeerFromPoint(Point p) { var hit = Owner.GetVisualAt(p)?.FindAncestorOfType(includeSelf: true); - return hit is object ? GetOrCreate(hit) : null; + + if (hit is null) + return null; + + var peer = GetOrCreate(hit); + + while (peer != this && peer.GetProvider() is { } embedded) + { + var embeddedHit = embedded.GetPeerFromPoint(p); + if (embeddedHit is null) + break; + peer = embeddedHit; + } + + return peer; } protected void StartTrackingFocus() diff --git a/src/Avalonia.Controls/Automation/Provider/IEmbeddedRootProvider.cs b/src/Avalonia.Controls/Automation/Provider/IEmbeddedRootProvider.cs new file mode 100644 index 00000000000..1b1caef182f --- /dev/null +++ b/src/Avalonia.Controls/Automation/Provider/IEmbeddedRootProvider.cs @@ -0,0 +1,33 @@ +using System; +using Avalonia.Automation.Peers; + +namespace Avalonia.Automation.Provider +{ + /// + /// Exposure methods and properties to support UI Automation client access to the root of an + /// automation tree hosted by another UI framework. + /// + /// + /// This interface is implemented by the class, and can be used + /// to embed an automation tree from a 3rd party UI framework that wishes to use Avalonia's + /// automation support. + /// + public interface IEmbeddedRootProvider + { + /// + /// Gets the currently focused element. + /// + AutomationPeer? GetFocus(); + + /// + /// Gets the element at the specified point, expressed in top-level coordinates. + /// + /// The point. + AutomationPeer? GetPeerFromPoint(Point p); + + /// + /// Raised by the automation peer when the focus changes. + /// + event EventHandler? FocusChanged; + } +} diff --git a/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs b/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs index ce380595596..6a266da5c50 100644 --- a/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs +++ b/src/Avalonia.Controls/Automation/Provider/IRootProvider.cs @@ -4,11 +4,36 @@ namespace Avalonia.Automation.Provider { + /// + /// Exposes methods and properties to support UI Automation client access to the root of an + /// automation tree. + /// + /// + /// This interface is implemented by the class, and should only + /// be implemented on true root elements, such as Windows. To embed an automation tree, use + /// instead. + /// public interface IRootProvider { + /// + /// Gets the platform implementation of the TopLevel for the element. + /// ITopLevelImpl? PlatformImpl { get; } + + /// + /// Gets the currently focused element. + /// AutomationPeer? GetFocus(); + + /// + /// Gets the element at the specified point, expressed in top-level coordinates. + /// + /// The point. AutomationPeer? GetPeerFromPoint(Point p); + + /// + /// Raised by the automation peer when the focus changes. + /// event EventHandler? FocusChanged; } } diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs index 6c4e96b31be..af4958b02fe 100644 --- a/src/Avalonia.Native/AvnAutomationPeer.cs +++ b/src/Avalonia.Native/AvnAutomationPeer.cs @@ -22,8 +22,8 @@ private AvnAutomationPeer(AutomationPeer inner) { _inner = inner; _inner.ChildrenChanged += (_, _) => Node?.ChildrenChanged(); - if (inner is WindowBaseAutomationPeer window) - window.FocusChanged += (_, _) => Node?.FocusChanged(); + if (inner is IRootProvider root) + root.FocusChanged += (_, _) => Node?.FocusChanged(); } ~AvnAutomationPeer() => Node?.Dispose(); @@ -39,6 +39,7 @@ private AvnAutomationPeer(AutomationPeer inner) public IAvnAutomationPeer? LabeledBy => Wrap(_inner.GetLabeledBy()); public IAvnString Name => _inner.GetName().ToAvnString(); public IAvnAutomationPeer? Parent => Wrap(_inner.GetParent()); + public IAvnAutomationPeer? VisualRoot => Wrap(_inner.GetVisualRoot()); public int HasKeyboardFocus() => _inner.HasKeyboardFocus().AsComBool(); public int IsContentElement() => _inner.IsContentElement().AsComBool(); @@ -48,6 +49,13 @@ private AvnAutomationPeer(AutomationPeer inner) public void SetFocus() => _inner.SetFocus(); public int ShowContextMenu() => _inner.ShowContextMenu().AsComBool(); + public void SetNode(IAvnAutomationNode node) + { + if (Node is not null) + throw new InvalidOperationException("The AvnAutomationPeer already has a node."); + Node = node; + } + public IAvnAutomationPeer? RootPeer { get @@ -55,7 +63,7 @@ public IAvnAutomationPeer? RootPeer var peer = _inner; var parent = peer.GetParent(); - while (peer is not IRootProvider && parent is not null) + while (peer.GetProvider() is null && parent is not null) { peer = parent; parent = peer.GetParent(); @@ -65,26 +73,23 @@ public IAvnAutomationPeer? RootPeer } } - public void SetNode(IAvnAutomationNode node) - { - if (Node is not null) - throw new InvalidOperationException("The AvnAutomationPeer already has a node."); - Node = node; - } - - public int IsRootProvider() => (_inner is IRootProvider).AsComBool(); + private IEmbeddedRootProvider EmbeddedRootProvider => GetProvider(); + private IExpandCollapseProvider ExpandCollapseProvider => GetProvider(); + private IInvokeProvider InvokeProvider => GetProvider(); + private IRangeValueProvider RangeValueProvider => GetProvider(); + private IRootProvider RootProvider => GetProvider(); + private ISelectionItemProvider SelectionItemProvider => GetProvider(); + private IToggleProvider ToggleProvider => GetProvider(); + private IValueProvider ValueProvider => GetProvider(); - public IAvnWindowBase RootProvider_GetWindow() - { - var window = (WindowBase)((ControlAutomationPeer)_inner).Owner; - return ((WindowBaseImpl)window.PlatformImpl!).Native; - } - - public IAvnAutomationPeer? RootProvider_GetFocus() => Wrap(((IRootProvider)_inner).GetFocus()); + public int IsRootProvider() => IsProvider(); + + public IAvnWindowBase? RootProvider_GetWindow() => (RootProvider.PlatformImpl as WindowBaseImpl)?.Native; + public IAvnAutomationPeer? RootProvider_GetFocus() => Wrap(RootProvider.GetFocus()); public IAvnAutomationPeer? RootProvider_GetPeerFromPoint(AvnPoint point) { - var result = ((IRootProvider)_inner).GetPeerFromPoint(point.ToAvaloniaPoint()); + var result = RootProvider.GetPeerFromPoint(point.ToAvaloniaPoint()); if (result is null) return null; @@ -103,46 +108,80 @@ public IAvnWindowBase RootProvider_GetWindow() return Wrap(result); } - public int IsExpandCollapseProvider() => (_inner is IExpandCollapseProvider).AsComBool(); - public int ExpandCollapseProvider_GetIsExpanded() => ((IExpandCollapseProvider)_inner).ExpandCollapseState switch + public int IsEmbeddedRootProvider() => IsProvider(); + + public IAvnAutomationPeer? EmbeddedRootProvider_GetFocus() => Wrap(EmbeddedRootProvider.GetFocus()); + + public IAvnAutomationPeer? EmbeddedRootProvider_GetPeerFromPoint(AvnPoint point) + { + var result = EmbeddedRootProvider.GetPeerFromPoint(point.ToAvaloniaPoint()); + + if (result is null) + return null; + + // The OSX accessibility APIs expect non-ignored elements when hit-testing. + while (!result.IsControlElement()) + { + var parent = result.GetParent(); + + if (parent is not null) + result = parent; + else + break; + } + + return Wrap(result); + } + + public int IsExpandCollapseProvider() => IsProvider(); + + public int ExpandCollapseProvider_GetIsExpanded() => ExpandCollapseProvider.ExpandCollapseState switch { ExpandCollapseState.Expanded => 1, ExpandCollapseState.PartiallyExpanded => 1, _ => 0, }; - public int ExpandCollapseProvider_GetShowsMenu() => ((IExpandCollapseProvider)_inner).ShowsMenu.AsComBool(); - public void ExpandCollapseProvider_Expand() => ((IExpandCollapseProvider)_inner).Expand(); - public void ExpandCollapseProvider_Collapse() => ((IExpandCollapseProvider)_inner).Collapse(); + public int ExpandCollapseProvider_GetShowsMenu() => ExpandCollapseProvider.ShowsMenu.AsComBool(); + public void ExpandCollapseProvider_Expand() => ExpandCollapseProvider.Expand(); + public void ExpandCollapseProvider_Collapse() => ExpandCollapseProvider.Collapse(); - public int IsInvokeProvider() => (_inner is IInvokeProvider).AsComBool(); - public void InvokeProvider_Invoke() => ((IInvokeProvider)_inner).Invoke(); + public int IsInvokeProvider() => IsProvider(); + public void InvokeProvider_Invoke() => InvokeProvider.Invoke(); - public int IsRangeValueProvider() => (_inner is IRangeValueProvider).AsComBool(); - public double RangeValueProvider_GetValue() => ((IRangeValueProvider)_inner).Value; - public double RangeValueProvider_GetMinimum() => ((IRangeValueProvider)_inner).Minimum; - public double RangeValueProvider_GetMaximum() => ((IRangeValueProvider)_inner).Maximum; - public double RangeValueProvider_GetSmallChange() => ((IRangeValueProvider)_inner).SmallChange; - public double RangeValueProvider_GetLargeChange() => ((IRangeValueProvider)_inner).LargeChange; - public void RangeValueProvider_SetValue(double value) => ((IRangeValueProvider)_inner).SetValue(value); + public int IsRangeValueProvider() => IsProvider(); + public double RangeValueProvider_GetValue() => RangeValueProvider.Value; + public double RangeValueProvider_GetMinimum() => RangeValueProvider.Minimum; + public double RangeValueProvider_GetMaximum() => RangeValueProvider.Maximum; + public double RangeValueProvider_GetSmallChange() => RangeValueProvider.SmallChange; + public double RangeValueProvider_GetLargeChange() => RangeValueProvider.LargeChange; + public void RangeValueProvider_SetValue(double value) => RangeValueProvider.SetValue(value); - public int IsSelectionItemProvider() => (_inner is ISelectionItemProvider).AsComBool(); - public int SelectionItemProvider_IsSelected() => ((ISelectionItemProvider)_inner).IsSelected.AsComBool(); + public int IsSelectionItemProvider() => IsProvider(); + public int SelectionItemProvider_IsSelected() => SelectionItemProvider.IsSelected.AsComBool(); - public int IsToggleProvider() => (_inner is IToggleProvider).AsComBool(); - public int ToggleProvider_GetToggleState() => (int)((IToggleProvider)_inner).ToggleState; - public void ToggleProvider_Toggle() => ((IToggleProvider)_inner).Toggle(); + public int IsToggleProvider() => IsProvider(); + public int ToggleProvider_GetToggleState() => (int)ToggleProvider.ToggleState; + public void ToggleProvider_Toggle() => ToggleProvider.Toggle(); - public int IsValueProvider() => (_inner is IValueProvider).AsComBool(); - public IAvnString ValueProvider_GetValue() => ((IValueProvider)_inner).Value.ToAvnString(); - public void ValueProvider_SetValue(string value) => ((IValueProvider)_inner).SetValue(value); + public int IsValueProvider() => IsProvider(); + public IAvnString ValueProvider_GetValue() => ValueProvider.Value.ToAvnString(); + public void ValueProvider_SetValue(string value) => ValueProvider.SetValue(value); [return: NotNullIfNotNull("peer")] public static AvnAutomationPeer? Wrap(AutomationPeer? peer) { return peer is null ? null : s_wrappers.GetValue(peer, x => new(peer)); } + + private T GetProvider() + { + return _inner.GetProvider() ?? throw new InvalidOperationException( + $"The peer {_inner} does not implement {typeof(T)}."); + } + + private int IsProvider() => (_inner.GetProvider() is not null).AsComBool(); } internal class AvnAutomationPeerArray : NativeCallbackBase, IAvnAutomationPeerArray diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index bc372bbcb53..0911e5ffff6 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -921,6 +921,7 @@ interface IAvnAutomationPeer : IUnknown IAvnAutomationPeer* GetLabeledBy(); IAvnString* GetName(); IAvnAutomationPeer* GetParent(); + IAvnAutomationPeer* GetVisualRoot(); bool HasKeyboardFocus(); bool IsContentElement(); bool IsControlElement(); @@ -935,7 +936,11 @@ interface IAvnAutomationPeer : IUnknown IAvnWindowBase* RootProvider_GetWindow(); IAvnAutomationPeer* RootProvider_GetFocus(); IAvnAutomationPeer* RootProvider_GetPeerFromPoint(AvnPoint point); - + + bool IsEmbeddedRootProvider(); + IAvnAutomationPeer* EmbeddedRootProvider_GetFocus(); + IAvnAutomationPeer* EmbeddedRootProvider_GetPeerFromPoint(AvnPoint point); + bool IsExpandCollapseProvider(); bool ExpandCollapseProvider_GetIsExpanded(); bool ExpandCollapseProvider_GetShowsMenu(); diff --git a/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs index 3eeedc4b5da..569f7da738e 100644 --- a/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/AutomationNode.cs @@ -54,25 +54,11 @@ public AutomationNode(AutomationPeer peer) _runtimeId = new int[] { 3, GetHashCode() }; Peer = peer; s_nodes.Add(peer, this); - peer.ChildrenChanged += Peer_ChildrenChanged; - peer.PropertyChanged += Peer_PropertyChanged; - } - - private void Peer_ChildrenChanged(object? sender, EventArgs e) - { - ChildrenChanged(); - } + peer.ChildrenChanged += OnPeerChildrenChanged; + peer.PropertyChanged += OnPeerPropertyChanged; - private void Peer_PropertyChanged(object? sender, AutomationPropertyChangedEventArgs e) - { - if (s_propertyMap.TryGetValue(e.Property, out var id)) - { - UiaCoreProviderApi.UiaRaiseAutomationPropertyChangedEvent( - this, - (int)id, - e.OldValue as IConvertible, - e.NewValue as IConvertible); - } + if (Peer.GetProvider() is { } embeddedRoot) + embeddedRoot.FocusChanged += OnEmbeddedRootFocusChanged; } public AutomationPeer Peer { get; protected set; } @@ -89,21 +75,12 @@ public Rect BoundingRectangle public virtual IRawElementProviderFragmentRoot? FragmentRoot { - get => InvokeSync(() => GetRoot()) as IRawElementProviderFragmentRoot; + get => InvokeSync(() => GetRoot()); } public virtual IRawElementProviderSimple? HostRawElementProvider => null; public ProviderOptions ProviderOptions => ProviderOptions.ServerSideProvider; - public void ChildrenChanged() - { - UiaCoreProviderApi.UiaRaiseStructureChangedEvent( - this, - StructureChangeType.ChildrenInvalidated, - null, - 0); - } - [return: MarshalAs(UnmanagedType.IUnknown)] public virtual object? GetPatternProvider(int patternId) { @@ -250,21 +227,49 @@ protected TResult InvokeSync(Func func throw new NotSupportedException(); } + protected void RaiseChildrenChanged() + { + UiaCoreProviderApi.UiaRaiseStructureChangedEvent( + this, + StructureChangeType.ChildrenInvalidated, + null, + 0); + } - private AutomationNode? GetRoot() + protected void RaiseFocusChanged(AutomationNode? focused) + { + UiaCoreProviderApi.UiaRaiseAutomationEvent( + focused, + (int)UiaEventId.AutomationFocusChanged); + } + + private RootAutomationNode? GetRoot() { Dispatcher.UIThread.VerifyAccess(); + return GetOrCreate(Peer.GetVisualRoot()) as RootAutomationNode; + } - var peer = Peer; - var parent = peer.GetParent(); + private void OnPeerChildrenChanged(object? sender, EventArgs e) + { + RaiseChildrenChanged(); + } - while (peer.GetProvider() is null && parent is object) + private void OnPeerPropertyChanged(object? sender, AutomationPropertyChangedEventArgs e) + { + if (s_propertyMap.TryGetValue(e.Property, out var id)) { - peer = parent; - parent = peer.GetParent(); + UiaCoreProviderApi.UiaRaiseAutomationPropertyChangedEvent( + this, + (int)id, + e.OldValue as IConvertible, + e.NewValue as IConvertible); } + } - return peer is object ? GetOrCreate(peer) : null; + private void OnEmbeddedRootFocusChanged(object? sender, EventArgs e) + { + if (Peer.GetProvider() is { } embeddedRoot) + RaiseFocusChanged(GetOrCreate(embeddedRoot.GetFocus())); } private static AutomationNode Create(AutomationPeer peer) diff --git a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs index 739f0ac2512..7334186c804 100644 --- a/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs +++ b/src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs @@ -9,18 +9,14 @@ namespace Avalonia.Win32.Automation { [RequiresUnreferencedCode("Requires .NET COM interop")] - internal class RootAutomationNode : AutomationNode, - IRawElementProviderFragmentRoot, - IRawElementProviderAdviseEvents + internal class RootAutomationNode : AutomationNode, IRawElementProviderFragmentRoot { - private int _raiseFocusChanged; - public RootAutomationNode(AutomationPeer peer) : base(peer) { Peer = base.Peer.GetProvider() ?? throw new AvaloniaInternalException( "Attempt to create RootAutomationNode from peer which does not implement IRootProvider."); - Peer.FocusChanged += FocusChanged; + Peer.FocusChanged += OnRootFocusChanged; } public override IRawElementProviderFragmentRoot? FragmentRoot => this; @@ -44,41 +40,6 @@ public RootAutomationNode(AutomationPeer peer) return GetOrCreate(focus); } - void IRawElementProviderAdviseEvents.AdviseEventAdded(int eventId, int[] properties) - { - switch ((UiaEventId)eventId) - { - case UiaEventId.AutomationFocusChanged: - ++_raiseFocusChanged; - break; - } - } - - void IRawElementProviderAdviseEvents.AdviseEventRemoved(int eventId, int[] properties) - { - switch ((UiaEventId)eventId) - { - case UiaEventId.AutomationFocusChanged: - --_raiseFocusChanged; - break; - } - } - - protected void RaiseFocusChanged(AutomationNode? focused) - { - if (_raiseFocusChanged > 0) - { - UiaCoreProviderApi.UiaRaiseAutomationEvent( - focused, - (int)UiaEventId.AutomationFocusChanged); - } - } - - public void FocusChanged(object? sender, EventArgs e) - { - RaiseFocusChanged(GetOrCreate(Peer.GetFocus())); - } - public Rect ToScreen(Rect rect) { if (WindowImpl is null) @@ -101,5 +62,10 @@ public override IRawElementProviderSimple? HostRawElementProvider return result; } } + + private void OnRootFocusChanged(object? sender, EventArgs e) + { + RaiseFocusChanged(GetOrCreate(Peer.GetFocus())); + } } }