File: System\Windows\Automation\Peers\AutomationPeer.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.
 
//#define ENABLE_AUTOMATIONPEER_LOGGING   // uncomment to include logging of various activities
 
using System.Collections;
using System.Windows.Threading;
using System.Windows.Automation.Provider;
using MS.Internal;
using MS.Internal.Automation;
 
namespace System.Windows.Automation.Peers
{
    ///
    public enum PatternInterface
    {
        ///
        Invoke,
        ///
        Selection,
        ///
        Value,
        ///
        RangeValue,
        ///
        Scroll,
        ///
        ScrollItem,
        ///
        ExpandCollapse,
        ///
        Grid,
        ///
        GridItem,
        ///
        MultipleView,
        ///
        Window,
        ///
        SelectionItem,
        ///
        Dock,
        ///
        Table,
        ///
        TableItem,
        ///
        Toggle,
        ///
        Transform,
        ///
        Text,
        ///
        ItemContainer,
        ///
        VirtualizedItem,
        ///
        SynchronizedInput,
    }
 
    ///
    public enum AutomationOrientation
    {
        ///
        None = 0,
        ///
        Horizontal,
        ///
        Vertical,
    }
 
    ///
    public enum AutomationControlType
    {
        ///
        Button,
        ///
        Calendar,
        ///
        CheckBox,
        ///
        ComboBox,
        ///
        Edit,
        ///
        Hyperlink,
        ///
        Image,
        ///
        ListItem,
        ///
        List,
        ///
        Menu,
        ///
        MenuBar,
        ///
        MenuItem,
        ///
        ProgressBar,
        ///
        RadioButton,
        ///
        ScrollBar,
        ///
        Slider,
        ///
        Spinner,
        ///
        StatusBar,
        ///
        Tab,
        ///
        TabItem,
        ///
        Text,
        ///
        ToolBar,
        ///
        ToolTip,
        ///
        Tree,
        ///
        TreeItem,
        ///
        Custom,
        ///
        Group,
        ///
        Thumb,
        ///
        DataGrid,
        ///
        DataItem,
        ///
        Document,
        ///
        SplitButton,
        ///
        Window,
        ///
        Pane,
        ///
        Header,
        ///
        HeaderItem,
        ///
        Table,
        ///
        TitleBar,
        ///
        Separator,
    }
 
    ///
    public enum AutomationEvents
    {
        ///
        ToolTipOpened,
        ///
        ToolTipClosed,
        ///
        MenuOpened,
        ///
        MenuClosed,
        ///
        AutomationFocusChanged,
        ///
        InvokePatternOnInvoked,
        ///
        SelectionItemPatternOnElementAddedToSelection,
        ///
        SelectionItemPatternOnElementRemovedFromSelection,
        ///
        SelectionItemPatternOnElementSelected,
        ///
        SelectionPatternOnInvalidated,
        ///
        TextPatternOnTextSelectionChanged,
        ///
        TextPatternOnTextChanged,
        ///
        AsyncContentLoaded,
        ///
        PropertyChanged,
        ///
        StructureChanged,
        ///
        InputReachedTarget,
        ///
        InputReachedOtherElement,
        ///
        InputDiscarded,
        ///
        LiveRegionChanged,
        ///
        Notification,
        ///
        ActiveTextPositionChanged,
    }
 
 
    ///<summary> This is a helper class to facilate the storage of Security critical data ( aka "Plutonium")
    /// What is "critical data" ? This is any data created that required an Assert for it's creation.
    ///</summary> As an example - the passage of hosted Hwnd between some AutomationPeer and UIA infrastructure.
    public sealed class HostedWindowWrapper
    {
        /// <summary>
        /// This is the only public constructor on this class.
        /// </summary>
        public HostedWindowWrapper(IntPtr hwnd)
        {
            _hwnd = hwnd;
        }
 
        private HostedWindowWrapper()
        {
            _hwnd = IntPtr.Zero;
        }
 
        internal static HostedWindowWrapper CreateInternal(IntPtr hwnd)
        {
            HostedWindowWrapper wrapper = new HostedWindowWrapper();
            wrapper._hwnd = hwnd;
            return wrapper;
        }
 
        internal IntPtr Handle
        {
            get
            {
                return _hwnd;
            }
        }
 
        private IntPtr _hwnd;
    }
 
 
    ///
    public abstract class AutomationPeer: DispatcherObject
    {
        ///
        static AutomationPeer()
        {
            // Disable message processing to avoid re-entrancy (WM_GETOBJECT)
            using (Dispatcher.CurrentDispatcher.DisableProcessing())
            {
                Initialize();
            }
        }
 
#if ENABLE_AUTOMATIONPEER_LOGGING
        protected AutomationPeer()
        {
            LogPeer(this);
        }
#endif
 
        //
        // VIRTUAL CALLBACKS
        //
 
        ///
        abstract protected List<AutomationPeer> GetChildrenCore();
 
        ///
        abstract public object GetPattern(PatternInterface patternInterface);
 
 
        //
        // PUBLIC METHODS
        //
 
        ///
        public void InvalidatePeer()
        {
            if(_invalidated) return;
 
            Dispatcher.BeginInvoke(DispatcherPriority.Background, _updatePeer, this);
            _invalidated = true;
        }
 
        ///<summary>
        /// Used to check if Automation is indeed listening for the event.
        /// Typical usage is to check this before even creating the peer that will fire the event.
        /// Basically, this is a performance measure since if the Automation does not listen for the event,
        /// it does not make sense to create a peer to fire one.
        /// NOTE: the method is static and only answers if there is some listener in Automation,
        /// not specifically for some element. The Automation can hook up "broadcast listeners" so the
        /// per-element info is basically unavailable.
        ///</summary>
        static public bool ListenerExists(AutomationEvents eventId)
        {
            return (EventMap.HasRegisteredEvent(eventId));
        }
 
        ///<summary>
        /// Used by peer implementation to raise an event for Automation
        ///</summary>
        // Never inline, as we don't want to unnecessarily link the automation DLL.
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        public void RaiseAutomationEvent(AutomationEvents eventId)
        {
            AutomationEvent eventObject = EventMap.GetRegisteredEvent(eventId);
 
            if (eventObject == null)
            {
                // nobody is listening to this event
                return;
            }
 
            IRawElementProviderSimple provider = ProviderFromPeer(this);
            if (provider != null)
            {
                AutomationInteropProvider.RaiseAutomationEvent(
                    eventObject,
                    provider,
                    new AutomationEventArgs(eventObject));
            }
        }
 
        /// <summary>
        /// This method is called by implementation of the peer to raise the automation propertychange notifications
        /// Typically, the peers that implement automation patterns liek IScrollProvider need to raise events specified by
        /// the particular pattern in case specific properties are changing.
        /// </summary>
        // Never inline, as we don't want to unnecessarily link the automation DLL via the ScrollPattern reference.
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        public void RaisePropertyChangedEvent(AutomationProperty property, object oldValue, object newValue)
        {
            // Only send the event if there are listeners for this property change
            if (AutomationInteropProvider.ClientsAreListening)
            {
                RaisePropertyChangedInternal(ProviderFromPeer(this), property,oldValue,newValue);
            }
        }
 
        /// <summary>
        /// This method is called by implementation of the peer to raise the automation "async content loaded" notifications
        /// </summary>
        // Never inline, as we don't want to unnecessarily link the automation DLL.
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        public void RaiseAsyncContentLoadedEvent(AsyncContentLoadedEventArgs args)
        {
            ArgumentNullException.ThrowIfNull(args);
 
            if (EventMap.HasRegisteredEvent(AutomationEvents.AsyncContentLoaded))
            {
                IRawElementProviderSimple provider = ProviderFromPeer(this);
                if(provider != null)
                {
                    AutomationInteropProvider.RaiseAutomationEvent(
                        AutomationElementIdentifiers.AsyncContentLoadedEvent,
                        provider,
                        args);
                }
            }
        }
 
        /// <summary>
        /// This method is called by implementation of the peer to raise the automation "notification" event
        /// </summary>
        // Never inline, as we don't want to unnecessarily link the automation DLL.
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        public void RaiseNotificationEvent(AutomationNotificationKind notificationKind,
                                              AutomationNotificationProcessing notificationProcessing,
                                              string displayString,
                                              string activityId)
        {
            if (EventMap.HasRegisteredEvent(AutomationEvents.Notification))
            {
                IRawElementProviderSimple provider = ProviderFromPeer(this);
                if (provider != null)
                {
                    AutomationInteropProvider.RaiseAutomationEvent(
                        AutomationElementIdentifiers.NotificationEvent,
                        provider,
                        new NotificationEventArgs(notificationKind, notificationProcessing, displayString, activityId));
                }
            }
        }
 
        internal static void RaiseFocusChangedEventHelper(IInputElement newFocus)
        {
            // Callers have only checked if automation clients are present so filter for any interest in this particular event.
            if (EventMap.HasRegisteredEvent(AutomationEvents.AutomationFocusChanged))
            {
                AutomationPeer peer = AutomationPeerFromInputElement(newFocus);
 
                if (peer != null)
                {
                    peer.RaiseAutomationEvent(AutomationEvents.AutomationFocusChanged);
                }
                else //non-automated element got focus, same as focus lost
                {
                    //No focused peer. Just don't report anything.
                }
            }
        }
 
