AccessKeyManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Input / AccessKeyManager.cs / 1305600 / AccessKeyManager.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections; 
using System.Collections.Generic;
using System.Globalization; 
using System.Threading;
using System.Windows.Media;
using System.Windows.Interop;
using MS.Internal; 
using System.Diagnostics;
using System.Windows; 
using System.Security; 
using System.Security.Permissions;
 
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;

namespace System.Windows.Input 
{
    ///  
    ///   AccessKeyManager object is created on demand and it is one per thread. 
    /// It attached an event handler for PostProcessInput on InputManager and expose registration and
    /// unregistration of access keys. When the access key is pressed in calls OnAccessKey method on the target element 
    /// 
    public sealed class AccessKeyManager
    {
        #region Public API 
        /// 
        ///   Register the access key binding to the element that is the accesskey. 
        ///  
        /// When the key is pressed the element OnAccessKey method is called
        /// The registration element 
        public static void Register(string key, IInputElement element)
        {
            if (element == null)
            { 
                throw new ArgumentNullException("element");
            } 
            key = NormalizeKey(key); 

            AccessKeyManager akm = AccessKeyManager.Current; 

            lock (akm._keyToElements)
            {
                ArrayList elements = (ArrayList)akm._keyToElements[key]; 

                if (elements == null) 
                { 
                    elements = new ArrayList(1);
                    akm._keyToElements[key] = elements; 
                }
                else
                {
                    // There were some elements there, remove dead ones 
                    PurgeDead(elements, null);
                } 
 
                elements.Add(new WeakReference(element));
            } 
        }

        /// 
        /// Unregister one key bound to a particular element 
        /// 
        ///  
        ///  
        public static void Unregister(string key, IInputElement element)
        { 
            if (element == null)
            {
                throw new ArgumentNullException("element");
            } 
            key = NormalizeKey(key);
 
            AccessKeyManager akm = AccessKeyManager.Current; 

            lock (akm._keyToElements) 
            {
                // Get all elements bound to this key and remove this element
                ArrayList elements = (ArrayList)akm._keyToElements[key];
 
                if (elements != null)
                { 
                    PurgeDead(elements, element); 
                    if (elements.Count == 0)
                    { 
                        akm._keyToElements.Remove(key);
                    }
                }
            } 
        }
 
        ///  
        /// Tells if there is a particular key registered at the global scope in this Context.
        ///  
        /// Scope to query (for example the PresentationSource of the visual)
        /// 
        /// 
        public static bool IsKeyRegistered(object scope, string key) 
        {
            key = NormalizeKey(key); 
 
            AccessKeyManager akm = AccessKeyManager.Current;
            List targets = akm.GetTargetsForScope(scope, key, null, AccessKeyInformation.Empty); 
            return (targets != null && targets.Count > 0);
        }

        ///  
        /// Process the given key as if the key were pressed at the global scope in this context.
        ///  
        /// scope in which to invoke the access key 
        /// character being pressed
        /// True if this key has multiple matches 
        /// false if there are no more keys that match, true otherwise
        /// 
        ///     Executes the command on the given command source.
        ///  
        /// 
        ///     Critical - calls critical function (ProcessKeyForScope) 
        ///     PublicOK - always passes in false for userInitiated, which is safe 
        /// 
        [SecurityCritical] 
        public static bool ProcessKey(object scope, string key, bool isMultiple)
        {
            key = NormalizeKey(key);
 
            AccessKeyManager akm = AccessKeyManager.Current;
            return (akm.ProcessKeyForScope(scope, key, isMultiple,false) == ProcessKeyResult.MoreMatches); 
        } 

        ///  
        /// Returns StringInfo.GetNextTextElement(key).ToUpperInvariant() throwing exceptions for null
        /// and multi-char strings.
        /// 
        ///  
        /// 
        private static string NormalizeKey(string key) 
        { 
            if (key == null)
            { 
                throw new ArgumentNullException("key");
            }

            string firstCharacter = StringInfo.GetNextTextElement(key); 

            if (key != firstCharacter) 
            { 
                throw new ArgumentException(SR.Get(SRID.AccessKeyManager_NotAUnicodeCharacter, "key"));
            } 

            return firstCharacter.ToUpperInvariant();
        }
 
        /// 
        /// This event is used by elements that want to define a scope for accesskeys, such as Menu and Popup. 
        /// This event will never be raised, it is used to identify classes that define new scopes. 
        /// 
        public static readonly RoutedEvent AccessKeyPressedEvent = EventManager.RegisterRoutedEvent( 
            "AccessKeyPressed", RoutingStrategy.Bubble, typeof(AccessKeyPressedEventHandler), typeof(AccessKeyManager));

        /// 
        ///     Adds a handler for the AccessKeyPressed attached event 
        /// 
        /// UIElement or ContentElement that listens to this event 
        /// Event Handler to be added 
        public static void AddAccessKeyPressedHandler(DependencyObject element, AccessKeyPressedEventHandler handler)
        { 
            UIElement.AddHandler(element, AccessKeyPressedEvent, handler);
        }

        ///  
        ///     Removes a handler for the AccessKeyPressed attached event
        ///  
        /// UIElement or ContentElement that listens to this event 
        /// Event Handler to be removed
        public static void RemoveAccessKeyPressedHandler(DependencyObject element, AccessKeyPressedEventHandler handler) 
        {
            UIElement.RemoveHandler(element, AccessKeyPressedEvent, handler);
        }
 
        #endregion
 
        #region Constructor 
        /// 
        ///     Critical: This code accesses InputManager.Current which is critical 
        ///     TreatAsSafe: This code does not expose it and simply adds an event handler that is internal
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private AccessKeyManager() 
        {
            InputManager.Current.PostProcessInput += new ProcessInputEventHandler(PostProcessInput); 
        } 

        #endregion 

        #region Properties
        /// 
        /// Access to the current context's AccessKeyManager class 
        /// 
        private static AccessKeyManager Current 
        { 
            get
            { 
                if (_accessKeyManager == null)
                    _accessKeyManager = new AccessKeyManager();
                return _accessKeyManager;
            } 
        }
        #endregion 
 
        #region PostProcessInput Event Handlers
 
        private enum ProcessKeyResult
        {
            NoMatch,
            MoreMatches, 
            LastMatch
        } 
 
        /// 
        ///     Critical: accesses e.StagingItem.Input 
        /// 
        [SecurityCritical]
        private void PostProcessInput(object sender, ProcessInputEventArgs e)
        { 
            if (e.StagingItem.Input.Handled) return;
 
            if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyDownEvent) 
            {
                OnKeyDown((KeyEventArgs)e.StagingItem.Input); 
            }
            else if (e.StagingItem.Input.RoutedEvent == TextCompositionManager.TextInputEvent)
            {
                OnText((TextCompositionEventArgs)e.StagingItem.Input); 
            }
 
        } 

