File: MS\Internal\AutomationProxies\ProxyFragment.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\UIAutomation\UIAutomationClientSideProviders\UIAutomationClientSideProviders.csproj (UIAutomationClientSideProviders)
// 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: Base class for all the Win32 and office Controls.
//
//              The ProxyFragment class is the base class for NODES in the
//              element tree that are not hwnd based in the case of Win32 controls (e.g. ListViewItem)
//
//              The UIAutomation internal provider does not allow for a node to
//              iterate through its children.
//              The ProxyFragment class removes this limitation.
//
//              Class ProxyFragment:
//                  ProxySimple - methods
//                  ElementProviderFromPoint
//                  GetFocus
//
//
//              NOTE: ProxyFragment is responsible for hit-testing on its children, since UIAutomation
//                    will not call ElementProviderFromPoint on the Fragment!
//             
//              Here are the easy steps for drilling down:
//                     UIAutomation will call ProxyHwnd.ElementProviderFromPoint automatically,
//                     you (implementer) should do a hit test on ProxyHwnd's immediate children only.
//                     If hit-test succeeds and element that you got is ProxyFragment
//                     call ProxyFragment.DrillDownIntoFragment(fragment, x, y)
//                     Each ProxyFragment should implement ElementProviderFromPoint; this method will be used by
//                     DrillDownIntoFragment to drill into the hierarchy of any depth.
//                     Note: You can implement all the drilling down "locally" in your ProxyHwnd.ElementProviderFromPoint,
//                           but it is strongly suggested to do it as described above, so we'll have a generic solution that always works
//                    
//
//
 
using System;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
 
namespace MS.Internal.AutomationProxies
{
    // Base Class for all the Windows Control that supports navigation.
    // Implements the default behaviors
    class ProxyFragment : ProxySimple, IRawElementProviderFragmentRoot
    {
        // ------------------------------------------------------
        //
        // Constructors
        //
        // ------------------------------------------------------
 
        #region Constructors
 
        internal ProxyFragment (IntPtr hwnd, ProxyFragment parent, int item) : base (hwnd, parent, item)
        {}
 
        #endregion
 
        // ------------------------------------------------------
        //
        // Patterns Implementation
        //
        // ------------------------------------------------------
 
        #region ProxyFragment Methods
 
        // ------------------------------------------------------
        //
        // Default implementation for ProxyFragment members
        //
        // ------------------------------------------------------
                
        // Next Silbing: assumes none, must be overloaded by a subclass if any
        // The method is called on the parent with a reference to the child.
        // This makes the implementation a lot more clean than the UIAutomation call
        internal virtual ProxySimple GetNextSibling (ProxySimple child)
        {
            return null;
        }
 
        // Prev Silbing: assumes none, must be overloaded by a subclass if any
        // The method is called on the parent with a reference to the child.
        // This makes the implementation a lot more clean than the UIAutomation call
        internal virtual ProxySimple GetPreviousSibling (ProxySimple child)
        {
            return null;
        }
 
        // GetFirstChild: assumes none, must be overloaded by a subclass if any
        internal virtual ProxySimple GetFirstChild ()
        {
            return null;
        }
 
        // GetLastChild: assumes none, must be overloaded by a subclass if any
        internal virtual ProxySimple GetLastChild ()
        {
            return null;
        }
 
        // Returns a Proxy element corresponding to the specified screen coordinates.
        // In the derived class implement this method to do fragment specific drill down (if needed)
        // Fragment should drill down only among its immediate children.  
        internal virtual ProxySimple ElementProviderFromPoint (int x, int y)
        {
            return this;
        }
 
        // Returns an item corresponding to the focused element (if there is one), or null otherwise.
        internal virtual ProxySimple GetFocus ()
        {
            return this is ProxyHwnd ? this : null;
        }
 
        #endregion
        
 
        #region IRawElementProviderFragmentRoot Interface
        // NOTE: This method should only be called (by Proxy or/and UIAutomation) on Proxies of type
        //       ProxyHwnd. Anything else indicates a problem
        IRawElementProviderFragment IRawElementProviderFragmentRoot.ElementProviderFromPoint (double x, double y)
        {
            System.Diagnostics.Debug.Assert(this is ProxyHwnd,  "Invalid method called ElementProviderFromPoint");
 
            // Do the proper rounding.
            return ElementProviderFromPoint ((int) (x + 0.5), (int) (y + 0.5));
        }
 
        IRawElementProviderFragment IRawElementProviderFragmentRoot.GetFocus ()
        {
            return GetFocus ();
        }
 
        #endregion
 
        #region IRawElementProviderFragment Interface
 
        // Request to return the element in the specified direction
        IRawElementProviderFragment IRawElementProviderFragment.Navigate(NavigateDirection direction)
        {
            switch (direction)
            {
                case NavigateDirection.NextSibling :
                    {
                        // NOTE: Do not use GetParent(), call _parent explicitly
                        return _fSubTree ? _parent.GetNextSibling (this) : null;
                    }
 
                case NavigateDirection.PreviousSibling :
                    {
                        // NOTE: Do not use GetParent(), call _parent explicitly
                        return _fSubTree ? _parent.GetPreviousSibling (this) : null;
                    }
 
                case NavigateDirection.FirstChild :
                    {
                        return GetFirstChild ();
                    }
 
                case NavigateDirection.LastChild :
                    {
                        return GetLastChild ();
                    }
 
                case NavigateDirection.Parent :
                    {
                        return GetParent ();
                    }
 
                default :
                    {
                        return null;
                    }
            }
        }
 
        #endregion
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        // Recursively Raise an Event for all the sub elements
        override internal void RecursiveRaiseEvents (object idProp, AutomationPropertyChangedEventArgs e)
        {
            AutomationInteropProvider.RaiseAutomationPropertyChangedEvent (this, e);
            for (ProxySimple el = GetFirstChild (); el != null; el = this.GetNextSibling (el))
            {
                el.RecursiveRaiseEvents (idProp, e);
            }
        }
    
        #endregion
        
        //------------------------------------------------------
        //
        //  Protected Methods
        //
        //------------------------------------------------------
 
        #region Protected Methods
 
        // This method will return the leaf element that lives in ProxyFragment at Point(x,y)
        static internal ProxySimple DrillDownFragment(ProxyFragment fragment, int x, int y)
        {
            System.Diagnostics.Debug.Assert(fragment != null, "DrillDownFragment: starting point is null");
 
            // drill down
            ProxySimple fromPoint = fragment.ElementProviderFromPoint(x, y);
 
            System.Diagnostics.Debug.Assert(fromPoint != null, @"DrillDownFragment: calling ElementProviderFromPoint on Fragment should not return null");
 
            // Check if we got back a new fragment
            // do this check before trying to cast to ProxyFragment
            if (fragment == fromPoint || Misc.Compare(fragment, fromPoint))
            {
                // Point was on the fragment
                // but not on any element that lives inside of the fragment
                return fragment;
            }
 
            fragment = fromPoint as ProxyFragment;
            if (fragment == null)
            {
                // we got back a simple element                
                return fromPoint;
            }
 
            // Got a new fragment, continue drilling
            return DrillDownFragment(fragment, x, y);
        }
 
        #endregion
                
    }
}