diff --git a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
index b28d445443..9fb8f4026d 100644
--- a/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
@@ -420,6 +420,17 @@ namespace Avalonia.Automation.Peers
[PrivateApi]
public AutomationPeer? GetAutomationRoot() => GetAutomationRootCore();
+ ///
+ /// Converts a rectangle in the peer's coordinate space, as returned by
+ /// , to screen coordinates; returns null if the peer is
+ /// not currently hosted in a rendered visual tree.
+ ///
+ ///
+ /// A rectangle in the peer's own coordinate space, as returned by
+ /// .
+ ///
+ public Rect? ToScreen(Rect rect) => ToScreenCore(rect);
+
///
/// Gets a value that indicates whether the element that is associated with this automation
/// peer currently has keyboard focus.
@@ -669,6 +680,8 @@ namespace Avalonia.Automation.Peers
private protected virtual AutomationPeer? GetVisualRootCore() => GetAutomationRootCore();
+ private protected virtual Rect? ToScreenCore(Rect rect) => null;
+
protected virtual bool IsContentElementOverrideCore()
{
diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
index c4128465b7..24f143f408 100644
--- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
@@ -155,6 +155,13 @@ namespace Avalonia.Automation.Peers
return null;
}
+ private protected override Rect? ToScreenCore(Rect rect)
+ {
+ if (Owner?.PresentationSource?.RootVisual is not { } root)
+ return null;
+ return new PixelRect(root.PointToScreen(rect.TopLeft), root.PointToScreen(rect.BottomRight)).ToRect(1);
+ }
+
///
/// Invalidates the peer's children and causes a re-read from .
///
diff --git a/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs b/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs
index 1c0236e8c0..d5b0a84179 100644
--- a/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs
+++ b/src/Windows/Avalonia.Win32.Automation/AutomationNode.cs
@@ -11,7 +11,6 @@ using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Automation.Peers;
using Avalonia.Threading;
-using Avalonia.VisualTree;
using Avalonia.Win32.Automation.Interop;
using AAP = Avalonia.Automation.Provider;
using UIA = Avalonia.Win32.Automation.Interop;
@@ -76,18 +75,7 @@ namespace Avalonia.Win32.Automation
public virtual Rect GetBoundingRectangle()
{
- return InvokeSync(() =>
- {
- if (Peer.GetVisualRoot() is ControlAutomationPeer root &&
- root.Owner.GetPresentationSource() is not null)
- {
- var originalRect = Peer.GetBoundingRectangle();
- return new PixelRect(root.Owner.PointToScreen(originalRect.TopLeft),
- root.Owner.PointToScreen(originalRect.BottomRight)).ToRect(1);
- }
-
- return default;
- });
+ return InvokeSync(() => Peer.ToScreen(Peer.GetBoundingRectangle()) ?? default);
}
public virtual IRawElementProviderFragmentRoot? GetFragmentRoot()
diff --git a/tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs b/tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs
index 7ba9ff4fdb..41aa697237 100644
--- a/tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs
@@ -232,6 +232,31 @@ namespace Avalonia.Controls.UnitTests.Automation
}
}
+ public class ToScreen : ScopedTestBase
+ {
+ [Fact]
+ public void Converts_Rect_When_Attached_To_Rooted_Tree()
+ {
+ var border = new Border();
+ var root = new TestRoot(border);
+ var peer = CreatePeer(border);
+
+ Assert.Equal(new Rect(10, 20, 30, 40), peer.ToScreen(new Rect(10, 20, 30, 40)));
+ }
+
+ [Fact]
+ public void Returns_Null_When_Detached()
+ {
+ var border = new Border();
+ var root = new TestRoot(border);
+ var peer = CreatePeer(border);
+
+ root.Child = null;
+
+ Assert.Null(peer.ToScreen(new Rect(10, 20, 30, 40)));
+ }
+ }
+
private static AutomationPeer CreatePeer(Control control)
{
return ControlAutomationPeer.CreatePeerForElement(control);