        // Assumes key is already a single unicode character 
        /// 
        /// Critical - sets the userInitiated bit on a command, which is used
        ///            for security purposes later.
        ///  
        [SecurityCritical]
        private ProcessKeyResult ProcessKeyForSender(object sender, string key, bool existsElsewhere, bool userInitiated) 
        { 
            // This comes from OnKeyDown or OnText and though it is a single character it might not be uppercased.
            key = key.ToUpperInvariant(); 

            IInputElement inputElementSender = sender as IInputElement;
            List targets = GetTargetsForSender(inputElementSender, key);
 
            return ProcessKey(targets, key, existsElsewhere, userInitiated);
        } 
 
        // Assumes key is already a single unicode character AND is uppercased
        ///  
        /// Critical - sets the userInitiated bit on a command, which is used
        ///            for security purposes later.
        /// 
        [SecurityCritical] 
        private ProcessKeyResult ProcessKeyForScope(object scope, string key, bool existsElsewhere, bool userInitiated)
        { 
            List targets = GetTargetsForScope(scope, key, null, AccessKeyInformation.Empty); 

            return ProcessKey(targets, key, existsElsewhere, userInitiated); 
        }

        /// 
        /// Critical - Sets calls AccessKeyPressedEventArgs setting the userInitiated bit which is used 
        ///            for security purposes later.
        ///  
        [SecurityCritical] 
        private ProcessKeyResult ProcessKey(List targets, string key, bool existsElsewhere, bool userInitiated)
        { 
            if (targets != null)
            {
                bool oneUIElement = true;
                UIElement invokeUIElement = null; 
                bool lastWasFocused = false;
 
                int chosenIndex = 0; 
                for (int i = 0; i < targets.Count; i++)
                { 
                    UIElement target = targets[i] as UIElement;
                    Debug.Assert(target != null, "Targets should only be UIElements");
                    if (!target.IsEnabled)
                        continue; 

                    if (invokeUIElement == null) 
                    { 
                        invokeUIElement = target;
                        chosenIndex = i; 
                    }
                    else
                    {
                        if (lastWasFocused) 
                        {
                            invokeUIElement = target; 
                            chosenIndex = i; 
                        }
 
                        oneUIElement = false;
                    }

                    // 
                    lastWasFocused = target.IsKeyboardFocused;
                } 
 
                if (invokeUIElement != null)
                { 
                    AccessKeyEventArgs args = new AccessKeyEventArgs(key, !oneUIElement || existsElsewhere /* == isMultiple */,userInitiated);
                    try
                    {
                        invokeUIElement.InvokeAccessKey(args); 
                    }
                    finally 
                    { 
                        args.ClearUserInitiated();
                    } 

                    return (chosenIndex == targets.Count - 1) ? ProcessKeyResult.LastMatch : ProcessKeyResult.MoreMatches;
                }
            } 

            return ProcessKeyResult.NoMatch; 
        } 

        ///  
        /// Critical - Calls ProcessKeyForSender, setting the userInitiated
        ///             bit, which is used for security purposes later.
        /// 
        [SecurityCritical] 
        private void OnText(TextCompositionEventArgs e)
        { 
            // AccessKeyManager handles both text and system text. 
            string text = e.Text;
            if ((text == null) || (text.Length == 0)) 
            {
                text = e.SystemText;
            }
 
            if ((text != null) && (text.Length > 0))
            { 
                if (ProcessKeyForSender(e.OriginalSource, text, false /* existsElsewhere */,e.UserInitiated) != ProcessKeyResult.NoMatch) 
                {
                    e.Handled = true; 
                }
            }
        }
 
        /// 
        /// Critical - Calls ProcessKeyForSender, setting the userInitiated 
        ///             bit, which is used for security purposes later. 
        /// 
        [SecurityCritical] 
        private void OnKeyDown(KeyEventArgs e)
        {
            KeyboardDevice keyboard = (KeyboardDevice)e.Device;
 
            string text = null;
            switch (e.RealKey) 
            { 
                case Key.Enter :
                     text = "\x000D"; 
                     break;

                case Key.Escape :
                     text = "\x001B"; 
                     break;
            } 
 
            if (text != null)
            { 
                if (ProcessKeyForSender(e.OriginalSource, text, false /* existsElsewhere */,e.UserInitiated) != ProcessKeyResult.NoMatch)
                {
                    e.Handled = true;
                } 
            }
        } 
 
        /// 
        /// Get the list of access key targets for the sender of the keyboard event.  If sender is null, 
        /// pretend key was pressed in the active window.
        /// 
        /// 
        ///  
        /// 
        ///  
        ///     Critical: calls Critical member GetInfoForElement() and accesses Scope from AccessKeyInformation. 
        ///     TreatAsSafe:  Does not pass the sender info (which may contain a PresentationSource) out.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private List GetTargetsForSender(IInputElement sender, string key)
        {
            // Find the scope for the sender -- will be matched against the possible targets' scopes 
            AccessKeyInformation senderInfo = GetInfoForElement(sender, key);
 
            return GetTargetsForScope(senderInfo.Scope, key, sender, senderInfo); 
        }
 
        /// 
        ///     Critical: calls CriticalGetActiveSource()
        ///     TreatAsSafe:  Does not pass the returned scope to any other method, nor is it
        ///                 returned from GetTargetsForScope.  Also returned value is not critical. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private List GetTargetsForScope(object scope, string key, IInputElement sender, AccessKeyInformation senderInfo) 
        {
            // null scope defaults to the active window 
            if (scope == null)
            {
                scope = CriticalGetActiveSource();
 
                // if there is no active scope then give up
                if (scope == null) 
                { 
                    return null;
                } 
            }

            //Scoping:
            //    1) When key is pressed, find matching AKs -> S 
            //    3) find scope for keyevent.Source
            //    4) find scope for everything in S. throw away those that don't match. 
            //    5) Final selection uses S.  yay! 
            //
            // 
            List possibleElements;
            lock (_keyToElements)
            {
                possibleElements = CopyAndPurgeDead(_keyToElements[key] as ArrayList); 
            }
 
            if (possibleElements == null) return null; 

            List finalTargets = new List(1); 

            // Go through all the possible elements, find the interesting candidates
            for (int i = 0; i < possibleElements.Count;  i++)
            { 
                IInputElement element = possibleElements[i];
                if (element != sender) 
                { 
                    if (IsTargetable(element))
                    { 
                        AccessKeyInformation elementInfo = GetInfoForElement(element, key);

                        if (elementInfo.target == null) continue;
 
                        if (scope == elementInfo.Scope)
                        { 
                            finalTargets.Add(elementInfo.target); 
                        }
                    } 
                }
                else
                {
                    // This is the same element that sent the event so it must be in the same scope. 
                    // Just add it to the final targets
                    if (senderInfo.target != null) 
                    { 
                        finalTargets.Add(senderInfo.target);
                    } 
                }
            }

            return finalTargets; 
        }
 
