FocusTracker.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / AccessibleTech / longhorn / Automation / UIAutomationClient / MS / Internal / Automation / FocusTracker.cs / 1 / FocusTracker.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// 
// Description: Class that tracks Win32 focus changes 
//
// History: 
//  06/17/2003 : [....] Ported to WCP
//
//---------------------------------------------------------------------------
 
using System;
using System.Windows.Automation; 
using System.Windows.Automation.Provider; 
using System.Diagnostics;
using MS.Win32; 

namespace MS.Internal.Automation
{
    // Class that tracks Win32 focus changes 
    internal class FocusTracker : WinEventWrap
    { 
        //----------------------------------------------------- 
        //
        //  Constructors 
        //
        //-----------------------------------------------------

        #region Constructors 

        // Ctor - provide the WinEvent identifiers used to track focus changes 
        internal FocusTracker() 
            : base(_eventIds)
        { 
            // Intentionally not setting the callback for the base WinEventWrap since the WinEventProc override
            // in this class calls RaiseEventInThisClientOnly to actually raise the event to the client.
        }
 
        #endregion Constructors
 
 
        //------------------------------------------------------
        // 
        //  Internal Methods
        //
        //-----------------------------------------------------
 
        #region Internal Methods
 
        // WinEventProc - override to process WinEvents 
        internal override void WinEventProc(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime)
        { 
            if (hwnd != IntPtr.Zero)
            {
                switch(eventId)
                { 
                    case NativeMethods.EVENT_OBJECT_FOCUS:          OnEventObjectFocus(eventId, hwnd, idObject, idChild, eventTime); break;
                    case NativeMethods.EVENT_SYSTEM_MENUSTART:      OnEventSystemMenuStart(eventId, hwnd, idObject, idChild, eventTime); break; 
                    case NativeMethods.EVENT_SYSTEM_MENUEND:        OnEventSystemMenuEnd(eventId, hwnd, idObject, idChild, eventTime); break; 
                    case NativeMethods.EVENT_SYSTEM_SWITCHSTART:    OnEventSystemMenuStart(eventId, hwnd, idObject, idChild, eventTime); break;
                    case NativeMethods.EVENT_SYSTEM_SWITCHEND:      OnEventSystemMenuEnd(eventId, hwnd, idObject, idChild, eventTime); break; 
                    case NativeMethods.EVENT_OBJECT_DESTROY:        OnEventObjectDestroy(eventId, hwnd, idObject, idChild, eventTime); break;
                    case NativeMethods.EVENT_SYSTEM_MENUPOPUPSTART: OnEventSystemMenuPopupStart(eventId, hwnd, idObject, idChild, eventTime); break;
                    case NativeMethods.EVENT_SYSTEM_CAPTURESTART:   OnEventSystemCaptureStart(eventId, hwnd, idObject, idChild, eventTime); break;
                    case NativeMethods.EVENT_SYSTEM_CAPTUREEND:     OnEventSystemCaptureEnd(eventId, hwnd, idObject, idChild, eventTime); break; 
                }
            } 
        } 

        #endregion Internal Methods 


        //------------------------------------------------------
        // 
        //  Private Methods
        // 
        //------------------------------------------------------ 

        #region Private Methods 