        //  helper method. Makes attempt to find an automation peer corresponding to the given IInputElement...
        internal static AutomationPeer AutomationPeerFromInputElement(IInputElement focusedElement)
        {
            AutomationPeer peer = null;
 
            UIElement uie = focusedElement as UIElement;
            if (uie != null)
            {
                peer = UIElementAutomationPeer.CreatePeerForElement(uie);
            }
            else
            {
                ContentElement ce = focusedElement as ContentElement;
                if (ce != null)
                {
                    peer = ContentElementAutomationPeer.CreatePeerForElement(ce);
                }
                else
                {
                    UIElement3D uie3D = focusedElement as UIElement3D;
                    if (uie3D != null)
                    {
                        peer = UIElement3DAutomationPeer.CreatePeerForElement(uie3D);
                    }
                }
            }
 
            if (peer != null)
            {
                //  ValidateConnected ensures that EventsSource is initialized
                peer.ValidateConnected(peer);
 
                //  always use event source when available
                if (peer.EventsSource != null)
                {
                    peer = peer.EventsSource;
                }
            }
 
            return peer;
        }
 
        // We can only return peers to UIA that are properly connected to the UIA tree already
        // This means they should have _hwnd and _parent already set and _parent should point to the
        // peer which would have this peer returned from its GetChildrenCore. This method checks if the
        // peer is already connected, and if not then it walks the tree of peers from the top down, calling
        // GetChildren and trying to find itself in someone's children. Once this succeeds, the peer is connected
        // (because GetChildren will connect it). In this case this method will return "this".
        // However if the search does not find the peer, that means the peer
        // would never be exposed by specific context even though it is createable on the element (the decision to expose
        // children is on parent peers and parent peer may decide not to expose subpart of itself). In this case,
        // this method returns null.
        // ConnectedPeer parameter is some peer which is known to be connected (typically root, but if not, this method will
        // walk up from the given connectedPeer up to find a root)
        internal AutomationPeer ValidateConnected(AutomationPeer connectedPeer)
        {
            ArgumentNullException.ThrowIfNull(connectedPeer);
 
            if (_parent != null && _hwnd != IntPtr.Zero) return this;
 
            if((connectedPeer._hwnd) != IntPtr.Zero)
            {
                while(connectedPeer._parent != null) connectedPeer = connectedPeer._parent;
 
                //now connectedPeer is the root
                if ((connectedPeer == this) || isDescendantOf(connectedPeer))
                    return this;
            }
 
            //last effort - find across all roots
            //only start fault in the tree from the root if we are not in the recursive sync update
            //Otherwise it will go through the peers that are currently on the stack
            ContextLayoutManager lm = ContextLayoutManager.From(this.Dispatcher);
            if(lm != null && lm.AutomationSyncUpdateCounter == 0)
            {
                AutomationPeer[] roots = lm.GetAutomationRoots();
                for(int i = 0; i < roots.Length; i++)
                {
                    AutomationPeer root = roots[i];
 
                    if (root != null)
                    {
                        if((root == this) || isDescendantOf(root))
                        return this;
                    }
                }
            }
 
            return null;
        }
 
        /// <summary>
        /// This is responsible for adding parent info like the parent window handle
        /// and the parent itself to it's child. This is by definition is a securitycritical operation
        /// for two reasons
        /// 1. it's doing an action which is securitycritical
        /// 2. it can not be treated as safe as it doesn't know whether
        ///    the peer is actually this objects's parent or not and must be used by methods which has
        /// </summary>
        /// <param name="peer"></param>
        internal bool TrySetParentInfo(AutomationPeer peer)
        {
            Invariant.Assert((peer != null));
 
            if(peer._hwnd == IntPtr.Zero)
            {
                // parent is not yet part of Automation Tree itself
                return false;
            }
 
            _hwnd = peer._hwnd;
            _parent = peer;
 
            return true;
        }
 
        // To determine if the peer corresponds to DataItem
        virtual internal bool IsDataItemAutomationPeer()
        {
            return false;
        }
 
        // UpdatePeer is called asynchronously.  Between the time the call is
        // posted (InvalidatePeer) and the time the call is executed (UpdatePeer),
        // changes to the visual tree and/or automation tree may have eliminated
        // the need for UpdatePeer, or even made it a mistake.
        // Subclasses can override this method if they can detect this situation.
        virtual internal bool IgnoreUpdatePeer()
        {
            return false;
        }
 
        // This is mainly for enabling ITemsControl to keep the Cache of the Item's Proxy Weak Ref to
        // re-use the item peers being passed to clinet and still exist in memory
        virtual internal void AddToParentProxyWeakRefCache()
        {
            //do nothing
        }
        private bool isDescendantOf(AutomationPeer parent)
        {
            ArgumentNullException.ThrowIfNull(parent);
 
            List<AutomationPeer> children  = parent.GetChildren();
 
            if(children == null)
                return false;
 
            int cnt = children.Count;
            for(int i = 0; i < cnt; ++i)
            {
                AutomationPeer child = children[i];
 
                //depth first
                if(child == this || this.isDescendantOf(child))
                    return true;
            }
 
            return false;
        }
 
        ///<summary>
        /// Outside of hosting scenarios AutomationPeers shoudl not override this method.
        /// It is needed for peers that implement their own host HWNDs
        /// for these HWNDs to appear in a proper place in the UIA tree.
        /// Without this interface being omplemented, the HWND is parented by UIA as a child
        /// of the HwndSource that hosts whole Avalon app. Instead, it is usually desirable
        /// to override this defautl behavior and tell UIA to parent hosted HWND as a child
        /// somewhere in Avlaon tree where it is actually hosted.
        /// <para/>
        /// Automation infrastructure provides necessary hookup, the AutomationPeer of the element that
        /// immediately hosts the HWND should implement this interface to be properly wired in.
        /// In addition to that, it should return this peer as IRawElementProviderSimple as a response to
        /// WM_GETOBJECT coming to the hosted HWND.
        /// <para/>
        /// To obtain the IRawElementProviderSimple interface, the peer should use
        /// System.Windows.Automation.AutomationInteropProvider.HostProviderFromHandle(hwnd).
        ///</summary>
        virtual protected HostedWindowWrapper GetHostRawElementProviderCore()
        {
            HostedWindowWrapper host = null;
 
            //in normal Avalon subtrees, only root peers should return wrapped HWND
            if(GetParent() == null)
            {
                // this way of creating HostedWindowWrapper does not require FullTrust
                host = HostedWindowWrapper.CreateInternal(Hwnd);
            }
 
            return host;
        }
 
        internal HostedWindowWrapper GetHostRawElementProvider()
        {
            return GetHostRawElementProviderCore();
        }
 
        ///<summary>
        /// Returns 'true' only if this is a peer that hosts HWND in Avalon (WindowsFormsHost or Popup for example).
        /// Such peers also have to override GetHostRawElementProviderCore method.
        ///</summary>
        virtual protected internal bool IsHwndHost { get { return false; }}
 
        //
        // P R O P E R T I E S
        //
 
        ///
        abstract protected Rect GetBoundingRectangleCore();
 
        ///
        abstract protected bool IsOffscreenCore();
 
        ///
        abstract protected AutomationOrientation GetOrientationCore();
 
        ///
        abstract protected string GetItemTypeCore();
 
        ///
        abstract protected string GetClassNameCore();
 
        ///
        abstract protected string GetItemStatusCore();
 
        ///
        abstract protected bool IsRequiredForFormCore();
 
        ///
        abstract protected bool IsKeyboardFocusableCore();
 
        ///
        abstract protected bool HasKeyboardFocusCore();
 
        ///
        abstract protected bool IsEnabledCore();
 
        ///
        abstract protected bool IsPasswordCore();
 
        ///
        abstract protected string GetAutomationIdCore();
 
        ///
        abstract protected string GetNameCore();
 
        ///
        abstract protected AutomationControlType GetAutomationControlTypeCore();
 
        ///
        virtual protected string GetLocalizedControlTypeCore()
        {
            ControlType controlType = GetControlType();
            return controlType.LocalizedControlType;
        }
 
        ///
        abstract protected bool IsContentElementCore();
 
        ///
        abstract protected bool IsControlElementCore();
 
        ///
        virtual protected bool IsDialogCore(){
            return false;
        }
 
        ///
        abstract protected AutomationPeer GetLabeledByCore();
 
        ///
        abstract protected string GetHelpTextCore();
 
        ///
        abstract protected string GetAcceleratorKeyCore();
 
        ///
        abstract protected string GetAccessKeyCore();
 
        ///
        abstract protected Point GetClickablePointCore();
 
        ///
        abstract protected void SetFocusCore();
 
        ///
        virtual protected AutomationLiveSetting GetLiveSettingCore()
        {
            return AutomationLiveSetting.Off;
        }
 
        /// <summary>
        /// Override this method to provide UIAutomation with a list of elements affected or controlled by this AutomationPeer.
        /// </summary>
        /// <returns>
        /// A list of AutomationPeers for the controlled elements.
        /// </returns>
        virtual protected List<AutomationPeer> GetControlledPeersCore()
        {
            return null;
        }
 
        /// <summary>
        /// Override this method to provide UIAutomation with an integer value describing the size of a group or set this element belongs to.
        /// </summary>
        virtual protected int GetSizeOfSetCore()
        {
            return AutomationProperties.AutomationSizeOfSetDefault;
        }
 
        /// <summary>
        /// Override this method to provide UIAutomation with a 1-based integer value describing the position this element occupies in a group or set.
        /// </summary>
        virtual protected int GetPositionInSetCore()
        {
            return AutomationProperties.AutomationPositionInSetDefault;
        }
 
        /// <summary>
        /// Override this method to provide UIAutomation with the heading level of this element.
        /// </summary>
        virtual protected AutomationHeadingLevel GetHeadingLevelCore()
        {
            return AutomationHeadingLevel.None;
        }
 
        //
        // INTERNAL STUFF - NOT OVERRIDABLE
        //
        virtual internal Rect GetVisibleBoundingRectCore()
        {
            // Too late to add abstract methods, since this class has already shipped(using default definition)!
            return GetBoundingRectangle();
        }
 
        ///
        public Rect GetBoundingRectangle()
        {
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                _boundingRectangle = GetBoundingRectangleCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return _boundingRectangle;
        }
 
        ///
        public bool IsOffscreen()
        {
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                _isOffscreen = IsOffscreenCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return _isOffscreen;
        }
 
        ///
        public AutomationOrientation GetOrientation()
        {
            AutomationOrientation result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetOrientationCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public string GetItemType()
        {
            string result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetItemTypeCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public string GetClassName()
        {
            string result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetClassNameCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public string GetItemStatus()
        {
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                _itemStatus = GetItemStatusCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return _itemStatus;
        }
 
        ///
        public bool IsRequiredForForm()
        {
            bool result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = IsRequiredForFormCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public bool IsKeyboardFocusable()
        {
            bool result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = IsKeyboardFocusableCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public bool HasKeyboardFocus()
        {
            bool result;
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = HasKeyboardFocusCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public bool IsEnabled()
        {
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                _isEnabled = IsEnabledCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return _isEnabled;
        }
 
        ///
        public bool IsPassword()
        {
            bool result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = IsPasswordCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public string GetAutomationId()
        {
            string result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetAutomationIdCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public string GetName()
        {
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                _name = GetNameCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return _name;
        }
 
        ///
        public AutomationControlType GetAutomationControlType()
        {
            AutomationControlType result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetAutomationControlTypeCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public string GetLocalizedControlType()
        {
            string result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetLocalizedControlTypeCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public bool IsContentElement()
        {
            bool result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
 
                // As per the UIA guidelines an entity has to be a control to be able to hold content.
                // See http://msdn.microsoft.com/en-us/library/system.windows.automation.automationelement.iscontentelementproperty.aspx
 
                result = IsControlElementPrivate() && IsContentElementCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public bool IsControlElement()
        {
            bool result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = IsControlElementPrivate();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        private bool IsControlElementPrivate()
        {
            return IsControlElementCore();
        }
 
        ///
        public AutomationPeer GetLabeledBy()
        {
            AutomationPeer result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetLabeledByCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public string GetHelpText()
        {
            string result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetHelpTextCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public string GetAcceleratorKey()
        {
            string result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetAcceleratorKeyCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public string GetAccessKey()
        {
            string result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetAccessKeyCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public Point GetClickablePoint()
        {
            Point result;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                if (IsOffscreenCore())
                {
                    result = new Point(double.NaN, double.NaN);
                }
                else
                {
                    result = GetClickablePointCore();
                }
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public void SetFocus()
        {
            if (_publicSetFocusInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicSetFocusInProgress = true;
                SetFocusCore();
            }
            finally
            {
                _publicSetFocusInProgress = false;
            }
        }
 
        ///
        public AutomationLiveSetting GetLiveSetting()
        {
            AutomationLiveSetting result = AutomationLiveSetting.Off;
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetLiveSettingCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        /// <summary>
        /// This method provides UIAuatomation with a list of elements affected or controlled by this AutomationPeer.
        /// </summary>
        /// <returns>
        /// A list of AutomationPeers for the controlled elements.
        /// </returns>
        public List<AutomationPeer> GetControlledPeers()
        {
            List<AutomationPeer> result = null;
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetControlledPeersCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        /// <summary>
        ///     Calls <see cref="GetControlledPeers"/> to get a list of AutomationPeers then transforms it into an array
        ///     of <see cref="IRawElementProviderSimple"/> to provide the ControlleFor property to UIA.
        /// </summary>
        /// <returns>
        ///     An array of <see cref="IRawElementProviderSimple"/> representing the AutomationPeers provided by <see cref="GetControlledPeers"/>
        /// </returns>
        private IRawElementProviderSimple[] GetControllerForProviderArray()
        {
            List<AutomationPeer> controlledPeers = GetControlledPeers();
            IRawElementProviderSimple[] result = null;
 
            if (controlledPeers != null)
            {
                result = new IRawElementProviderSimple[controlledPeers.Count];
 
                for (int i = 0; i < controlledPeers.Count; i++)
                {
                    result[i] = ProviderFromPeer(controlledPeers[i]);
                }
            }
 
            return result;
        }
        /// <summary>
        /// Attempt to get the value for the SizeOfSet property.
        /// </summary>
        /// <remarks>
        /// This public call cannot be attempted if another public call is in progress.
        /// </remarks>
        /// <returns>
        /// The value for the SizeOfSet property.
        /// </returns>
        public int GetSizeOfSet()
        {
            int result = AutomationProperties.AutomationSizeOfSetDefault;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetSizeOfSetCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        /// <summary>
        /// Attempt to get the value for the HeadingLevel property.
        /// </summary>
        /// <remarks>
        /// This public call cannot be attempted if another public call is in progress.
        /// </remarks>
        /// <returns>
        /// The value for the HeadingLevel property.
        /// </returns>
        public AutomationHeadingLevel GetHeadingLevel()
        {
            AutomationHeadingLevel result = AutomationHeadingLevel.None;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetHeadingLevelCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
 
        private enum HeadingLevel
        {
            None = 80050,
            Level1,
            Level2,
            Level3,
            Level4,
            Level5,
            Level6,
            Level7,
            Level8,
            Level9,
        }
        private static HeadingLevel ConvertHeadingLevelToId(AutomationHeadingLevel value){
            switch(value)
            {
                case AutomationHeadingLevel.None:
                    return HeadingLevel.None;
                case AutomationHeadingLevel.Level1:
                    return HeadingLevel.Level1;
                case AutomationHeadingLevel.Level2:
                    return HeadingLevel.Level2;
                case AutomationHeadingLevel.Level3:
                    return HeadingLevel.Level3;
                case AutomationHeadingLevel.Level4:
                    return HeadingLevel.Level4;
                case AutomationHeadingLevel.Level5:
                    return HeadingLevel.Level5;
                case AutomationHeadingLevel.Level6:
                    return HeadingLevel.Level6;
                case AutomationHeadingLevel.Level7:
                    return HeadingLevel.Level7;
                case AutomationHeadingLevel.Level8:
                    return HeadingLevel.Level8;
                case AutomationHeadingLevel.Level9:
                    return HeadingLevel.Level9;
                default:
                    return HeadingLevel.None;
            }
        }
 
 
        /// <summary>
        /// Attempt to get the value for the IsDialog property.
        /// </summary>
        /// <remarks>
        /// This public call cannot be attempted if another public call is in progress.
        /// </remarks>
        /// <returns>
        /// The value for the IsDialog property.
        /// </returns>
        public bool IsDialog()
        {
            bool result = false;
            if(_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = IsDialogCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        /// <summary>
        /// Attempt to get the value for the PositionInSet property.
        /// </summary>
        /// <remarks>
        /// This public call cannot be attempted if another public call is in progress.
        /// </remarks>
        /// <returns>
        /// The value for the PositionInSet property.
        /// </returns>
        public int GetPositionInSet()
        {
            int result = AutomationProperties.AutomationPositionInSetDefault;
 
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                result = GetPositionInSetCore();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return result;
        }
 
        ///
        public AutomationPeer GetParent()
        {
            return _parent;
        }
 
        ///
        public List<AutomationPeer> GetChildren()
        {
            if (_publicCallInProgress)
                throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
 
            try
            {
                _publicCallInProgress = true;
                EnsureChildren();
            }
            finally
            {
                _publicCallInProgress = false;
            }
            return _children;
        }
 
        ///
        public void ResetChildrenCache()
        {
            using (UpdateChildren())
            {
            }
        }
 
        ///
        internal int[] GetRuntimeId()
        {
            return new int [] { 7, Environment.ProcessId, this.GetHashCode() };
        }
 
        ///
        internal string GetFrameworkId() { return ("WPF"); }
 
        //
        internal AutomationPeer GetFirstChild()
        {
            AutomationPeer peer = null;
 
            EnsureChildren();
 
            if (_children != null && _children.Count > 0)
            {
                peer = _children[0];
                peer.ChooseIterationParent(this);
            }
 
            return peer;
        }
 
        //
        private void EnsureChildren()
        {
            //  if !_childrenValid or _ancestorsInvalid,  indicates that the automation tree under this peer is not up to date, so requires
            //  rebuilding before navigating. This usually is the case when the peer is re-parented because of UI changes but
            // UpdateSubtree is not called on it yet.
            if (!_childrenValid || _ancestorsInvalid)
            {
                _children = GetChildrenCore();
                if (_children != null)
                {
                    int count = _children.Count;
                    for (int i = 0; i < count; ++i)
                    {
                        _children[i]._parent = this;
                        _children[i]._index = i;
                        _children[i]._hwnd = _hwnd;
                    }
                }
                _childrenValid = true;
            }
        }
 
        // Use to update the Children irrespective of the _childrenValid flag.
        internal void ForceEnsureChildren()
        {
            _childrenValid = false;
            EnsureChildren();
        }
 
        //
        internal AutomationPeer GetLastChild()
        {
            AutomationPeer peer = null;
 
            EnsureChildren();
 
            if (_children != null && _children.Count > 0)
            {
                peer = _children[_children.Count - 1];
                peer.ChooseIterationParent(this);
            }
 
            return peer;
        }
 
        //
        internal virtual InteropAutomationProvider GetInteropChild()
        {
            return null;
        }
 
        //
        internal AutomationPeer GetNextSibling()
        {
            AutomationPeer sibling = null;
            AutomationPeer parent = IterationParent;
 
            if (parent != null)
            {
                parent.EnsureChildren();
 
                if (    parent._children != null
                    &&  _index >= 0
                    &&  _index + 1 < parent._children.Count
                    &&  parent._children[_index] == this    )
                {
                    sibling = parent._children[_index + 1];
                    sibling.IterationParent = parent;
                }
            }
 
            return sibling;
        }
 
        //
        internal AutomationPeer GetPreviousSibling()
        {
            AutomationPeer sibling = null;
            AutomationPeer parent = IterationParent;
 
            if (parent != null)
            {
                parent.EnsureChildren();
 
                if (    parent._children != null
                    &&  _index - 1 >= 0
                    &&  _index < parent._children.Count
                    &&  parent._children[_index] == this    )
                {
                    sibling = parent._children[_index - 1];
                    sibling.IterationParent = parent;
                }
            }
 
            return sibling;
        }
 
        // For ItemsControls, there are typically two different peers for each
        // (visible) item:  an "item" peer and a "wrapper" peer.  The former
        // corresponds to the item itself, the latter to the container.  (These
        // are different peers, even when the item is its own container.)  The item
        // peer appears as a child of the ItemsControl's peer, while the wrapper
        // peer is what you get by asking the container for its peer directly.
        // For example, a ListBoxAutomationPeer holds children of type
        // ListBoxItemAutomationPeer, while the peer for a ListBoxItem has type
        // ListBoxItemWrapperAutomationPeer.
        //
        // The item and wrapper peers for a particular item can (and do) share
        // children, i.e. the _children lists can contain the same elements.  The
        // _parent pointer in a (shared) child peer could point to either the
        // item peer or the wrapper peer, depending on which parent peer rebuilt
        // its _children collection (EnsureChildren) most recently.  This is
        // confusing, but usually the two lists are identical so the navigation
        // methods (GetFirstChild, GetNextSibling, etc.) produce the correct
        // result regardless of which parent is used.
        //
        // It matters for TabControl, however.  The item peer (TabItemAutomationPeer)
        // and wrapper peer (TabItemWrapperAutomationPeer) contain the same
        // children, except when the TabItem is selected (so that its content
        // is shown in the TabControls large display area).  In that case the item
        // peer has extra children, corresponding to the displayed content.
        //
        // When an app does something that causes the wrapper peer to call
        // EnsureChildren (e.g. changes TabControl.IsEnabled), the shared
        // children all point back to the wrapper peer, while the extra children
        // still point back to the item peer.  An automation client that searches
        // through, or iterates over, the children of the item peer will not see
        // the extra children.  It (effectively) calls itemPeer.GetFirstChild,
        // followed by repeated calls to childPeer.GetNextSibling;  those calls
        // are evaluated using the _parent's child collection, but _parent points
        // to the wrapper peer (which doesn't have the extra children).
        //
        // To patch this problem, the navigation methods should use the parent that
        // started the iteration, i.e. the one where GetFirstChild was called.  But
        // we can't change _parent (the "custody parent") - we tried that, and it
        // caused compatibility problems (crashes). Instead we add a new pointer, referring to the
        // "iteration parent". GetFirstChild sets it, and GetNextSibling propagates
        // it through the family as the iteration proceeds.  We only need to do
        // this if the iteration parent differs from the custody parent and has
        // a different set of children (a rare situation), so to make it pay-for-play
        // we implement the new pointer as an indirection through an existing field.
        // We chose _eventsSource - it's already encapsulated in a property, and is used
        // in situations that aren't perf-critical (creation of new peers, et al.).
 
        // Called by GetFirstChild, GetLastChild.
        // Choose which of the custody parent (_parent) and the caller
        // should serve as the iteration parent for the iteration that's starting.
        private void ChooseIterationParent(AutomationPeer caller)
        {
            Debug.Assert(caller._children != null, "iteration over a null family");
            AutomationPeer iterationParent;
 
            // easy (and frequent) case:  both candidates are the same
            // easy case: custody parent is null (preserve original behavior)
            if (_parent == caller || _parent == null)
            {
                iterationParent = _parent;
            }
            else
            {
                // Usually we choose the custody parent.   The only case that needs
                // something different is the TabItem case described earlier, which we
                // recognize by the fact that the families have different size.
                //
                // There's also a funky case when the custody parent's family is null,
                // even after EnsureChildren.  Like the "custodyParent == null" case above,
                // I'm not sure this can ever happen, but if it does we choose the
                // custody parent;  this preserves the original logic that terminates
                // the iteration at the next call to GetNextSibling.
                _parent.EnsureChildren();
                iterationParent = (_parent._children == null || _parent._children.Count == caller._children.Count)
                    ? _parent : caller;
            }
 
            IterationParent = iterationParent;
        }
 
        private AutomationPeer IterationParent
        {
            get
            {
                return !_hasIterationParent ? _parent : ((PeerRecord)_eventsSourceOrPeerRecord).IterationParent;
            }
            set
            {
                if (value == _parent)
                {
                    if (_hasIterationParent)
                    {
                        // remove the indirection
                        _eventsSourceOrPeerRecord = EventsSource;
                        _hasIterationParent = false;
                    }
                    else
                    {
                        // this is the 90% case - nothing to do
                    }
                }
                else
                {
                    if (!_hasIterationParent)
                    {
                        // add the indirection
                        PeerRecord record = new PeerRecord { EventsSource = EventsSource, IterationParent = value };
                        _eventsSourceOrPeerRecord = record;
                        _hasIterationParent = true;
                    }
                    else
                    {
                        // revise the existing indirection
                        ((PeerRecord)_eventsSourceOrPeerRecord).IterationParent = value;
                    }
                }
            }
        }
 
        //
        internal ControlType GetControlType()
        {
            ControlType controlType = null;
 
            AutomationControlType type = GetAutomationControlTypeCore();
 
            switch (type)
            {
                case AutomationControlType.Button:        controlType = ControlType.Button;       break;
                case AutomationControlType.Calendar:      controlType = ControlType.Calendar;     break;
                case AutomationControlType.CheckBox:      controlType = ControlType.CheckBox;     break;
                case AutomationControlType.ComboBox:      controlType = ControlType.ComboBox;     break;
                case AutomationControlType.Edit:          controlType = ControlType.Edit;         break;
                case AutomationControlType.Hyperlink:     controlType = ControlType.Hyperlink;    break;
                case AutomationControlType.Image:         controlType = ControlType.Image;        break;
                case AutomationControlType.ListItem:      controlType = ControlType.ListItem;     break;
                case AutomationControlType.List:          controlType = ControlType.List;         break;
                case AutomationControlType.Menu:          controlType = ControlType.Menu;         break;
                case AutomationControlType.MenuBar:       controlType = ControlType.MenuBar;      break;
                case AutomationControlType.MenuItem:      controlType = ControlType.MenuItem;     break;
                case AutomationControlType.ProgressBar:   controlType = ControlType.ProgressBar;  break;
                case AutomationControlType.RadioButton:   controlType = ControlType.RadioButton;  break;
                case AutomationControlType.ScrollBar:     controlType = ControlType.ScrollBar;    break;
                case AutomationControlType.Slider:        controlType = ControlType.Slider;       break;
                case AutomationControlType.Spinner:       controlType = ControlType.Spinner;      break;
                case AutomationControlType.StatusBar:     controlType = ControlType.StatusBar;    break;
                case AutomationControlType.Tab:           controlType = ControlType.Tab;          break;
                case AutomationControlType.TabItem:       controlType = ControlType.TabItem;      break;
                case AutomationControlType.Text:          controlType = ControlType.Text;         break;
                case AutomationControlType.ToolBar:       controlType = ControlType.ToolBar;      break;
                case AutomationControlType.ToolTip:       controlType = ControlType.ToolTip;      break;
                case AutomationControlType.Tree:          controlType = ControlType.Tree;         break;
                case AutomationControlType.TreeItem:      controlType = ControlType.TreeItem;     break;
                case AutomationControlType.Custom:        controlType = ControlType.Custom;       break;
                case AutomationControlType.Group:         controlType = ControlType.Group;        break;
                case AutomationControlType.Thumb:         controlType = ControlType.Thumb;        break;
                case AutomationControlType.DataGrid:      controlType = ControlType.DataGrid;     break;
                case AutomationControlType.DataItem:      controlType = ControlType.DataItem;     break;
                case AutomationControlType.Document:      controlType = ControlType.Document;     break;
                case AutomationControlType.SplitButton:   controlType = ControlType.SplitButton;  break;
                case AutomationControlType.Window:        controlType = ControlType.Window;       break;
                case AutomationControlType.Pane:          controlType = ControlType.Pane;         break;
                case AutomationControlType.Header:        controlType = ControlType.Header;       break;
                case AutomationControlType.HeaderItem:    controlType = ControlType.HeaderItem;   break;
                case AutomationControlType.Table:         controlType = ControlType.Table;        break;
                case AutomationControlType.TitleBar:      controlType = ControlType.TitleBar;     break;
                case AutomationControlType.Separator:     controlType = ControlType.Separator;    break;
                default: break;
            }
 
            return controlType;
        }
 
        public AutomationPeer GetPeerFromPoint(Point point)
        {
            return GetPeerFromPointCore(point);
        }
 
        protected virtual AutomationPeer GetPeerFromPointCore(Point point)
        {
            AutomationPeer found = null;
 
            if(!IsOffscreen())
            {
                List<AutomationPeer> children = GetChildren();
                if(children != null)
                {
                    int count = children.Count;
                    for(int i = count-1; (i >= 0) && (found == null); --i)
                    {
                        found = children[i].GetPeerFromPoint(point);
                    }
                }
 
                if(found == null)
                {
                    Rect bounds = GetVisibleBoundingRect();
                    if (bounds.Contains(point))
                        found = this;
                }
            }
 
            return found;
        }
 
        /// <summary>
        /// inherited by item peers to return just visible part of bounding rectangle.
        /// </summary>
        /// <returns></returns>
        internal Rect GetVisibleBoundingRect()
        {
            return GetVisibleBoundingRectCore();
        }
 
        ///<Summary>
        /// Creates an element provider (proxy) from a peer. Some patterns require returning objects of type
        /// IRawElementProviderSimple - this is an Automation-specific wrapper interface that corresponds to a peer.
        /// To wrap an AutomationPeer into the wrapper that exposes this interface, use this method.
        ///</Summary>
        protected internal IRawElementProviderSimple ProviderFromPeer(AutomationPeer peer)
        {
            AutomationPeer referencePeer = this;
 
            //replace itself with EventsSource if we are aggregated and hidden from the UIA
            AutomationPeer eventsSource;
            if((peer == this) && ((eventsSource = EventsSource) != null))
            {
                referencePeer = peer = eventsSource;
            }
 
            return ElementProxy.StaticWrap(peer, referencePeer);
        }
 
        private IRawElementProviderSimple ProviderFromPeerNoDelegation(AutomationPeer peer)
        {
            AutomationPeer referencePeer = this;
            return ElementProxy.StaticWrap(peer, referencePeer);
        }
 
        ///<Summary>
        /// When one AutomationPeer is using the pattern of another AutomationPeer instead of exposing
        /// it in the children collection (example - ListBox exposes IScrollProvider from internal ScrollViewer
        /// but does not expose the ScrollViewerAutomationPeer as its child) - then before returning the pattern
        /// interface from GetPattern, the "main" AutomationPeer should call this method to set up itself as
        /// "source" for the events fired by the pattern on the subordinate AutomationPeer.
        /// Otherwise, the hidden subordinate AutomationPeer will fire pattern's events from its own identity which
        /// will confuse UIA since its identity is not exposed to UIA.
        ///</Summary>
        public AutomationPeer EventsSource
        {
            get
            {
                return !_hasIterationParent
                    ? (AutomationPeer)_eventsSourceOrPeerRecord
                    : ((PeerRecord)_eventsSourceOrPeerRecord).EventsSource;
            }
            set
            {
                if (!_hasIterationParent)
                {
                    _eventsSourceOrPeerRecord = value;
                }
                else
                {
                    ((PeerRecord)_eventsSourceOrPeerRecord).EventsSource = value;
                }
            }
        }
 
 
        ///<Summary>
        /// Returns AutomationPeer corresponding to the given provider.
        ///</Summary>
        protected AutomationPeer PeerFromProvider(IRawElementProviderSimple provider)
        {
            ElementProxy proxy = provider as ElementProxy;
            if (proxy != null)
            {
                return (proxy.Peer);
            }
 
            return null;
        }
 
        //called on a root peer of a tree when it's time to fire automation events
        //walks down the tree, updates caches and fires automation events
        internal void FireAutomationEvents()
        {
            UpdateSubtree();
        }
 
        // internal handling of structure chanegd events
        private void RaisePropertyChangedInternal(IRawElementProviderSimple provider,
                                                             AutomationProperty propertyId,
                                                             object oldValue,
                                                             object newValue)
        {
            // Callers have only checked if automation clients are present so filter for any interest in this particular event.
            if (  provider != null
               && EventMap.HasRegisteredEvent(AutomationEvents.PropertyChanged) )
            {
                AutomationPropertyChangedEventArgs e = new AutomationPropertyChangedEventArgs(propertyId, oldValue, newValue);
                AutomationInteropProvider.RaiseAutomationPropertyChangedEvent(provider, e);
            }
#if ENABLE_AUTOMATIONPEER_LOGGING
            LogPropertyChanged(this, propertyId);
#endif
        }
 
        // InvalidateLimit is the lower bound for raising ChildrenInvalidated StructureChange event
        internal void UpdateChildrenInternal(int invalidateLimit)
        {
            List<AutomationPeer> oldChildren = _children;
            List<AutomationPeer> addedChildren = null;
            HashSet<AutomationPeer> hs = null;
 
            _childrenValid = false;
            EnsureChildren();
 
            // Callers have only checked if automation clients are present so filter for any interest in this particular event.
            if (!EventMap.HasRegisteredEvent(AutomationEvents.StructureChanged))
                return;
 
            //store old children in a hashset
            if(oldChildren != null)
            {
                hs = new HashSet<AutomationPeer>();
                for(int count = oldChildren.Count, i = 0; i < count; i++)
                {
                    hs.Add(oldChildren[i]);
                }
            }
 
            //walk over new children, remove the ones that were in the old collection from hash table
            //and add new ones into addedChildren list
            int addedCount = 0;
 
            if(_children != null)
            {
                for(int count = _children.Count, i = 0; i < count; i++)
                {
                    AutomationPeer child = _children[i];
                    if(hs != null && hs.Contains(child))
                    {
                        hs.Remove(child); //same child, nothing to notify
                    }
                    else
                    {
                        if(addedChildren == null) addedChildren = new List<AutomationPeer>();
 
                        //stop accumulatin new children here because the notification
                        //is going to become "bulk anyways and exact set of chidlren is not
                        //needed, only count.
                        ++addedCount;
                        if(addedCount <= invalidateLimit)
                            addedChildren.Add(child);
                    }
                }
            }
 
            //now the hs only has "removed" children. If the count does not yet
            //calls for "bulk" notification, use per-child notification, otherwise use "bulk"
            int removedCount = (hs == null ? 0 : hs.Count);
 
            if(removedCount + addedCount > invalidateLimit) //bilk invalidation
            {
                StructureChangeType flags;
 
                // Set bulk event type depending on if these were adds, removes or a mix
                if (addedCount == 0)
                    flags = StructureChangeType.ChildrenBulkRemoved;
                else if ( removedCount == 0 )
                    flags = StructureChangeType.ChildrenBulkAdded;
                else
                    flags = StructureChangeType.ChildrenInvalidated;
 
                IRawElementProviderSimple provider = ProviderFromPeerNoDelegation(this);
                if(provider != null)
                {
                    int [] rid = this.GetRuntimeId(); //use runtimeID of parent for bulk notifications
 
                    AutomationInteropProvider.RaiseStructureChangedEvent(
                                    provider,
                                    new StructureChangedEventArgs(flags, rid));
                }
            }
            else
            {
                if (removedCount > 0)
                {
                    //for children removed, provider is the parent
                    IRawElementProviderSimple provider = ProviderFromPeerNoDelegation(this);
                    if (provider != null)
                    {
                        //hs contains removed children by now
                        foreach (AutomationPeer removedChild in hs)
                        {
                            int[] rid = removedChild.GetRuntimeId();
 
                            AutomationInteropProvider.RaiseStructureChangedEvent(
                                            provider,
                                            new StructureChangedEventArgs(StructureChangeType.ChildRemoved, rid));
                        }
                    }
                }
                if (addedCount > 0)
                {
                    //hs contains removed children by now
                    foreach (AutomationPeer addedChild in addedChildren)
                    {
                        //for children added, provider is the child itself
                        IRawElementProviderSimple provider = ProviderFromPeerNoDelegation(addedChild);
                        if (provider != null)
                        {
                            int[] rid = addedChild.GetRuntimeId();
 
                            AutomationInteropProvider.RaiseStructureChangedEvent(
                                            provider,
                                            new StructureChangedEventArgs(StructureChangeType.ChildAdded, rid));
                        }
                    }
                }
            }
        }
 
        // internal handling of structure changed events
        virtual internal IDisposable UpdateChildren()
        {
            UpdateChildrenInternal(AutomationInteropProvider.InvalidateLimit);
            return null;
        }
 
        ///<summary>
        /// This method causes recursive walk from this peer down and updates all the cahced information -
        /// set of children and some most frequently asked properties. It also fires notifications to UIA if those change.
        /// Notrmally, the system would call this method automationcally upon layout update, walking UIA tree and calling this method.
        /// However, in some rare cases
        /// developers should use this method from implementation of custom peers - example is when peer is actually
        /// delegating the functionality to some other peer which is not even exposed in UIA tree - like ListBoxItemAP
        /// delegates the work to ListBoxItemWrapperAP. Since "subordinate" peer is not being updated by the system since it is not
        /// in the UIA tree.
        ///</summary>
        ///<remarks>
        /// Is it possible that they turn around and reenter asking for new value while in event handler?
        //  If yes, we'll serve old value
        ///</remarks>
        internal void UpdateSubtree()
        {
            ContextLayoutManager lm = ContextLayoutManager.From(this.Dispatcher);
            if(lm != null)
            {
                lm.AutomationSyncUpdateCounter = lm.AutomationSyncUpdateCounter + 1;
 
                try
                {
                    IRawElementProviderSimple provider = null;
 
 
 
 
                    bool notifyPropertyChanged = EventMap.HasRegisteredEvent(AutomationEvents.PropertyChanged);
                    bool notifyStructureChanged = EventMap.HasRegisteredEvent(AutomationEvents.StructureChanged);
 
                    //  did anybody ask for property changed norification?
                    if (notifyPropertyChanged)
                    {
                        string itemStatus = GetItemStatusCore();
                        if (itemStatus != _itemStatus)
                        {
                            if(provider == null)
                                provider = ProviderFromPeerNoDelegation(this);
                            RaisePropertyChangedInternal(provider,
                                                         AutomationElementIdentifiers.ItemStatusProperty,
                                                         _itemStatus,
                                                         itemStatus);
                            _itemStatus = itemStatus;
                        }
 
                        string name = GetNameCore();
                        if (name != _name)
                        {
                            if(provider == null)
                                provider = ProviderFromPeerNoDelegation(this);
                            RaisePropertyChangedInternal(provider,
                                                         AutomationElementIdentifiers.NameProperty,
                                                         _name,
                                                         name);
                            _name = name;
                        }
 
                        bool isOffscreen = IsOffscreenCore();
                        if (isOffscreen != _isOffscreen)
                        {
                            if(provider == null)
                                provider = ProviderFromPeerNoDelegation(this);
                            RaisePropertyChangedInternal(provider,
                                                         AutomationElementIdentifiers.IsOffscreenProperty,
                                                         _isOffscreen,
                                                         isOffscreen);
                            _isOffscreen = isOffscreen;
                        }
 
                        bool isEnabled = IsEnabledCore();
                        if (isEnabled != _isEnabled)
                        {
                            if(provider == null)
                                provider = ProviderFromPeerNoDelegation(this);
                            RaisePropertyChangedInternal(provider,
                                                         AutomationElementIdentifiers.IsEnabledProperty,
                                                         _isEnabled,
                                                         isEnabled);
                            _isEnabled = isEnabled;
                        }
}
 
                    //  did anybody ask for structure changed norification?
                    //  if somebody asked for property changed then structure must be updated
                    if (this._childrenValid? (this.AncestorsInvalid || (ControlType.Custom == this.GetControlType())) : (notifyStructureChanged || notifyPropertyChanged))
                    {
                        using (UpdateChildren())
                        {
                            AncestorsInvalid = false;
 
                            for(AutomationPeer peer = GetFirstChild(); peer != null; peer = peer.GetNextSibling())
                            {
                                peer.UpdateSubtree();
                            }
                        }
                    }
                    AncestorsInvalid = false;
                    _invalidated = false;
                }
                finally
                {
                    lm.AutomationSyncUpdateCounter = lm.AutomationSyncUpdateCounter - 1;
                }
            }
        }
 
        /// <summary>
        /// propagate the new value for AncestorsInvalid through the parent chain,
        /// use EventSource (wrapper) peers whenever available as it's the one connected to the tree.
        /// </summary>
        internal void InvalidateAncestorsRecursive()
        {
            if (!AncestorsInvalid)
            {
                AncestorsInvalid = true;
                if (EventsSource != null)
                {
                    EventsSource.InvalidateAncestorsRecursive();
                }
 
                if (_parent != null)
                    _parent.InvalidateAncestorsRecursive();
            }
        }
 
        private static object UpdatePeer(object arg)
        {
            AutomationPeer peer = (AutomationPeer)arg;
            if (!peer.IgnoreUpdatePeer())
            {
                peer.UpdateSubtree();
            }
            return null;
        }
 
        internal void AddToAutomationEventList()
        {
            if(!_addedToEventList)
            {
                ContextLayoutManager lm = ContextLayoutManager.From(this.Dispatcher);
                lm.AutomationEvents.Add(this); //this adds the root peer into the list of roots, for deferred event firing
                _addedToEventList = true;
            }
}
 
        internal IntPtr Hwnd
        {
            get { return _hwnd; }
            set { _hwnd = value; }
        }
 
        //
        internal object GetWrappedPattern(int patternId)
        {
            object result = null;
 
            PatternInfo info = (PatternInfo)s_patternInfo[patternId];
 
            if (info != null)
            {
                object iface = GetPattern(info.PatternInterface);
                if (iface != null)
                {
                    result = info.WrapObject(this, iface);
                }
            }
 
            return result;
        }
 
        //
        internal object GetPropertyValue(int propertyId)
        {
            object result = null;
 
            GetProperty getProperty = (GetProperty)s_propertyInfo[propertyId];
 
            if (getProperty != null)
            {
                result = getProperty(this);
                if(AutomationElementIdentifiers.HeadingLevelProperty != null && propertyId == AutomationElementIdentifiers.HeadingLevelProperty.Id)
                {
                    result = ConvertHeadingLevelToId((AutomationHeadingLevel)result);
                }
            }
 
            return result;
        }
 
        //
        internal virtual bool AncestorsInvalid
        {
            get { return _ancestorsInvalid; }
            set { _ancestorsInvalid = value; }
        }
 
        //
        internal bool ChildrenValid
        {
            get { return _childrenValid; }
            set { _childrenValid = value; }
        }
 
        //
        internal bool IsInteropPeer
        {
            get { return _isInteropPeer; }
            set { _isInteropPeer = value; }
        }
 
        //
        internal int Index
        {
            get { return _index; }
        }
 
        //
        internal List<AutomationPeer> Children
        {
            get { return _children; }
        }
 
        // To Keep the WeakRefernce of ElementProxy wrapper for this peer so that it can be reused
        // rather than creating the new Wrapper object if there is need and it still exist.
        internal WeakReference ElementProxyWeakReference
        {
            get{ return _elementProxyWeakReference; }
            set
            {
                if(value.Target as ElementProxy != null)
                    _elementProxyWeakReference = value;
            }
        }
 
        // Determine whether invisible items should show up in UIAutomation Control View
        internal bool IncludeInvisibleElementsInControlView
        {
            get
            {
                //  As part of this breaking change, invisible items should no longer show in
                // UIAutomation's Control View, as usual with other accessibility breaking changes we control
                // whether the user gets the new behavior or not via the UseLegacyAccessibilityFeatures3 AppContext flag.
                return AccessibilitySwitches.UseNetFx472CompatibleAccessibilityFeatures;
            }
        }
 
#if ENABLE_AUTOMATIONPEER_LOGGING
                // For diagnosing bugs (especially perf), it can be helpful to know how
                // often certain activities occur in a given timeframe.  These logs record
                // two such activities:  raising of PropertyChanged events, and creation
                // of new AutomationPeers.  Each broken down by the type of peer.
                // To use this:
                // 1. Uncomment the definition of ENABLE_AUTOMATIONPEER_LOGGING (line 1)
                // 2. Add calls to ClearLog and SummarizeLog at the beginning and end
                //      of the timeframe of interest.  For example, at the beginning and
                //      end of ContextLayoutManager.fireAutomationEvents - to record
                //      activity during a single pass of rebuilding the automation tree.
                // 3. Set the thresholds as desired (can also be done at debug time).
                // 4. Rebuild PresentationCore (and any assemblies you changed in step 2).
                // 5. Install the instrumented assemblies and run your scenario
                // 6. Set breakpoints/tracepoints in SummarizeLog to see the results of
                //      interest.  Start by tracing the summary string.
                // 7. Add logs for other activities as desired, following the obvious pattern.
 
                static Dictionary<Type, int> _pcLog = new Dictionary<Type, int>();
                static int _pcThreshold = 20;
 
                static Dictionary<Type, int> _peerLog = new Dictionary<Type, int>();
                static int _peerThreshold = 20;
 
                static void LogPropertyChanged(AutomationPeer peer, AutomationProperty property)
                {
                    int oldCount, newCount;
                    Type peerType = peer.GetType();
                    if (_pcLog.TryGetValue(peerType, out oldCount))
                    {
                        newCount = oldCount + 1;
                    }
                    else
                    {
                        newCount = 1;
                    }
                    _pcLog[peerType] = newCount;
                }
 
                static void LogPeer(AutomationPeer peer)
                {
                    int oldCount, newCount;
                    Type peerType = peer.GetType();
                    if (_peerLog.TryGetValue(peerType, out oldCount))
                    {
                        newCount = oldCount + 1;
                    }
                    else
                    {
                        newCount = 1;
                    }
                    _peerLog[peerType] = newCount;
                }
 
                static internal void ClearLog()
                {
                    _pcLog.Clear();
                    _peerLog.Clear();
                }
 
                static internal void SummarizeLog()
                {
                    SummarizeLog(_pcLog, _pcThreshold, "events");
                    SummarizeLog(_peerLog, _peerThreshold, "peers");
                }
 
                static void SummarizeLog(Dictionary<Type,int> log, int threshold, string title)
                {
                    int size = log.Count;
                    if (size == 0)
                        return;
 
                    KeyValuePair<Type,int>[] pairs = new KeyValuePair<Type,int>[size];
                    foreach (KeyValuePair<Type,int> kvp in log)
                    {
                        pairs[--size] = kvp;
                    }
 
                    Array.Sort(pairs, new LogComparer());
 
                    int largeCount = 0;
                    int sum = 0;
                    size = pairs.Length;
                    for (int i=0; i<size; ++i)
                    {
                        KeyValuePair<Type,int> kvp = pairs[i];
                        if (kvp.Value > threshold)
                        {
                            string s1 = String.Format("{0} {1}", kvp.Value, kvp.Key.Name);
                            ++largeCount;
                        }
                        sum += kvp.Value;
                    }
 
                    if (largeCount > 0)
                    {
                        string s = String.Format("--- {0} {3}, {1}/{2} types ---",
                                        sum, largeCount, size, title);
                    }
                }
 
                class LogComparer : Comparer<KeyValuePair<Type,int>>
                {
                    public override int Compare(KeyValuePair<Type,int> x, KeyValuePair<Type,int> y)
                    {
                        return (y.Value - x.Value);
                    }
                }
#endif
 
        private static void Initialize()
        {
            //  initializeing patterns
            s_patternInfo = new Hashtable();
            s_patternInfo[InvokePatternIdentifiers.Pattern.Id]          = new PatternInfo(InvokePatternIdentifiers.Pattern.Id,          new WrapObject(InvokeProviderWrapper.Wrap),             PatternInterface.Invoke);
            s_patternInfo[SelectionPatternIdentifiers.Pattern.Id]       = new PatternInfo(SelectionPatternIdentifiers.Pattern.Id,       new WrapObject(SelectionProviderWrapper.Wrap),          PatternInterface.Selection);
            s_patternInfo[ValuePatternIdentifiers.Pattern.Id]           = new PatternInfo(ValuePatternIdentifiers.Pattern.Id,           new WrapObject(ValueProviderWrapper.Wrap),              PatternInterface.Value);
            s_patternInfo[RangeValuePatternIdentifiers.Pattern.Id]      = new PatternInfo(RangeValuePatternIdentifiers.Pattern.Id,      new WrapObject(RangeValueProviderWrapper.Wrap),         PatternInterface.RangeValue);
            s_patternInfo[ScrollPatternIdentifiers.Pattern.Id]          = new PatternInfo(ScrollPatternIdentifiers.Pattern.Id,          new WrapObject(ScrollProviderWrapper.Wrap),             PatternInterface.Scroll);
            s_patternInfo[ScrollItemPatternIdentifiers.Pattern.Id]      = new PatternInfo(ScrollItemPatternIdentifiers.Pattern.Id,      new WrapObject(ScrollItemProviderWrapper.Wrap),         PatternInterface.ScrollItem);
            s_patternInfo[ExpandCollapsePatternIdentifiers.Pattern.Id]  = new PatternInfo(ExpandCollapsePatternIdentifiers.Pattern.Id,  new WrapObject(ExpandCollapseProviderWrapper.Wrap),     PatternInterface.ExpandCollapse);
            s_patternInfo[GridPatternIdentifiers.Pattern.Id]            = new PatternInfo(GridPatternIdentifiers.Pattern.Id,            new WrapObject(GridProviderWrapper.Wrap),               PatternInterface.Grid);
            s_patternInfo[GridItemPatternIdentifiers.Pattern.Id]        = new PatternInfo(GridItemPatternIdentifiers.Pattern.Id,        new WrapObject(GridItemProviderWrapper.Wrap),           PatternInterface.GridItem);
            s_patternInfo[MultipleViewPatternIdentifiers.Pattern.Id]    = new PatternInfo(MultipleViewPatternIdentifiers.Pattern.Id,    new WrapObject(MultipleViewProviderWrapper.Wrap),       PatternInterface.MultipleView);
            s_patternInfo[WindowPatternIdentifiers.Pattern.Id]          = new PatternInfo(WindowPatternIdentifiers.Pattern.Id,          new WrapObject(WindowProviderWrapper.Wrap),             PatternInterface.Window);
            s_patternInfo[SelectionItemPatternIdentifiers.Pattern.Id]   = new PatternInfo(SelectionItemPatternIdentifiers.Pattern.Id,   new WrapObject(SelectionItemProviderWrapper.Wrap),      PatternInterface.SelectionItem);
            s_patternInfo[DockPatternIdentifiers.Pattern.Id]            = new PatternInfo(DockPatternIdentifiers.Pattern.Id,            new WrapObject(DockProviderWrapper.Wrap),               PatternInterface.Dock);
            s_patternInfo[TablePatternIdentifiers.Pattern.Id]           = new PatternInfo(TablePatternIdentifiers.Pattern.Id,           new WrapObject(TableProviderWrapper.Wrap),              PatternInterface.Table);
            s_patternInfo[TableItemPatternIdentifiers.Pattern.Id]       = new PatternInfo(TableItemPatternIdentifiers.Pattern.Id,       new WrapObject(TableItemProviderWrapper.Wrap),          PatternInterface.TableItem);
            s_patternInfo[TogglePatternIdentifiers.Pattern.Id]          = new PatternInfo(TogglePatternIdentifiers.Pattern.Id,          new WrapObject(ToggleProviderWrapper.Wrap),             PatternInterface.Toggle);
            s_patternInfo[TransformPatternIdentifiers.Pattern.Id]       = new PatternInfo(TransformPatternIdentifiers.Pattern.Id,       new WrapObject(TransformProviderWrapper.Wrap),          PatternInterface.Transform);
            s_patternInfo[TextPatternIdentifiers.Pattern.Id]            = new PatternInfo(TextPatternIdentifiers.Pattern.Id,            new WrapObject(TextProviderWrapper.Wrap),               PatternInterface.Text);
 
            // To avoid the worst situation on legacy systems which may not have new unmanaged core. with this change with old unmanaged core
            // this will these patterns will be null and won't be added and hence reponse will be as it is not present at all rather than any crash.
            if (VirtualizedItemPatternIdentifiers.Pattern != null)
                s_patternInfo[VirtualizedItemPatternIdentifiers.Pattern.Id] = new PatternInfo(VirtualizedItemPatternIdentifiers.Pattern.Id, new WrapObject(VirtualizedItemProviderWrapper.Wrap), PatternInterface.VirtualizedItem);
            if (ItemContainerPatternIdentifiers.Pattern != null)
                s_patternInfo[ItemContainerPatternIdentifiers.Pattern.Id] = new PatternInfo(ItemContainerPatternIdentifiers.Pattern.Id, new WrapObject(ItemContainerProviderWrapper.Wrap), PatternInterface.ItemContainer);
            if (SynchronizedInputPatternIdentifiers.Pattern != null)
            {
                s_patternInfo[SynchronizedInputPatternIdentifiers.Pattern.Id] = new PatternInfo(SynchronizedInputPatternIdentifiers.Pattern.Id, new WrapObject(SynchronizedInputProviderWrapper.Wrap), PatternInterface.SynchronizedInput);
            }
 
            //  initializeing properties
            s_propertyInfo = new Hashtable();
            s_propertyInfo[AutomationElementIdentifiers.IsControlElementProperty.Id] = new GetProperty(IsControlElement);
            s_propertyInfo[AutomationElementIdentifiers.ControlTypeProperty.Id] = new GetProperty(GetControlType);
            s_propertyInfo[AutomationElementIdentifiers.IsContentElementProperty.Id] = new GetProperty(IsContentElement);
            s_propertyInfo[AutomationElementIdentifiers.LabeledByProperty.Id] = new GetProperty(GetLabeledBy);
            s_propertyInfo[AutomationElementIdentifiers.NativeWindowHandleProperty.Id] = new GetProperty(GetNativeWindowHandle);
            s_propertyInfo[AutomationElementIdentifiers.AutomationIdProperty.Id] = new GetProperty(GetAutomationId);
            s_propertyInfo[AutomationElementIdentifiers.ItemTypeProperty.Id] = new GetProperty(GetItemType);
            s_propertyInfo[AutomationElementIdentifiers.IsPasswordProperty.Id] = new GetProperty(IsPassword);
            s_propertyInfo[AutomationElementIdentifiers.LocalizedControlTypeProperty.Id] = new GetProperty(GetLocalizedControlType);
            s_propertyInfo[AutomationElementIdentifiers.NameProperty.Id] = new GetProperty(GetName);
            s_propertyInfo[AutomationElementIdentifiers.AcceleratorKeyProperty.Id] = new GetProperty(GetAcceleratorKey);
            s_propertyInfo[AutomationElementIdentifiers.AccessKeyProperty.Id] = new GetProperty(GetAccessKey);
            s_propertyInfo[AutomationElementIdentifiers.HasKeyboardFocusProperty.Id] = new GetProperty(HasKeyboardFocus);
            s_propertyInfo[AutomationElementIdentifiers.IsKeyboardFocusableProperty.Id] = new GetProperty(IsKeyboardFocusable);
            s_propertyInfo[AutomationElementIdentifiers.IsEnabledProperty.Id] = new GetProperty(IsEnabled);
            s_propertyInfo[AutomationElementIdentifiers.BoundingRectangleProperty.Id] = new GetProperty(GetBoundingRectangle);
            s_propertyInfo[AutomationElementIdentifiers.ProcessIdProperty.Id] = new GetProperty(GetCurrentProcessId);
            s_propertyInfo[AutomationElementIdentifiers.RuntimeIdProperty.Id] = new GetProperty(GetRuntimeId);
            s_propertyInfo[AutomationElementIdentifiers.ClassNameProperty.Id] = new GetProperty(GetClassName);
            s_propertyInfo[AutomationElementIdentifiers.HelpTextProperty.Id] = new GetProperty(GetHelpText);
            s_propertyInfo[AutomationElementIdentifiers.ClickablePointProperty.Id] = new GetProperty(GetClickablePoint);
            s_propertyInfo[AutomationElementIdentifiers.CultureProperty.Id] = new GetProperty(GetCultureInfo);
            s_propertyInfo[AutomationElementIdentifiers.IsOffscreenProperty.Id] = new GetProperty(IsOffscreen);
            s_propertyInfo[AutomationElementIdentifiers.OrientationProperty.Id] = new GetProperty(GetOrientation);
            s_propertyInfo[AutomationElementIdentifiers.FrameworkIdProperty.Id] = new GetProperty(GetFrameworkId);
            s_propertyInfo[AutomationElementIdentifiers.IsRequiredForFormProperty.Id] = new GetProperty(IsRequiredForForm);
            s_propertyInfo[AutomationElementIdentifiers.ItemStatusProperty.Id] = new GetProperty(GetItemStatus);
            if (!AccessibilitySwitches.UseNetFx47CompatibleAccessibilityFeatures && AutomationElementIdentifiers.LiveSettingProperty != null)
            {
                s_propertyInfo[AutomationElementIdentifiers.LiveSettingProperty.Id] = new GetProperty(GetLiveSetting);
            }
            if (!AccessibilitySwitches.UseNetFx472CompatibleAccessibilityFeatures && AutomationElementIdentifiers.ControllerForProperty != null)
            {
                s_propertyInfo[AutomationElementIdentifiers.ControllerForProperty.Id] = new GetProperty(GetControllerFor);
            }
            if (!AccessibilitySwitches.UseNetFx472CompatibleAccessibilityFeatures && AutomationElementIdentifiers.SizeOfSetProperty != null)
            {
                s_propertyInfo[AutomationElementIdentifiers.SizeOfSetProperty.Id] = new GetProperty(GetSizeOfSet);
            }
            if (!AccessibilitySwitches.UseNetFx472CompatibleAccessibilityFeatures && AutomationElementIdentifiers.PositionInSetProperty != null)
            {
                s_propertyInfo[AutomationElementIdentifiers.PositionInSetProperty.Id] = new GetProperty(GetPositionInSet);
            }
            if (AutomationElementIdentifiers.HeadingLevelProperty != null)
            { 
                s_propertyInfo[AutomationElementIdentifiers.HeadingLevelProperty.Id] = new GetProperty(GetHeadingLevel);
            }
            if (AutomationElementIdentifiers.IsDialogProperty != null)
            {
                s_propertyInfo[AutomationElementIdentifiers.IsDialogProperty.Id] = new GetProperty(IsDialog);
            }
        }
 
        private delegate object WrapObject(AutomationPeer peer, object iface);
 
        private class PatternInfo
        {
            internal PatternInfo(int id, WrapObject wrapObject, PatternInterface patternInterface)
            {
                Id = id;
                WrapObject = wrapObject;
                PatternInterface = patternInterface;
            }
 
            internal int Id;
            internal WrapObject WrapObject;
            internal PatternInterface PatternInterface;
        }
 
        private delegate object GetProperty(AutomationPeer peer);
 
        private static object IsControlElement(AutomationPeer peer)         {   return peer.IsControlElement(); }
        private static object GetControlType(AutomationPeer peer)           {   ControlType controlType = peer.GetControlType(); return controlType.Id;  }
        private static object IsContentElement(AutomationPeer peer)         {   return peer.IsContentElement(); }
        private static object GetLabeledBy(AutomationPeer peer)             {   AutomationPeer byPeer = peer.GetLabeledBy(); return ElementProxy.StaticWrap(byPeer, peer);  }
        private static object GetNativeWindowHandle(AutomationPeer peer)    {   return null /* not used? */;    }
        private static object GetAutomationId(AutomationPeer peer)          {   return peer.GetAutomationId();  }
        private static object GetItemType(AutomationPeer peer)              {   return peer.GetItemType();      }
        private static object IsPassword(AutomationPeer peer)               {   return peer.IsPassword();       }
        private static object GetLocalizedControlType(AutomationPeer peer)  {   return peer.GetLocalizedControlType();  }
        private static object GetName(AutomationPeer peer)                  {   return peer.GetName();          }
        private static object GetAcceleratorKey(AutomationPeer peer)        {   return peer.GetAcceleratorKey();    }
        private static object GetAccessKey(AutomationPeer peer)             {   return peer.GetAccessKey();     }
        private static object HasKeyboardFocus(AutomationPeer peer)         {   return peer.HasKeyboardFocus(); }
        private static object IsKeyboardFocusable(AutomationPeer peer)      {   return peer.IsKeyboardFocusable();  }
        private static object IsEnabled(AutomationPeer peer)                {   return peer.IsEnabled();        }
        private static object GetBoundingRectangle(AutomationPeer peer)     {   return peer.GetBoundingRectangle(); }
        private static object GetCurrentProcessId(AutomationPeer peer)      {   return Environment.ProcessId; }
        private static object GetRuntimeId(AutomationPeer peer)             {   return peer.GetRuntimeId();     }
        private static object GetClassName(AutomationPeer peer)             {   return peer.GetClassName();     }
        private static object GetHelpText(AutomationPeer peer)              {   return peer.GetHelpText();  }
        private static object GetClickablePoint(AutomationPeer peer)        {   Point pt = peer.GetClickablePoint(); return new double[] {pt.X, pt.Y};  }
        private static object GetCultureInfo(AutomationPeer peer)           {   return null;    }
        private static object IsOffscreen(AutomationPeer peer)              {   return peer.IsOffscreen();  }
        private static object GetOrientation(AutomationPeer peer)           {   return peer.GetOrientation();   }
        private static object GetFrameworkId(AutomationPeer peer)           {   return peer.GetFrameworkId();   }
        private static object IsRequiredForForm(AutomationPeer peer)        {   return peer.IsRequiredForForm();    }
        private static object GetItemStatus(AutomationPeer peer)            {   return peer.GetItemStatus();    }
        private static object GetLiveSetting(AutomationPeer peer)           {   return peer.GetLiveSetting(); }
        private static object GetControllerFor(AutomationPeer peer)         {   return peer.GetControllerForProviderArray(); }
        private static object GetSizeOfSet(AutomationPeer peer)             {   return peer.GetSizeOfSet(); }
        private static object GetPositionInSet(AutomationPeer peer)         {   return peer.GetPositionInSet(); }
        private static object GetHeadingLevel(AutomationPeer peer)          {   return peer.GetHeadingLevel(); }
        private static object IsDialog(AutomationPeer peer)                 {   return peer.IsDialog(); }
 
        private static Hashtable s_patternInfo;
        private static Hashtable s_propertyInfo;
 
        private int _index = -1;
        private IntPtr _hwnd;
        private List<AutomationPeer> _children;
        private AutomationPeer _parent;
 
        private object _eventsSourceOrPeerRecord;
 
        private Rect _boundingRectangle;
        private string _itemStatus;
        private string _name;
        private bool _isOffscreen;
        private bool _isEnabled;
        private bool _invalidated;
        private bool _ancestorsInvalid;
        private bool _childrenValid;
        private bool _addedToEventList;
        private bool _publicCallInProgress;
        private bool _publicSetFocusInProgress;
        private bool _isInteropPeer;
        private bool _hasIterationParent;
        private WeakReference _elementProxyWeakReference = null;
 
        private static DispatcherOperationCallback _updatePeer = new DispatcherOperationCallback(UpdatePeer);
 
        private class PeerRecord
        {
            private AutomationPeer _eventsSource;
            public AutomationPeer EventsSource
            {
                 get { return _eventsSource; }
                 set { _eventsSource = value; }
            }
 
            private AutomationPeer _iterationParent;
            public AutomationPeer IterationParent
            {
                get { return _iterationParent; }
                set { _iterationParent = value; }
            }
        }
    }
}