        ///  
        /// Returns scope for the given element.
        ///  
        /// 
        /// 
        /// Scope for the given element, null means the context global scope
        ///  
        ///     Critical: calls GetSourceForElement(), CriticalGetActiveSource(), and returns AccessKeyInformation.
        ///  
        [SecurityCritical] 
        private AccessKeyInformation GetInfoForElement(IInputElement element, string key)
        { 
            AccessKeyInformation info = new AccessKeyInformation();
            if (element != null)
            {
                AccessKeyPressedEventArgs args = new AccessKeyPressedEventArgs(key); 

                element.RaiseEvent(args); 
                info.Scope = args.Scope; 
                info.target = args.Target;
                if (info.Scope == null) 
                {
                    info.Scope = GetSourceForElement(element);
                }
            } 
            else
            { 
                info.Scope = CriticalGetActiveSource(); 
            }
            return info; 
        }

        /// 
        ///     Critical: calls PresentationSource.CriticalFromVisual, and returns PresentationSource 
        /// 
        [SecurityCritical] 
        private PresentationSource GetSourceForElement(IInputElement element) 
        {
            PresentationSource source = null; 
            DependencyObject elementDO = element as DependencyObject;

            // Use internal helpers to try to find the source of the element.
            // Because IInputElements can move around without notification we need to 
            // look up the source every time.
            if (elementDO != null) 
            { 
                DependencyObject containingVisual = InputElement.GetContainingVisual(elementDO);
 
                if (containingVisual != null)
                {
                    source = PresentationSource.CriticalFromVisual(containingVisual);
                } 
            }
 
            // NOTE: source can be null but IsTargetable(element) == true if the 
            // element is in an orphaned tree but the tree has not yet been garbage collected.
            return source; 
        }

        /// 
        ///     Critical: calls UnsafeNativeMethod GetActiveWindow(), and Critical method 
        ///               HwndSource.FromHwnd().
        ///     TreatAsSafe: Does not pass any parameters to GetActiveWindow(), and does 
        ///                 not expose the return value, and HwndSource.FromHwnd will demand 
        ///                 UIPermissionWindow.AllWindows.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private PresentationSource GetActiveSource()
        {
            IntPtr hwnd = MS.Win32.UnsafeNativeMethods.GetActiveWindow(); 
            if (hwnd != IntPtr.Zero)
                return HwndSource.FromHwnd(hwnd); 
 
            return null;
        } 

        /// 
        ///     Critical: calls UnsafeNativeMethod GetActiveWindow() and HwndSource.CriticalFromHwnd()
        ///  
        [SecurityCritical]
        private PresentationSource CriticalGetActiveSource() 
        { 
            IntPtr hwnd = MS.Win32.UnsafeNativeMethods.GetActiveWindow();
            if (hwnd != IntPtr.Zero) 
                return HwndSource.CriticalFromHwnd(hwnd);

            return null;
        } 

 
        private bool IsTargetable(IInputElement element) 
        {
            DependencyObject uielement = InputElement.GetContainingUIElement((DependencyObject)element); 

            // For an element to be a valid target it must be visible and enabled
            if (uielement != null
                && IsVisible(uielement) 
                && IsEnabled(uielement))
            { 
                return true; 
            }
 
            return false;
        }

        private static bool IsVisible(DependencyObject element) 
        {
            while (element != null) 
            { 
                Visibility visibility;
                UIElement uiElem = element as UIElement; 
                UIElement3D uiElem3D = element as UIElement3D;

                if (uiElem != null)

                { 
                    visibility = uiElem.Visibility;
                } 
                else 
                {
                    visibility = uiElem3D.Visibility; 
                }

                if (visibility != Visibility.Visible)
                { 
                    return false;
                } 
 
                element = UIElementHelper.GetUIParent(element);
            } 

            return true;
        }
 
        // returns whether the given DO is enabled or not
        private static bool IsEnabled(DependencyObject element) 
        { 
            return ((bool)element.GetValue(UIElement.IsEnabledProperty));
        } 

        private struct AccessKeyInformation
        {
            ///  
            ///     Critical: Scope may contain an PresentationSource which we
            ///               would not want exposed. 
            ///  
            public object Scope
            { 
                [SecurityCritical]
                get
                {
                    return _scope; 
                }
                set 
                { 
                    _scope = value;
                } 
            }


            public UIElement target; 

            private static AccessKeyInformation _empty = new AccessKeyInformation(); 
            public static AccessKeyInformation Empty 
            {
                get 
                {
                    return _empty;
                }
            } 

            private object _scope; 
        } 

        private static void PurgeDead(ArrayList elements, object elementToRemove) 
        {
            for (int i = 0; i < elements.Count; )
            {
                WeakReference weakReference = (WeakReference)elements[i]; 
                object element = weakReference.Target;
 
                if (element == null || element == elementToRemove) 
                {
                    elements.RemoveAt(i); 
                }
                else
                {
                    i++; 
                }
            } 
        } 

        ///  
        ///     Takes an ArrayList of WeakReferences, removes the dead references and returns
        ///     a generic List of IInputElements (strong references)
        /// 
        private static List CopyAndPurgeDead(ArrayList elements) 
        {
            if (elements == null) 
            { 
                return null;
            } 

            List copy = new List(elements.Count);

            for (int i = 0; i < elements.Count; ) 
            {
                WeakReference weakReference = (WeakReference)elements[i]; 
                object element = weakReference.Target; 

                if (element == null) 
                {
                    elements.RemoveAt(i);
                }
                else 
                {
                    Debug.Assert(element is IInputElement, "Element in AccessKeyManager store was not of type IInputElement"); 
                    copy.Add((IInputElement)element); 
                    i++;
                } 
            }

            return copy;
        } 

        #endregion 
 
        #region Private Properties
 
        /////////////////////////////////////////////////////////////////////////////////
        // Overview: Algorithm to look up access key from the element for which it is a target.
        //
        //     When the AccessKeyCharacter for an element is requested we see if there 
        //     is a corresponding AccessKeyElement stashed on the element.  If there is,
        //     raise the AccessKeyPressed event on it to see if that element is  still the 
        //     target for it.  If not, go through all registered accesskeys and get their 
        //     targets until we find the desired element.  The "primary" access key character
        //     is the first one we find. 
        //
        //     Note: The algorithm ends up being O(n) for each request for AccessKeyCharacter
        //     because there is no mapping from AccessKeyElement to its "primary" character.
        //     Maintaining this would require storing a hash from AccessKeyElement to character 
        //     or requiring that each element registered implement an interface or some other
        //     kind of contract.  Because we don't keep track of this or enforce this, to find 
        //     the "primary" character we must go through all registered pairs of 
        //     (character, element) to find the character -- O(n).
        // 
        //     This ends up being just fine, because in any given context there shouldn't be
        //     so many elements registered that this cost is at all noticable.
        //
        ///////////////////////////////////////////////////////////////////////////////// 

        ///  
        ///     The primary access key element for an element.  This is stored as a WeakReference. 
        /// 
        private static readonly DependencyProperty AccessKeyElementProperty = 
            DependencyProperty.RegisterAttached("AccessKeyElement", typeof(WeakReference), typeof(AccessKeyManager));

        #endregion
 
        #region Private Methods for UIAutomation
 
        internal static string InternalGetAccessKeyCharacter(DependencyObject d) 
        {
            return Current.GetAccessKeyCharacter(d); 
        }

        private string GetAccessKeyCharacter(DependencyObject d)
        { 
            // See what the local value for AccessKeyElement is first and start with that.
            WeakReference cachedElementWeakRef = (WeakReference)d.GetValue(AccessKeyElementProperty); 
            IInputElement accessKeyElement = (cachedElementWeakRef != null) ? (IInputElement)cachedElementWeakRef.Target : null; 

            if (accessKeyElement != null) 
            {
                // First figure out if the target of accessKeyElement is still "d", then go find
                // the "primary" character for the accessKeyElement.
 
                AccessKeyPressedEventArgs accessKeyPressedEventArgs = new AccessKeyPressedEventArgs();
                accessKeyElement.RaiseEvent(accessKeyPressedEventArgs); 
                if (accessKeyPressedEventArgs.Target == d) 
                {
                    // Because there is no way to get at the access key element's character from the 
                    // element (there is no interface or anything) we have to go through all registered
                    // access keys and see if this access key element is still registered and what its
                    // "primary" character is.
 
                    foreach (DictionaryEntry entry in Current._keyToElements)
                    { 
                        ArrayList elements = (ArrayList)entry.Value; 
                        for (int i = 0; i < elements.Count; i++)
                        { 
                            // If this element matches accessKeyElement, then return the current character
                            WeakReference currentElementWeakRef = (WeakReference)elements[i];

                            if (currentElementWeakRef.Target == accessKeyElement) 
                            {
                                return (string)entry.Key; 
                            } 
                        }
                    } 
                }
            }

 
            // There was no access key stored or it no longer matched.  Clear out the cache and figure it out again.
            d.ClearValue(AccessKeyElementProperty); 
 
            foreach (DictionaryEntry entry in Current._keyToElements)
            { 
                ArrayList elements = (ArrayList)entry.Value;
                for (int i = 0; i < elements.Count; i++)
                {
                    // Determine the target for this element.  Cache the weak reference for the element on the target. 
                    WeakReference currentElementWeakRef = (WeakReference)elements[i];
                    IInputElement currentElement = (IInputElement)currentElementWeakRef.Target; 
 
                    if (currentElement != null)
                    { 
                        AccessKeyPressedEventArgs accessKeyPressedEventArgs = new AccessKeyPressedEventArgs();
                        currentElement.RaiseEvent(accessKeyPressedEventArgs);

                        // If the target was non-null, cache the access key element on the target. 
                        // if the target matches "d", return the current character.
                        if (accessKeyPressedEventArgs.Target != null) 
                        { 
                            accessKeyPressedEventArgs.Target.SetValue(AccessKeyElementProperty, currentElementWeakRef);
 
                            if (accessKeyPressedEventArgs.Target == d)
                            {
                                return (string)entry.Key;
                            } 
                        }
                    } 
                } 
            }
 

            return String.Empty;
        }
 
        #endregion
 
        #region Data 
        // Map: string -> ArrayList of WeakReferences to IInputElements
        private Hashtable _keyToElements = new Hashtable(10); 

        [ThreadStatic] private static AccessKeyManager _accessKeyManager;

        #endregion 
    }
 
    ///  
    /// The delegate type for handling a FindScope event
    ///  
    public delegate void AccessKeyPressedEventHandler(object sender, AccessKeyPressedEventArgs e);

    /// 
    /// The inputs to an AccessKeyPressedEventHandler 
    /// 
    public class AccessKeyPressedEventArgs : RoutedEventArgs 
    { 
        #region Constructors
 
        /// 
        /// The constructor for AccessKeyPressed event args
        /// 
        public AccessKeyPressedEventArgs() 
        {
            RoutedEvent = AccessKeyManager.AccessKeyPressedEvent; 
            _key = null; 
        }
 
        /// 
        /// Constructor for AccessKeyPressed event args
        /// 
        ///  
        public AccessKeyPressedEventArgs(string key) : this()
        { 
            _key = key; 
        }
 
        #endregion

        #region Public Properties
 
        /// 
        /// The scope for the element that raised this event. 
        ///  
        public object Scope
        { 
            get { return _scope; }
            set { _scope = value; }
        }
 
        /// 
        /// Target element for the element that raised this event. 
        ///  
        /// 
        public UIElement Target 
        {
            get { return _target; }
            set { _target = value; }
        } 

        ///  
        /// Key that was pressed 
        /// 
        ///  
        public string Key
        {
            get { return _key; }
        } 

        #endregion 
 
        #region Protected Methods
 
        /// 
        /// 
        /// The handler to invoke.
        /// The current object along the event's route. 
        protected override void InvokeEventHandler(Delegate genericHandler, object genericTarget)
        { 
            AccessKeyPressedEventHandler handler = (AccessKeyPressedEventHandler)genericHandler; 

            handler(genericTarget, this); 
        }

        #endregion
 
        #region Data
 
        private object _scope; 
        private UIElement _target;
        private string _key; 

        #endregion
    }
 
    /// 
    /// Information pertaining to when the access key associated with an element is pressed 
    ///  
    public class AccessKeyEventArgs : EventArgs
    { 
        /// 
        ///
        /// 
        ///  
        /// Critical - sets the userInitiated bit on a command, which is used
        ///            for security purposes later. 
        ///  
        [SecurityCritical]
        internal AccessKeyEventArgs(string key, bool isMultiple, bool userInitiated) 
        {
            _key = key;
            _isMultiple = isMultiple;
            _userInitiated = new SecurityCriticalDataForSet(userInitiated); 
        }
 
        ///  
        /// Critical - sets the userInitiated bit on a command, which is used
        ///            for security purposes later. 
        /// TreatAsSafe: Resets the user initiated bit
        /// 
        [SecurityCritical,SecurityTreatAsSafe]
        internal void ClearUserInitiated() 
        {
            _userInitiated.Value = false; 
        } 
        /// 
        /// The key that was pressed which invoked this access key 
        /// 
        /// 
        public string Key
        { 
            get { return _key; }
        } 
 
        /// 
        /// Were there other elements which are also invoked by this key 
        /// 
        /// 
        public bool IsMultiple
        { 
            get { return _isMultiple; }
        } 
 
        internal bool UserInitiated
        { 
            get { return _userInitiated.Value; }
        }

 
        private string _key;
        private bool _isMultiple; 
        ///  
        /// Critical -  This is critical for set, setting this bool can cause the spoofing of paste
        ///  
        private SecurityCriticalDataForSet_userInitiated;

    }
} 

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

