diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs index 3b68366f21c..45d0850e8bf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs @@ -1,316 +1,75 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -using System.Windows.Threading; -using System.Windows; -using System.Windows.Interop; -using System.Windows.Media; -using System.Windows.Automation; using System.Windows.Automation.Peers; -using MS.Win32; -using MS.Internal.Media; -using System.Runtime.InteropServices; +using System.Windows.Automation; +using System.Windows.Threading; -using MS.Internal.PresentationCore; +namespace MS.Internal.Automation; -namespace MS.Internal.Automation +/// <summary> +/// Utility class for working with <see cref="AutomationPeer"/>. +/// </summary> +internal static class ElementUtil { - // static class providing utility information for working with WCP elements - internal class ElementUtil + /// <summary> + /// Provides a helper to invoke work on the UI thread, re-throwing all exceptions on the thread that invoked this execution. + /// </summary> + internal static object Invoke(AutomationPeer peer, DispatcherOperationCallback work, object arg) { - //------------------------------------------------------ - // - // Constructors - // - //------------------------------------------------------ - - #region Constructors - - // static class, so use private ctor - private ElementUtil() { } - - #endregion Constructors - - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - - internal static Visual GetParent( Visual el ) - { - return VisualTreeHelper.GetParent(el) as Visual; - } - - internal static Visual GetFirstChild( Visual el ) - { - if (el == null) - { - return null; - } - - return FindVisibleSibling ( el, 0, true ); - } - - internal static Visual GetLastChild( Visual el ) - { - if (el == null) - { - return null; - } - - return FindVisibleSibling ( el, el.InternalVisualChildrenCount - 1, false ); - } - - // Warning: Method is O(N). See FindVisibleSibling function for more information. - internal static Visual GetNextSibling( Visual el ) - { - // To get next/previous sibling, have to find out where we - // are in our parent's children collection (ie. our siblings) - Visual parent = VisualTreeHelper.GetParent(el) as Visual; - // If parent is null, we're at root, so have no siblings - if (parent == null) - { - return null; - } - return FindVisibleSibling ( parent, el, searchForwards: true); - } - - // Warning: Method is O(N). See FindVisibleSibling function for more information. - internal static Visual GetPreviousSibling( Visual el ) - { - // To get next/previous sibling, have to find out where we - // are in our parent's children collection (ie. our siblings) - Visual parent = VisualTreeHelper.GetParent(el) as Visual; - // If parent is null, we're at root, so have no siblings - if (parent == null) - { - return null; - } - - return FindVisibleSibling ( parent, el, searchForwards: false); - } - - internal static Visual GetRoot( Visual el ) - { - // Keep moving up parent chain till we reach the top... - Visual scan = el; - for( ; ; ) - { - Visual test = VisualTreeHelper.GetParent(scan) as Visual; - if( test == null ) - break; - scan = test; - } - return scan; - } - - // Get bounding rectangle, in coords relative to root (not screen) - internal static Rect GetLocalRect( UIElement element ) - { - // Get top-most visual. - Visual parent = GetRoot( element ); - - // Get the points for the rectangle and transform them. - double height = element.RenderSize.Height; - double width = element.RenderSize.Width; - Rect rect = new Rect(0, 0, width, height); - - GeneralTransform g = element.TransformToAncestor(parent); - return g.TransformBounds(rect); - } - - // Get bounding rectangle, relative to screen - internal static Rect GetScreenRect( IntPtr hwnd, UIElement el ) - { - Rect rc = GetLocalRect( el ); - - // Map from local to screen coords... - NativeMethods.RECT rcWin32 = new NativeMethods.RECT( (int) rc.Left, (int) rc.Top, (int) rc.Right, (int) rc.Bottom ); - try - { - SafeSecurityHelper.TransformLocalRectToScreen(new HandleRef(null, hwnd), ref rcWin32); - } - catch (System.ComponentModel.Win32Exception) - { - return Rect.Empty; - } - - rc = new Rect( rcWin32.left, rcWin32.top, rcWin32.right - rcWin32.left, rcWin32.bottom - rcWin32.top ); - - return rc; - } + // Null dispatcher likely means the visual is in bad shape + Dispatcher dispatcher = peer.Dispatcher ?? throw new ElementNotAvailableException(); - // Get element at given point (screen coords) - internal static Visual GetElementFromPoint( IntPtr hwnd, Visual root, Point pointScreen ) - { - HwndSource hwndSource = HwndSource.CriticalFromHwnd(hwnd); - - if(hwndSource == null) - return null; - - Point pointClient = PointUtil.ScreenToClient( pointScreen, hwndSource ); - Point pointRoot = PointUtil.ClientToRoot(pointClient, hwndSource); - PointHitTestResult result = VisualTreeUtils.AsNearestPointHitTestResult(VisualTreeHelper.HitTest(root, pointRoot)); - Visual visual = result?.VisualHit; - - - return visual; - } + Exception remoteException = null; + bool completed = false; - // Ensures that an element is enabled; throws exception otherwise - internal static void CheckEnabled(Visual visual) - { - UIElement el = visual as UIElement; - - if( el != null && ! el.IsEnabled ) + object retVal = dispatcher.Invoke( + DispatcherPriority.Send, + TimeSpan.FromMinutes(3), + (DispatcherOperationCallback)delegate (object unused) { - throw new ElementNotEnabledException(); - } - } - - internal static object Invoke(AutomationPeer peer, DispatcherOperationCallback work, object arg) - { - Dispatcher dispatcher = peer.Dispatcher; - - // Null dispatcher likely means the visual is in bad shape! - if( dispatcher == null ) - { - throw new ElementNotAvailableException(); - } - - Exception remoteException = null; - bool completed = false; - - object retVal = dispatcher.Invoke( - DispatcherPriority.Send, - TimeSpan.FromMinutes(3), - (DispatcherOperationCallback) delegate(object unused) + try { - try - { - return work(arg); - } - catch(Exception e) - { - remoteException = e; - return null; - } - catch //for non-CLS Compliant exceptions - { - remoteException = null; - return null; - } - finally - { - completed = true; - } -}, - null); - - if(completed) - { - if(remoteException != null) + return work(arg); + } + catch (Exception e) { - throw remoteException; + remoteException = e; + return null; } - } - else - { - bool dispatcherInShutdown = dispatcher.HasShutdownStarted; - - if(dispatcherInShutdown) + catch //for non-CLS Compliant exceptions { - throw new InvalidOperationException(SR.AutomationDispatcherShutdown); + remoteException = null; + return null; } - else + finally { - throw new TimeoutException(SR.AutomationTimeout); + completed = true; } - } - - return retVal; -} - - #endregion Internal Methods - - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ + }, + null); - // Potential enhancement: Consider Visual3Ds in this walk? - // Does this walk need to continue through the Visual3D tree once - // we have UIElement3D? - private static Visual FindVisibleSibling ( Visual parent, int start, bool searchForwards) + if (completed) { - int index = start; - int childrenCount = parent.InternalVisualChildrenCount; - - while ( index >= 0 && index < childrenCount ) + if (remoteException is not null) { - Visual sibling = parent.InternalGetVisualChild(index); - - // if its visible or something other than a UIElement keep it - if ( !(sibling is UIElement) || (((UIElement)sibling).Visibility == Visibility.Visible ) ) - return sibling; - - index += searchForwards ? 1 : -1; + throw remoteException; } - - return null; } - - // Potential enhancement - Consider Visual3Ds in this walk? - // Does this walk need to continue through the Visual3D tree once - // we have UIElement3D? - // - // WARNING: This method is O(N) and can therefore lead to O(N^2) algorithms. - private static Visual FindVisibleSibling(Visual parent, Visual child, bool searchForwards) + else { - // - // First we figure out the index of the specified child Visual. This is why the runtime - // of this method is O(n). - - int childrenCount = parent.InternalVisualChildrenCount; - int childIndex; + bool dispatcherInShutdown = dispatcher.HasShutdownStarted; - for (childIndex = 0; childIndex < childrenCount; childIndex++) + if (dispatcherInShutdown) { - Visual current = parent.InternalGetVisualChild(childIndex); - if (current == child) - { - // Found the child. - break; - } + throw new InvalidOperationException(SR.AutomationDispatcherShutdown); } - - // - // Now that we have the child index, we can go and lookup the sibling. - if(searchForwards) - return FindVisibleSibling(parent, childIndex+1, searchForwards); // (FindVisibleSibling can deal with out of range indices). else - return FindVisibleSibling(parent, childIndex-1, searchForwards); // (FindVisibleSibling can deal with out of range indices). + { + throw new TimeoutException(SR.AutomationTimeout); + } } - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - // static class, so no private fields - - #endregion Private Fields + return retVal; } } - - -