        // HandleFocusChange - Called when a WinEvent we're listening to indicates the
        // focus has changed.  This is where the callback to the client is queued.
        private void HandleFocusChange(IntPtr hwnd, Accessible acc, int idObject, int idChild, uint eventTime) 
        {
            // If there is an hwnd then notify of a focus change 
            if (hwnd != IntPtr.Zero) 
            {
                Debug.Assert(acc != null, "HandleFocusChange got hwnd and null IAccessible"); 

                // Create an event args and get the source logical element
                AutomationFocusChangedEventArgs e = new InternalAutomationFocusChangedEventArgs(idObject, idChild, eventTime);
                AutomationElement srcEl = GetFocusedElementFromWinEvent(hwnd, idObject, idChild); 
                if (srcEl == null)
                { 
                    // Don't raise focus change events for UI that is gone.  This has been seen when toolbar menus are 
                    // being manipulated (e.g. mnu.SubMenu("File").MenuItems("Close").Click() in MITA).  We should be
                    // seeing another focus change soon and with that event we can re-establish focus. 
                    return;
                }

                // Check that item is actually focused 
                // Don't do this for statics - the controls in the color picker are statics, and
                // they get focus, but OLEACC assumes statics don't get focus, so never sets the 
                // focus bit. So, for now, assume statics that send focus do actually have focus. 
                if (!Accessible.IsStatic(hwnd) && !Accessible.IsComboDropdown(hwnd))
                { 
                    // instead of depending on oleacc to see if something has focus ask provider
                    if (!(bool)srcEl.GetCurrentPropertyValue(AutomationElement.HasKeyboardFocusProperty))
                    {
                        return; 
                    }
                } 
 
                // Do notifies
                ClientEventManager.RaiseEventInThisClientOnly(AutomationElement.AutomationFocusChangedEvent, srcEl, e); 
            }

            // Keep track of where we are right now (may be unknown/null)
            _accCurrent = acc; 
        }
 
 
        // We need to treat MSAA's FOCUS winevents differently depending on the OBJID -
        // OBJID_CLIENT gets routed to the proxies; _MENU and _SYSMENU get speical treatment. 
        private AutomationElement GetFocusedElementFromWinEvent(IntPtr hwnd, int idObject, int idChild)
        {
            try
            { 
                IRawElementProviderSimple provider = null;
                // These are the only object types that oleacc proxies allow to take focus. 
                // (Native IAccessibles can send focus for other custom OBJID valus, but those are no use 
                // to us.)
                // Try and get providers for them ourself - if we don't get anything, then 
                // defer to core to get the element for the HWND itself.
                if (idObject == UnsafeNativeMethods.OBJID_CLIENT)
                {
                    // regular focus - pass it off to a proxy... 
                    provider = ProxyManager.ProxyProviderFromHwnd(NativeMethods.HWND.Cast(hwnd), idChild, UnsafeNativeMethods.OBJID_CLIENT);
                } 
                else if (idObject == UnsafeNativeMethods.OBJID_MENU) 
                {
                    // menubar focus - see if there's a menubar pseudo-proxy registered... 
                    ClientSideProviderFactoryCallback factory = ProxyManager.NonClientMenuBarProxyFactory;
                    if (factory != null)
                    {
                        provider = factory(hwnd, idChild, idObject); 
                    }
                } 
                else if (idObject == UnsafeNativeMethods.OBJID_SYSMENU) 
                {
                    // system menu box focus - see if there's a sysmenu pseudo-proxy registered... 
                    ClientSideProviderFactoryCallback factory = ProxyManager.NonClientSysMenuProxyFactory;
                    if (factory != null)
                    {
                        provider = factory(hwnd, idChild, idObject); 
                    }
                } 
                else if (idObject <= 0) 
                {
                    return null; 
                }
                else
                {
                    // This covers OBJID_CLIENT and custom OBJID cases. 
                    // Pass it to the proxy manager: most proxies will just handle OBJID_CLIENT,
                    // but the MSAA proxy can potentally handle other OBJID values. 
                    provider = ProxyManager.ProxyProviderFromHwnd(NativeMethods.HWND.Cast(hwnd), idChild, idObject); 
                }
 
                if(provider != null)
                {
                    // Ask the fragment root if any of its children really have the focus
                    IRawElementProviderFragmentRoot fragment = provider as IRawElementProviderFragmentRoot; 
                    if (fragment != null)
                    { 
                        // if we get back something that is different than what we started with and its not null 
                        // use that instead.  This is here to get the subset link in the listview but could be usefull
                        // for listview subitems as well. 
                        IRawElementProviderSimple realFocus = fragment.GetFocus();
                        if(realFocus != null && !Object.ReferenceEquals(realFocus, provider))
                        {
                            provider = realFocus; 
                        }
                    } 
 
                    SafeNodeHandle hnode = UiaCoreApi.UiaNodeFromProvider(provider);
                    return AutomationElement.Wrap(hnode); 
                }
                else
                {
                    // Didn't find a proxy to handle this hwnd - pass off to core... 
                    return AutomationElement.FromHandle(hwnd);
                } 
            } 
            catch( Exception e )
            { 
                if( Misc.IsCriticalException( e ) )
                    throw;

                return null; 
            }
        } 
 
        // OnEventObjectFocus - process an EventObjectFocus WinEvent.
        private void OnEventObjectFocus(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime) 
        {
            Accessible acc = Accessible.Create(hwnd, idObject, idChild);
            if (acc == null)
            { 
                return;
            } 
 
            // Keep track of last focused non-menu item, so we can restore focus when we leave menu mode
            if (!_fInMenu) 
            {
                _accLastBeforeMenu = acc;
                _hwndLastBeforeMenu = hwnd;
                _idLastObject = idObject; 
                _idLastChild = idChild;
            } 
 
            HandleFocusChange(hwnd, acc, idObject, idChild, eventTime);
        } 

        // OnEventSystemMenuStart - process an EventSystemMenuStart WinEvent.
        private void OnEventSystemMenuStart(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime)
        { 
            // No immediate effect on focus - we expect to get a FOCUS event after this.
            _fInMenu = true; 
        } 

