File: MS\Internal\Automation\ElementUtil.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// 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.
 
//
// 
//
// Description: 
//
//
 
using System;
using System.Windows.Threading;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows.Automation.Peers;
using System.Security; 
using MS.Win32;
using MS.Internal.Media;
using System.Runtime.InteropServices;
using System.Globalization;
 
using MS.Internal.PresentationCore;                        // SafeSecurityHelper
 
using SR=MS.Internal.PresentationCore.SR;
 
namespace MS.Internal.Automation
{
    // static class providing utility information for working with WCP elements
    internal class ElementUtil
    {
        //------------------------------------------------------
        //
        //  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, true /* Next */);
        }
 
        // 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, false /* Previous */);
        }
 
        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;
        }
 
        // 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 != null) ? result.VisualHit : null;
 
 
            return visual;
        }
 
        // 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 )
            {
                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
                    {
                        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)
                {
                    throw remoteException;
                }
            }
            else
            {
                bool dispatcherInShutdown = dispatcher.HasShutdownStarted;
 
                if(dispatcherInShutdown)
                {
                    throw new InvalidOperationException(SR.AutomationDispatcherShutdown);
                }
                else
                {
                    throw new TimeoutException(SR.AutomationTimeout);
                }
            }
            
            return retVal;
}
 
        #endregion Internal Methods
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        // 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)
        {
            int index = start;
            int childrenCount = parent.InternalVisualChildrenCount;
            
            while ( index >= 0 && index < childrenCount )
            {
                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;
            }
 
            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)
        {
            //
            // 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;
 
            for (childIndex = 0; childIndex < childrenCount; childIndex++)
            {
                Visual current = parent.InternalGetVisualChild(childIndex);
                if (current == child)
                {
                    // Found the child.
                    break;
                }
            }
 
            //
            // 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).
        }
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Fields
 
        // static class, so no private fields
 
        #endregion Private Fields
    }
}