using System; 
using System.Collections; 
using System.Collections.Generic;
using System.Globalization; 
using System.Threading;
using System.Windows.Media;
using System.Windows.Interop;
using MS.Internal; 
using System.Diagnostics;
using System.Windows; 
using System.Security; 
using System.Security.Permissions;
 
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;

namespace System.Windows.Input 
{
    ///  
    ///   AccessKeyManager object is created on demand and it is one per thread. 
    /// It attached an event handler for PostProcessInput on InputManager and expose registration and
    /// unregistration of access keys. When the access key is pressed in calls OnAccessKey method on the target element 
    /// 
    public sealed class AccessKeyManager
    {
        #region Public API 
        /// 
        ///   Register the access key binding to the element that is the accesskey. 
        ///  
        /// When the key is pressed the element OnAccessKey method is called
        /// The registration element 
        public static void Register(string key, IInputElement element)
        {
            if (element == null)
            { 
                throw new ArgumentNullException("element");
            } 
            key = NormalizeKey(key); 

            AccessKeyManager akm = AccessKeyManager.Current; 

            lock (akm._keyToElements)
            {
                ArrayList elements = (ArrayList)akm._keyToElements[key]; 

                if (elements == null) 
                { 
                    elements = new ArrayList(1);
                    akm._keyToElements[key] = elements; 
                }
                else
                {
                    // There were some elements there, remove dead ones 
                    PurgeDead(elements, null);
                } 
 
                elements.Add(new WeakReference(element));
            } 
        }

        /// 
        /// Unregister one key bound to a particular element 
        /// 
        ///  
        ///  
        public static void Unregister(string key, IInputElement element)
        { 
            if (element == null)
            {
                throw new ArgumentNullException("element");
            } 
            key = NormalizeKey(key);
 
            AccessKeyManager akm = AccessKeyManager.Current; 

            lock (akm._keyToElements) 
            {
                // Get all elements bound to this key and remove this element
                ArrayList elements = (ArrayList)akm._keyToElements[key];
 
                if (elements != null)
                { 
                    PurgeDead(elements, element); 
                    if (elements.Count == 0)
                    { 
                        akm._keyToElements.Remove(key);
                    }
                }
            } 
        }
 
        ///  
        /// Tells if there is a particular key registered at the global scope in this Context.
        ///  
        /// Scope to query (for example the PresentationSource of the visual)
        /// 
        /// 
        public static bool IsKeyRegistered(object scope, string key) 
        {
            key = NormalizeKey(key); 
 
            AccessKeyManager akm = AccessKeyManager.Current;
            List targets = akm.GetTargetsForScope(scope, key, null, AccessKeyInformation.Empty); 
            return (targets != null && targets.Count > 0);
        }

        ///  
        /// Process the given key as if the key were pressed at the global scope in this context.
        ///  
        /// scope in which to invoke the access key 
        /// character being pressed
        /// True if this key has multiple matches 
        /// false if there are no more keys that match, true otherwise
        /// 
        ///     Executes the command on the given command source.
        ///  
        /// 
        ///     Critical - calls critical function (ProcessKeyForScope) 
        ///     PublicOK - always passes in false for userInitiated, which is safe 
        /// 
        [SecurityCritical] 
        public static bool ProcessKey(object scope, string key, bool isMultiple)
        {
            key = NormalizeKey(key);
 
            AccessKeyManager akm = AccessKeyManager.Current;
            return (akm.ProcessKeyForScope(scope, key, isMultiple,false) == ProcessKeyResult.MoreMatches); 
        } 

        ///  
        /// Returns StringInfo.GetNextTextElement(key).ToUpperInvariant() throwing exceptions for null
        /// and multi-char strings.
        /// 
        ///  
        /// 
        private static string NormalizeKey(string key) 
        { 
            if (key == null)
            { 
                throw new ArgumentNullException("key");
            }

            string firstCharacter = StringInfo.GetNextTextElement(key); 

            if (key != firstCharacter) 
            { 
                throw new ArgumentException(SR.Get(SRID.AccessKeyManager_NotAUnicodeCharacter, "key"));
            } 

            return firstCharacter.ToUpperInvariant();
        }
 
        /// 
        /// This event is used by elements that want to define a scope for accesskeys, such as Menu and Popup. 
        /// This event will never be raised, it is used to identify classes that define new scopes. 
        /// 
        public static readonly RoutedEvent AccessKeyPressedEvent = EventManager.RegisterRoutedEvent( 
            "AccessKeyPressed", RoutingStrategy.Bubble, typeof(AccessKeyPressedEventHandler), typeof(AccessKeyManager));

        /// 
        ///     Adds a handler for the AccessKeyPressed attached event 
        /// 
        /// UIElement or ContentElement that listens to this event 
        /// Event Handler to be added 
        public static void AddAccessKeyPressedHandler(DependencyObject element, AccessKeyPressedEventHandler handler)
        { 
            UIElement.AddHandler(element, AccessKeyPressedEvent, handler);
        }

        ///  
        ///     Removes a handler for the AccessKeyPressed attached event
        ///  
        /// UIElement or ContentElement that listens to this event 
        /// Event Handler to be removed
        public static void RemoveAccessKeyPressedHandler(DependencyObject element, AccessKeyPressedEventHandler handler) 
        {
            UIElement.RemoveHandler(element, AccessKeyPressedEvent, handler);
        }
 
        #endregion
 
        #region Constructor 
        /// 
        ///     Critical: This code accesses InputManager.Current which is critical 
        ///     TreatAsSafe: This code does not expose it and simply adds an event handler that is internal
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private AccessKeyManager() 
        {
            InputManager.Current.PostProcessInput += new ProcessInputEventHandler(PostProcessInput); 
        } 

        #endregion 

        #region Properties
        /// 
        /// Access to the current context's AccessKeyManager class 
        /// 
        private static AccessKeyManager Current 
        { 
            get
            { 
                if (_accessKeyManager == null)
                    _accessKeyManager = new AccessKeyManager();
                return _accessKeyManager;
            } 
        }
        #endregion 
 
        #region PostProcessInput Event Handlers
 
        private enum ProcessKeyResult
        {
            NoMatch,
            MoreMatches, 
            LastMatch
        } 
 
        /// 
        ///     Critical: accesses e.StagingItem.Input 
        /// 
        [SecurityCritical]
        private void PostProcessInput(object sender, ProcessInputEventArgs e)
        { 
            if (e.StagingItem.Input.Handled) return;
 
            if (e.StagingItem.Input.RoutedEvent == Keyboard.KeyDownEvent) 
            {
                OnKeyDown((KeyEventArgs)e.StagingItem.Input); 
            }
            else if (e.StagingItem.Input.RoutedEvent == TextCompositionManager.TextInputEvent)
            {
                OnText((TextCompositionEventArgs)e.StagingItem.Input); 
            }
 
        } 

        // Assumes key is already a single unicode character 
        /// 
        /// Critical - sets the userInitiated bit on a command, which is used
        ///            for security purposes later.
        ///  
        [SecurityCritical]
        private ProcessKeyResult ProcessKeyForSender(object sender, string key, bool existsElsewhere, bool userInitiated) 
        { 
            // This comes from OnKeyDown or OnText and though it is a single character it might not be uppercased.
            key = key.ToUpperInvariant(); 

            IInputElement inputElementSender = sender as IInputElement;
            List targets = GetTargetsForSender(inputElementSender, key);
 
            return ProcessKey(targets, key, existsElsewhere, userInitiated);
        } 
 
        // Assumes key is already a single unicode character AND is uppercased
        ///  
        /// Critical - sets the userInitiated bit on a command, which is used
        ///            for security purposes later.
        /// 
        [SecurityCritical] 
        private ProcessKeyResult ProcessKeyForScope(object scope, string key, bool existsElsewhere, bool userInitiated)
        { 
            List targets = GetTargetsForScope(scope, key, null, AccessKeyInformation.Empty); 

            return ProcessKey(targets, key, existsElsewhere, userInitiated); 
        }

        /// 
        /// Critical - Sets calls AccessKeyPressedEventArgs setting the userInitiated bit which is used 
        ///            for security purposes later.
        ///  
        [SecurityCritical] 
        private ProcessKeyResult ProcessKey(List targets, string key, bool existsElsewhere, bool userInitiated)
        { 
            if (targets != null)
            {
                bool oneUIElement = true;
                UIElement invokeUIElement = null; 
                bool lastWasFocused = false;
 
                int chosenIndex = 0; 
                for (int i = 0; i < targets.Count; i++)
                { 
                    UIElement target = targets[i] as UIElement;
                    Debug.Assert(target != null, "Targets should only be UIElements");
                    if (!target.IsEnabled)
                        continue; 

                    if (invokeUIElement == null) 
                    { 
                        invokeUIElement = target;
                        chosenIndex = i; 
                    }
                    else
                    {
                        if (lastWasFocused) 
                        {
                            invokeUIElement = target; 
                            chosenIndex = i; 
                        }
 
                        oneUIElement = false;
                    }

                    // 
                    lastWasFocused = target.IsKeyboardFocused;
                } 
 
                if (invokeUIElement != null)
                { 
                    AccessKeyEventArgs args = new AccessKeyEventArgs(key, !oneUIElement || existsElsewhere /* == isMultiple */,userInitiated);
                    try
                    {
                        invokeUIElement.InvokeAccessKey(args); 
                    }
                    finally 
                    { 
                        args.ClearUserInitiated();
                    } 

                    return (chosenIndex == targets.Count - 1) ? ProcessKeyResult.LastMatch : ProcessKeyResult.MoreMatches;
                }
            } 

            return ProcessKeyResult.NoMatch; 
        } 

        ///  
        /// Critical - Calls ProcessKeyForSender, setting the userInitiated
        ///             bit, which is used for security purposes later.
        /// 
        [SecurityCritical] 
        private void OnText(TextCompositionEventArgs e)
        { 
            // AccessKeyManager handles both text and system text. 
            string text = e.Text;
            if ((text == null) || (text.Length == 0)) 
            {
                text = e.SystemText;
            }
 
            if ((text != null) && (text.Length > 0))
            { 
                if (ProcessKeyForSender(e.OriginalSource, text, false /* existsElsewhere */,e.UserInitiated) != ProcessKeyResult.NoMatch) 
                {
                    e.Handled = true; 
                }
            }
        }
 
        /// 
        /// Critical - Calls ProcessKeyForSender, setting the userInitiated 
        ///             bit, which is used for security purposes later. 
        /// 
        [SecurityCritical] 
        private void OnKeyDown(KeyEventArgs e)
        {
            KeyboardDevice keyboard = (KeyboardDevice)e.Device;
 
            string text = null;
            switch (e.RealKey) 
            { 
                case Key.Enter :
                     text = "\x000D"; 
                     break;

                case Key.Escape :
                     text = "\x001B"; 
                     break;
            } 
 
            if (text != null)
            { 
                if (ProcessKeyForSender(e.OriginalSource, text, false /* existsElsewhere */,e.UserInitiated) != ProcessKeyResult.NoMatch)
                {
                    e.Handled = true;
                } 
            }
        } 
 
        /// 
        /// Get the list of access key targets for the sender of the keyboard event.  If sender is null, 
        /// pretend key was pressed in the active window.
        /// 
        /// 
        ///  
        /// 
        ///  
        ///     Critical: calls Critical member GetInfoForElement() and accesses Scope from AccessKeyInformation. 
        ///     TreatAsSafe:  Does not pass the sender info (which may contain a PresentationSource) out.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private List GetTargetsForSender(IInputElement sender, string key)
        {
            // Find the scope for the sender -- will be matched against the possible targets' scopes 
            AccessKeyInformation senderInfo = GetInfoForElement(sender, key);
 
            return GetTargetsForScope(senderInfo.Scope, key, sender, senderInfo); 
        }
 
        /// 
        ///     Critical: calls CriticalGetActiveSource()
        ///     TreatAsSafe:  Does not pass the returned scope to any other method, nor is it
        ///                 returned from GetTargetsForScope.  Also returned value is not critical. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private List GetTargetsForScope(object scope, string key, IInputElement sender, AccessKeyInformation senderInfo) 
        {
            // null scope defaults to the active window 
            if (scope == null)
            {
                scope = CriticalGetActiveSource();
 
                // if there is no active scope then give up
                if (scope == null) 
                { 
                    return null;
                } 
            }

            //Scoping:
            //    1) When key is pressed, find matching AKs -> S 
            //    3) find scope for keyevent.Source
            //    4) find scope for everything in S. throw away those that don't match. 
            //    5) Final selection uses S.  yay! 
            //
            // 
            List possibleElements;
            lock (_keyToElements)
            {
                possibleElements = CopyAndPurgeDead(_keyToElements[key] as ArrayList); 
            }
 
            if (possibleElements == null) return null; 

            List finalTargets = new List(1); 

            // Go through all the possible elements, find the interesting candidates
            for (int i = 0; i < possibleElements.Count;  i++)
            { 
                IInputElement element = possibleElements[i];
                if (element != sender) 
                { 
                    if (IsTargetable(element))
                    { 
                        AccessKeyInformation elementInfo = GetInfoForElement(element, key);

                        if (elementInfo.target == null) continue;
 
                        if (scope == elementInfo.Scope)
                        { 
                            finalTargets.Add(elementInfo.target); 
                        }
                    } 
                }
                else
                {
                    // This is the same element that sent the event so it must be in the same scope. 
                    // Just add it to the final targets
                    if (senderInfo.target != null) 
                    { 
                        finalTargets.Add(senderInfo.target);
                    } 
                }
            }

            return finalTargets; 
        }
 
        ///  
        /// Returns scope for the given element.
        ///  
        /// 
        /// 
        /// Scope for the given element, null means the context global scope
        ///  
        ///     Critical: calls GetSourceForElement(), CriticalGetActiveSource(), and returns AccessKeyInformation.
        ///  
        [SecurityCritical] 
        private AccessKeyInformation GetInfoForElement(IInputElement element, string key)
        { 
            AccessKeyInformation info = new AccessKeyInformation();
            if (element != null)
            {
                AccessKeyPressedEventArgs args = new AccessKeyPressedEventArgs(key); 

                element.RaiseEvent(args); 
                info.Scope = args.Scope; 
                info.target = args.Target;
                if (info.Scope == null) 
                {
                    info.Scope = GetSourceForElement(element);
                }
            } 
            else
            { 
                info.Scope = CriticalGetActiveSource(); 
            }
            return info; 
        }

        /// 
        ///     Critical: calls PresentationSource.CriticalFromVisual, and returns PresentationSource 
        /// 
        [SecurityCritical] 
        private PresentationSource GetSourceForElement(IInputElement element) 
        {
            PresentationSource source = null; 
            DependencyObject elementDO = element as DependencyObject;

            // Use internal helpers to try to find the source of the element.
            // Because IInputElements can move around without notification we need to 
            // look up the source every time.
            if (elementDO != null) 
            { 
                DependencyObject containingVisual = InputElement.GetContainingVisual(elementDO);
 
                if (containingVisual != null)
                {
                    source = PresentationSource.CriticalFromVisual(containingVisual);
                } 
            }
 
            // NOTE: source can be null but IsTargetable(element) == true if the 
            // element is in an orphaned tree but the tree has not yet been garbage collected.
            return source; 
        }

        /// 
        ///     Critical: calls UnsafeNativeMethod GetActiveWindow(), and Critical method 
        ///               HwndSource.FromHwnd().
        ///     TreatAsSafe: Does not pass any parameters to GetActiveWindow(), and does 
        ///                 not expose the return value, and HwndSource.FromHwnd will demand 
        ///                 UIPermissionWindow.AllWindows.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private PresentationSource GetActiveSource()
        {
            IntPtr hwnd = MS.Win32.UnsafeNativeMethods.GetActiveWindow(); 
            if (hwnd != IntPtr.Zero)
                return HwndSource.FromHwnd(hwnd); 
 
            return null;
        } 

        /// 
        ///     Critical: calls UnsafeNativeMethod GetActiveWindow() and HwndSource.CriticalFromHwnd()
        ///  
        [SecurityCritical]
        private PresentationSource CriticalGetActiveSource() 
        { 
            IntPtr hwnd = MS.Win32.UnsafeNativeMethods.GetActiveWindow();
            if (hwnd != IntPtr.Zero) 
                return HwndSource.CriticalFromHwnd(hwnd);

            return null;
        } 

 
        private bool IsTargetable(IInputElement element) 
        {
            DependencyObject uielement = InputElement.GetContainingUIElement((DependencyObject)element); 

            // For an element to be a valid target it must be visible and enabled
            if (uielement != null
                && IsVisible(uielement) 
                && IsEnabled(uielement))
            { 
                return true; 
            }
 
            return false;
        }

        private static bool IsVisible(DependencyObject element) 
        {
            while (element != null) 
            { 
                Visibility visibility;
                UIElement uiElem = element as UIElement; 
                UIElement3D uiElem3D = element as UIElement3D;

                if (uiElem != null)

                { 
                    visibility = uiElem.Visibility;
                } 
                else 
                {
                    visibility = uiElem3D.Visibility; 
                }

                if (visibility != Visibility.Visible)
                { 
                    return false;
                } 
 
                element = UIElementHelper.GetUIParent(element);
            } 

            return true;
        }
 
        // returns whether the given DO is enabled or not
        private static bool IsEnabled(DependencyObject element) 
        { 
            return ((bool)element.GetValue(UIElement.IsEnabledProperty));
        } 

        private struct AccessKeyInformation
        {
            ///  
            ///     Critical: Scope may contain an PresentationSource which we
            ///               would not want exposed. 
            ///  
            public object Scope
            { 
                [SecurityCritical]
                get
                {
                    return _scope; 
                }
                set 
                { 
                    _scope = value;
                } 
            }


            public UIElement target; 

            private static AccessKeyInformation _empty = new AccessKeyInformation(); 
            public static AccessKeyInformation Empty 
            {
                get 
                {
                    return _empty;
                }
            } 

            private object _scope; 
        } 

        private static void PurgeDead(ArrayList elements, object elementToRemove) 
        {
            for (int i = 0; i < elements.Count; )
            {
                WeakReference weakReference = (WeakReference)elements[i]; 
                object element = weakReference.Target;
 
                if (element == null || element == elementToRemove) 
                {
                    elements.RemoveAt(i); 
                }
                else
                {
                    i++; 
                }
            } 
        } 

        ///  
        ///     Takes an ArrayList of WeakReferences, removes the dead references and returns
        ///     a generic List of IInputElements (strong references)
        /// 
        private static List CopyAndPurgeDead(ArrayList elements) 
        {
            if (elements == null) 
            { 
                return null;
            } 

            List copy = new List(elements.Count);

            for (int i = 0; i < elements.Count; ) 
            {
                WeakReference weakReference = (WeakReference)elements[i]; 
                object element = weakReference.Target; 

                if (element == null) 
                {
                    elements.RemoveAt(i);
                }
                else 
                {
                    Debug.Assert(element is IInputElement, "Element in AccessKeyManager store was not of type IInputElement"); 
                    copy.Add((IInputElement)element); 
                    i++;
                } 
            }

            return copy;
        } 

        #endregion 
 
        #region Private Properties
 
        /////////////////////////////////////////////////////////////////////////////////
        // Overview: Algorithm to look up access key from the element for which it is a target.
        //
        //     When the AccessKeyCharacter for an element is requested we see if there 
        //     is a corresponding AccessKeyElement stashed on the element.  If there is,
        //     raise the AccessKeyPressed event on it to see if that element is  still the 
        //     target for it.  If not, go through all registered accesskeys and get their 
        //     targets until we find the desired element.  The "primary" access key character
        //     is the first one we find. 
        //
        //     Note: The algorithm ends up being O(n) for each request for AccessKeyCharacter
        //     because there is no mapping from AccessKeyElement to its "primary" character.
        //     Maintaining this would require storing a hash from AccessKeyElement to character 
        //     or requiring that each element registered implement an interface or some other
        //     kind of contract.  Because we don't keep track of this or enforce this, to find 
        //     the "primary" character we must go through all registered pairs of 
        //     (character, element) to find the character -- O(n).
        // 
        //     This ends up being just fine, because in any given context there shouldn't be
        //     so many elements registered that this cost is at all noticable.
        //
        ///////////////////////////////////////////////////////////////////////////////// 

        ///  
        ///     The primary access key element for an element.  This is stored as a WeakReference. 
        /// 
        private static readonly DependencyProperty AccessKeyElementProperty = 
            DependencyProperty.RegisterAttached("AccessKeyElement", typeof(WeakReference), typeof(AccessKeyManager));

        #endregion
 
        #region Private Methods for UIAutomation
 
        internal static string InternalGetAccessKeyCharacter(DependencyObject d) 
        {
            return Current.GetAccessKeyCharacter(d); 
        }

        private string GetAccessKeyCharacter(DependencyObject d)
        { 
            // See what the local value for AccessKeyElement is first and start with that.
            WeakReference cachedElementWeakRef = (WeakReference)d.GetValue(AccessKeyElementProperty); 
            IInputElement accessKeyElement = (cachedElementWeakRef != null) ? (IInputElement)cachedElementWeakRef.Target : null; 

            if (accessKeyElement != null) 
            {
                // First figure out if the target of accessKeyElement is still "d", then go find
                // the "primary" character for the accessKeyElement.
 
                AccessKeyPressedEventArgs accessKeyPressedEventArgs = new AccessKeyPressedEventArgs();
                accessKeyElement.RaiseEvent(accessKeyPressedEventArgs); 
                if (accessKeyPressedEventArgs.Target == d) 
                {
                    // Because there is no way to get at the access key element's character from the 
                    // element (there is no interface or anything) we have to go through all registered
                    // access keys and see if this access key element is still registered and what its
                    // "primary" character is.
 
                    foreach (DictionaryEntry entry in Current._keyToElements)
                    { 
                        ArrayList elements = (ArrayList)entry.Value; 
                        for (int i = 0; i < elements.Count; i++)
                        { 
                            // If this element matches accessKeyElement, then return the current character
                            WeakReference currentElementWeakRef = (WeakReference)elements[i];

                            if (currentElementWeakRef.Target == accessKeyElement) 
                            {
                                return (string)entry.Key; 
                            } 
                        }
                    } 
                }
            }

 
            // There was no access key stored or it no longer matched.  Clear out the cache and figure it out again.
            d.ClearValue(AccessKeyElementProperty); 
 
            foreach (DictionaryEntry entry in Current._keyToElements)
            { 
                ArrayList elements = (ArrayList)entry.Value;
                for (int i = 0; i < elements.Count; i++)
                {
                    // Determine the target for this element.  Cache the weak reference for the element on the target. 
                    WeakReference currentElementWeakRef = (WeakReference)elements[i];
                    IInputElement currentElement = (IInputElement)currentElementWeakRef.Target; 
 
                    if (currentElement != null)
                    { 
                        AccessKeyPressedEventArgs accessKeyPressedEventArgs = new AccessKeyPressedEventArgs();
                        currentElement.RaiseEvent(accessKeyPressedEventArgs);

                        // If the target was non-null, cache the access key element on the target. 
                        // if the target matches "d", return the current character.
                        if (accessKeyPressedEventArgs.Target != null) 
                        { 
                            accessKeyPressedEventArgs.Target.SetValue(AccessKeyElementProperty, currentElementWeakRef);
 
                            if (accessKeyPressedEventArgs.Target == d)
                            {
                                return (string)entry.Key;
                            } 
                        }
                    } 
                } 
            }
 

            return String.Empty;
        }
 
        #endregion
 
        #region Data 
        // Map: string -> ArrayList of WeakReferences to IInputElements
        private Hashtable _keyToElements = new Hashtable(10); 

        [ThreadStatic] private static AccessKeyManager _accessKeyManager;

        #endregion 
    }
 
    ///  
    /// The delegate type for handling a FindScope event
    ///  
    public delegate void AccessKeyPressedEventHandler(object sender, AccessKeyPressedEventArgs e);

    /// 
    /// The inputs to an AccessKeyPressedEventHandler 
    /// 
    public class AccessKeyPressedEventArgs : RoutedEventArgs 
    { 
        #region Constructors
 
        /// 
        /// The constructor for AccessKeyPressed event args
        /// 
        public AccessKeyPressedEventArgs() 
        {
            RoutedEvent = AccessKeyManager.AccessKeyPressedEvent; 
            _key = null; 
        }
 
        /// 
        /// Constructor for AccessKeyPressed event args
        /// 
        ///  
        public AccessKeyPressedEventArgs(string key) : this()
        { 
            _key = key; 
        }
 
        #endregion

        #region Public Properties
 
        /// 
        /// The scope for the element that raised this event. 
        ///  
        public object Scope
        { 
            get { return _scope; }
            set { _scope = value; }
        }
 
        /// 
        /// Target element for the element that raised this event. 
        ///  
        /// 
        public UIElement Target 
        {
            get { return _target; }
            set { _target = value; }
        } 

        ///  
        /// Key that was pressed 
        /// 
        ///  
        public string Key
        {
            get { return _key; }
        } 

        #endregion 
 
        #region Protected Methods
 
        /// 
        /// 
        /// The handler to invoke.
        /// The current object along the event's route. 
        protected override void InvokeEventHandler(Delegate genericHandler, object genericTarget)
        { 
            AccessKeyPressedEventHandler handler = (AccessKeyPressedEventHandler)genericHandler; 

            handler(genericTarget, this); 
        }

        #endregion
 
        #region Data
 
        private object _scope; 
        private UIElement _target;
        private string _key; 

        #endregion
    }
 
    /// 
    /// Information pertaining to when the access key associated with an element is pressed 
    ///  
    public class AccessKeyEventArgs : EventArgs
    { 
        /// 
        ///
        /// 
        ///  
        /// Critical - sets the userInitiated bit on a command, which is used
        ///            for security purposes later. 
        ///  
        [SecurityCritical]
        internal AccessKeyEventArgs(string key, bool isMultiple, bool userInitiated) 
        {
            _key = key;
            _isMultiple = isMultiple;
            _userInitiated = new SecurityCriticalDataForSet(userInitiated); 
        }
 
        ///  
        /// Critical - sets the userInitiated bit on a command, which is used
        ///            for security purposes later. 
        /// TreatAsSafe: Resets the user initiated bit
        /// 
        [SecurityCritical,SecurityTreatAsSafe]
        internal void ClearUserInitiated() 
        {
            _userInitiated.Value = false; 
        } 
        /// 
        /// The key that was pressed which invoked this access key 
        /// 
        /// 
        public string Key
        { 
            get { return _key; }
        } 
 
        /// 
        /// Were there other elements which are also invoked by this key 
        /// 
        /// 
        public bool IsMultiple
        { 
            get { return _isMultiple; }
        } 
 
        internal bool UserInitiated
        { 
            get { return _userInitiated.Value; }
        }

 
        private string _key;
        private bool _isMultiple; 
        ///  
        /// Critical -  This is critical for set, setting this bool can cause the spoofing of paste
        ///  
        private SecurityCriticalDataForSet_userInitiated;

    }
} 

// 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