        // OnEventSystemMenuEnd - process an EventSystemMenuEnd WinEvent. 
        private void OnEventSystemMenuEnd(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime)
        {
            // Restore focus to where it was before the menu appeared
            if (_fInMenu) 
            {
                _fInMenu = false; 
 
                if (_accLastBeforeMenu != null)
                { 
                    HandleFocusChange(_hwndLastBeforeMenu, _accLastBeforeMenu, _idLastObject, _idLastChild, eventTime);
                }
            }
        } 

        // OnEventObjectDestroy - process an EventObjectDestroy WinEvent. 
        private void OnEventObjectDestroy(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime) 
        {
            // Check if still alive. Ignore caret destroys - we're only interesed in 'real' objects here... 
            if (idObject != UnsafeNativeMethods.OBJID_CARET && _accCurrent != null)
            {
                bool fDead = false;
 
                try
                { 
                    int dwState = _accCurrent.State; 
                    IntPtr hwndCur = _accCurrent.Window;
                    if (hwndCur == IntPtr.Zero || !SafeNativeMethods.IsWindow(NativeMethods.HWND.Cast(hwndCur))) 
                    {
                        fDead = true;
                    }
                } 
                catch( Exception e )
                { 
                    if( Misc.IsCriticalException( e ) ) 
                        throw;
 
                    fDead = true;
                }

                if (fDead) 
                {
                    // It's dead... 
                    HandleFocusChange(IntPtr.Zero, null, 0, 0, eventTime); 
                }
            } 
        }

        // OnEventSystemMenuPopupStart - process an EventSystemMenuPopupStart WinEvent.
        private void OnEventSystemMenuPopupStart(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime) 
        {
            Accessible acc = Accessible.Create(hwnd, idObject, idChild); 
            if( acc == null ) 
                return;
 
            HandleFocusChange(hwnd, acc, idObject, idChild, eventTime);
        }

        // OnEventSystemCaptureStart - process an EventSystemCaptureStart WinEvent. 
        private void OnEventSystemCaptureStart(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime)
        { 
            // Deal only with Combolbox dropdowns... 
            if (Accessible.IsComboDropdown(hwnd))
            { 
                // Need to get id of focused item...
                try
                {
                    IntPtr i = Misc.SendMessageTimeout(NativeMethods.HWND.Cast(hwnd), UnsafeNativeMethods.LB_GETCURSEL, IntPtr.Zero, IntPtr.Zero); 
                    Accessible acc = Accessible.Create(hwnd, UnsafeNativeMethods.OBJID_CLIENT, i.ToInt32() + 1);
                    if (acc == null) 
                        return; 

                    HandleFocusChange(hwnd, acc, idObject, idChild, eventTime); 
                }
                catch (TimeoutException)
                {
                    // Ignore 
                }
            } 
        } 

        // OnEventSystemCaptureEnd - process an EventSystemCaptureEnd WinEvent. 
        private void OnEventSystemCaptureEnd(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime)
        {
            // Deal only with Combolbox dropdowns...
            if (Accessible.IsComboDropdown(hwnd)) 
            {
                SafeNativeMethods.GUITHREADINFO guiThreadInfo = new SafeNativeMethods.GUITHREADINFO(); 
 
                if (!Misc.GetGUIThreadInfo(0, ref guiThreadInfo))
                { 
                    return;
                }

                Accessible acc = Accessible.Create(guiThreadInfo.hwndFocus, UnsafeNativeMethods.OBJID_CLIENT, 0); 
                if (acc == null)
                    return; 
 
                HandleFocusChange(hwnd, acc, idObject, idChild, eventTime);
            } 
        }

        #endregion Private Methods
 

        //----------------------------------------------------- 
        // 
        //  Private Fields
        // 
        //------------------------------------------------------

        #region Private Fields
 
        private static int [] _eventIds = new int [] {
                NativeMethods.EVENT_OBJECT_FOCUS, 
                NativeMethods.EVENT_SYSTEM_MENUSTART, 
                NativeMethods.EVENT_SYSTEM_MENUPOPUPSTART,
                NativeMethods.EVENT_SYSTEM_MENUEND, 
                NativeMethods.EVENT_OBJECT_DESTROY,
                NativeMethods.EVENT_SYSTEM_CAPTURESTART,
                NativeMethods.EVENT_SYSTEM_CAPTUREEND,
                NativeMethods.EVENT_SYSTEM_SWITCHSTART, 
                NativeMethods.EVENT_SYSTEM_SWITCHEND
            }; 
 
        private Accessible _accCurrent;            // the IAccessible currently being handled
        private Accessible _accLastBeforeMenu;     // the last IAccessible before a menu got focus 
        private IntPtr     _hwndLastBeforeMenu;    // the last hwnd before a menu got focus
        private int        _idLastObject;          // the last idObject before a menu got focus
        private int        _idLastChild;           // the last idChild before a menu got focus
        private bool       _fInMenu;               // true if there's a menu up 

        #endregion Private Fields 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK