Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Framework / System / Windows / Input / KeyboardNavigation.cs / 1 / KeyboardNavigation.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Windows.Threading; using System.Threading; using System.Windows; using System.Windows.Documents; using System.Windows.Interop; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Media3D; using System.Security; using System.Security.Permissions; using MS.Utility; using MS.Internal.Controls; using MS.Internal; using MS.Internal.PresentationFramework; using MS.Internal.KnownBoxes; using Microsoft.Win32; namespace System.Windows.Input { #region public enum types ////// These options specify how the container will move the focus when tab and directional navigation occurs /// public enum KeyboardNavigationMode { ////// The container does not handle the keyboard navigation; /// each element receives keyboard focus as long as it is a key navigation stop. /// Continue, ////// The container and all of its child elements as a whole only receive focus once. /// Either the first tree child or the ActiveElement receive focus /// Once, ////// Depending on the direction of the navigation, /// the focus returns to the first or the last item when the end or /// the beginning of the container is reached, respectively. /// Cycle, ////// No keyboard navigation is allowed inside this container /// None, ////// Like cycle but does not move past the beginning or end of the container. /// Contained, ////// TabIndexes are considered on local subtree only inside this container /// Local, // NOTE: if you add or remove any values in this enum, be sure to update KeyboardNavigation.IsValidKeyNavigationMode() } #endregion public enum types ////// KeyboardNavigation class provide methods for logical (Tab) and directional (arrow) navigation between focusable controls /// public sealed class KeyboardNavigation { #region Constructors ////// Critical - this function elevates via a call to InputManager.Current /// TreatAsSafe: This code simply attaches a call back which is private /// [SecurityCritical,SecurityTreatAsSafe] internal KeyboardNavigation() { InputManager inputManager = InputManager.Current; inputManager.PostProcessInput += new ProcessInputEventHandler(PostProcessInput); inputManager.TranslateAccelerator += new KeyEventHandler(TranslateAccelerator); } #endregion Constructors #region public API #region Properties private static readonly DependencyProperty TabOnceActiveElementProperty = DependencyProperty.RegisterAttached("TabOnceActiveElement", typeof(WeakReference), typeof(KeyboardNavigation)); internal static DependencyObject GetTabOnceActiveElement(DependencyObject d) { WeakReference weakRef = (WeakReference)d.GetValue(TabOnceActiveElementProperty); if (weakRef != null && weakRef.IsAlive) { DependencyObject activeElement = weakRef.Target as DependencyObject; // Verify if the element is still in the same visual tree if (GetVisualRoot(activeElement) == GetVisualRoot(d)) return activeElement; else d.SetValue(TabOnceActiveElementProperty, null); } return null; } internal static void SetTabOnceActiveElement(DependencyObject d, DependencyObject value) { d.SetValue(TabOnceActiveElementProperty, new WeakReference(value)); } internal static readonly DependencyProperty ControlTabOnceActiveElementProperty = DependencyProperty.RegisterAttached("ControlTabOnceActiveElement", typeof(WeakReference), typeof(KeyboardNavigation)); private static DependencyObject GetControlTabOnceActiveElement(DependencyObject d) { WeakReference weakRef = (WeakReference)d.GetValue(ControlTabOnceActiveElementProperty); if (weakRef != null && weakRef.IsAlive) { DependencyObject activeElement = weakRef.Target as DependencyObject; // Verify if the element is still in the same visual tree if (GetVisualRoot(activeElement) == GetVisualRoot(d)) return activeElement; else d.SetValue(ControlTabOnceActiveElementProperty, null); } return null; } private static void SetControlTabOnceActiveElement(DependencyObject d, DependencyObject value) { d.SetValue(ControlTabOnceActiveElementProperty, new WeakReference(value)); } private DependencyObject GetActiveElement(DependencyObject d) { return _navigationProperty == ControlTabNavigationProperty ? GetControlTabOnceActiveElement(d) : GetTabOnceActiveElement(d); } private void SetActiveElement(DependencyObject d, DependencyObject value) { if (_navigationProperty == TabNavigationProperty) SetTabOnceActiveElement(d, value); else SetControlTabOnceActiveElement(d, value); } ////// Critical: This code retrieves PresentationSource which is a protected resource /// TreatAsSafe: It returns rootvisual which is ok and it does not expose the PresentationSource /// [SecurityCritical, SecurityTreatAsSafe] internal static Visual GetVisualRoot(DependencyObject d) { if (d is Visual || d is Visual3D) { PresentationSource source = PresentationSource.CriticalFromVisual(d); if (source != null) return source.RootVisual; } else { FrameworkContentElement fce = d as FrameworkContentElement; if (fce != null) return GetVisualRoot(fce.Parent); } return null; } // This internal property is used by GetRectagle method to deflate the bounding box of the element // If we expose this in the future - make sure it works with ContentElements too internal static readonly DependencyProperty DirectionalNavigationMarginProperty = DependencyProperty.RegisterAttached("DirectionalNavigationMargin", typeof(Thickness), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(new Thickness())); ////// The DependencyProperty for the TabIndex property. /// Flags: Can be used in style rules /// Default Value: Int32.MaxValue /// public static readonly DependencyProperty TabIndexProperty = DependencyProperty.RegisterAttached( "TabIndex", typeof(int), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(Int32.MaxValue)); ////// The DependencyProperty for the IsTabStop property. /// Flags: Can be used in style rules /// Default Value: true /// public static readonly DependencyProperty IsTabStopProperty = DependencyProperty.RegisterAttached( "IsTabStop", typeof(bool), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); ////// Controls the behavior of logical navigation on the children of the element this property is set on. /// TabNavigation is invoked with the TAB key. /// [CustomCategory("Accessibility")] [Localizability(LocalizationCategory.NeverLocalize)] [CommonDependencyProperty] public static readonly DependencyProperty TabNavigationProperty = DependencyProperty.RegisterAttached( "TabNavigation", typeof(KeyboardNavigationMode), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(KeyboardNavigationMode.Continue), new ValidateValueCallback(IsValidKeyNavigationMode)); ////// Controls the behavior of logical navigation on the children of the element this property is set on. /// ControlTabNavigation is invoked with the CTRL+TAB key. /// [CustomCategory("Accessibility")] [Localizability(LocalizationCategory.NeverLocalize)] [CommonDependencyProperty] public static readonly DependencyProperty ControlTabNavigationProperty = DependencyProperty.RegisterAttached( "ControlTabNavigation", typeof(KeyboardNavigationMode), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(KeyboardNavigationMode.Continue), new ValidateValueCallback(IsValidKeyNavigationMode)); ////// Controls the behavior of directional navigation on the children of the element this property is set on. /// Directional navigation is invoked with the arrow keys. /// [CustomCategory("Accessibility")] [Localizability(LocalizationCategory.NeverLocalize)] [CommonDependencyProperty] public static readonly DependencyProperty DirectionalNavigationProperty = DependencyProperty.RegisterAttached( "DirectionalNavigation", typeof(KeyboardNavigationMode), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(KeyboardNavigationMode.Continue), new ValidateValueCallback(IsValidKeyNavigationMode)); ////// Attached property set on elements registered with AccessKeyManager when AccessKeyCues should be shown. /// internal static readonly DependencyProperty ShowKeyboardCuesProperty = DependencyProperty.RegisterAttached( "ShowKeyboardCues", typeof(bool), typeof(KeyboardNavigation), new FrameworkPropertyMetadata( BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior, null /* No PropertyChangedCallback */, new CoerceValueCallback(CoerceShowKeyboardCues))); // Coercion for ShowKeyboardCuesProperty private static object CoerceShowKeyboardCues(DependencyObject d, object value) { // Always return true if the user has requested that KeyboardCues always // be on (accessibility setting). return SystemParameters.KeyboardCues ? BooleanBoxes.TrueBox : value; } ////// Indicates if VK_Return character is accepted by a control /// /// Default: false. /// public static readonly DependencyProperty AcceptsReturnProperty = DependencyProperty.RegisterAttached( "AcceptsReturn", typeof(bool), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); #region Workaround for Bug 908235 -- when focus change events go to PostProcessInput we don't need this glue. internal event KeyboardFocusChangedEventHandler FocusChanged { add { lock (_weakFocusChangedHandlers) { _weakFocusChangedHandlers.Add(new WeakReference(value)); } } remove { lock (_weakFocusChangedHandlers) { for (int i = 0; i < _weakFocusChangedHandlers.Count; i++) { object handler = _weakFocusChangedHandlers[i].Target; if (handler == null || (KeyboardFocusChangedEventHandler)handler == value) { _weakFocusChangedHandlers.RemoveAt(i); i--; } } } } } internal void NotifyFocusChanged(object sender, KeyboardFocusChangedEventArgs e) { if (_weakFocusChangedHandlers != null) { for (int i = 0; i < _weakFocusChangedHandlers.Count; i++) { KeyboardFocusChangedEventHandler handler = _weakFocusChangedHandlers[i].Target as KeyboardFocusChangedEventHandler; if (handler != null) { handler(sender, e); } else { _weakFocusChangedHandlers.RemoveAt(i); i--; } } } } private List_weakFocusChangedHandlers = new List (1); #endregion #endregion Properties #region methods /// /// Writes the attached property TabIndex to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetTabIndex(DependencyObject element, int index) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(TabIndexProperty, index); } /// /// Reads the attached property TabIndex from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static int GetTabIndex(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return GetTabIndexHelper(element); } /// /// Writes the attached property IsTabStop to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetIsTabStop(DependencyObject element, bool isTabStop) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(IsTabStopProperty, BooleanBoxes.Box(isTabStop)); } /// /// Reads the attached property IsTabStop from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static bool GetIsTabStop(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool)element.GetValue(IsTabStopProperty); } /// /// Writes the attached property TabNavigation to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetTabNavigation(DependencyObject element, KeyboardNavigationMode mode) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(TabNavigationProperty, mode); } /// /// Reads the attached property TabNavigation from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[CustomCategory("Accessibility")] [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static KeyboardNavigationMode GetTabNavigation(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (KeyboardNavigationMode)element.GetValue(TabNavigationProperty); } /// /// Writes the attached property ControlTabNavigation to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetControlTabNavigation(DependencyObject element, KeyboardNavigationMode mode) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(ControlTabNavigationProperty, mode); } /// /// Reads the attached property ControlTabNavigation from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[CustomCategory("Accessibility")] [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static KeyboardNavigationMode GetControlTabNavigation(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (KeyboardNavigationMode)element.GetValue(ControlTabNavigationProperty); } /// /// Writes the attached property DirectionalNavigation to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetDirectionalNavigation(DependencyObject element, KeyboardNavigationMode mode) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(DirectionalNavigationProperty, mode); } /// /// Reads the attached property DirectionalNavigation from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[CustomCategory("Accessibility")] [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static KeyboardNavigationMode GetDirectionalNavigation(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (KeyboardNavigationMode)element.GetValue(DirectionalNavigationProperty); } /// /// Writes the attached property AcceptsReturn to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetAcceptsReturn(DependencyObject element, bool enabled) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(AcceptsReturnProperty, BooleanBoxes.Box(enabled)); } /// /// Reads the attached property AcceptsReturn from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static bool GetAcceptsReturn(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool)element.GetValue(AcceptsReturnProperty); } private static bool IsValidKeyNavigationMode(object o) { KeyboardNavigationMode value = (KeyboardNavigationMode)o; return value == KeyboardNavigationMode.Contained || value == KeyboardNavigationMode.Continue || value == KeyboardNavigationMode.Cycle || value == KeyboardNavigationMode.None || value == KeyboardNavigationMode.Once || value == KeyboardNavigationMode.Local; } #endregion methods #endregion public API #region FocusVisualStyle API // This class is used by AdornerLayer which adds it to its visual tree // Once AdornerLayer and Adorner are UIElement we can remove this class and // apply FrameworkElement.FocusVisualStyle directly to AdornerLayer // Note:- This class is sealed because it calls OnVisualChildrenChanged virtual in the // constructor and it does not override it, but derived classes could. private sealed class FocusVisualAdorner: Adorner { public FocusVisualAdorner(UIElement adornedElement, Style focusVisualStyle) : base(adornedElement) { Debug.Assert(adornedElement != null, "adornedElement should not be null"); Debug.Assert(focusVisualStyle != null, "focusVisual should not be null"); Control control = new Control(); control.Style = focusVisualStyle; _adorderChild = control; IsClipEnabled = true; IsHitTestVisible = false; IsEnabled = false; AddVisualChild(_adorderChild); } public FocusVisualAdorner(ContentElement adornedElement, UIElement adornedElementParent, IContentHost contentHostParent, Style focusVisualStyle) : base(adornedElementParent) { Debug.Assert(adornedElement != null, "adornedElement should not be null"); Debug.Assert(adornedElementParent != null, "adornedElementParent should not be null"); Debug.Assert(contentHostParent != null, "contentHostParent should not be null"); Debug.Assert(contentHostParent is Visual, "contentHostParent should be Visual"); Debug.Assert(focusVisualStyle != null, "focusVisual should not be null"); _contentHostParent = contentHostParent; _adornedContentElement = adornedElement; _focusVisualStyle = focusVisualStyle; Canvas canvas = new Canvas(); _canvasChildren = canvas.Children; _adorderChild = canvas; AddVisualChild(_adorderChild); IsClipEnabled = true; IsHitTestVisible = false; IsEnabled = false; } /// /// Measure adorner. Default behavior is to size to match the adorned element. /// protected override Size MeasureOverride(Size constraint) { Size desiredSize = new Size(); // If the focus visual is adorning a content element, // the child will be a canvas that doesn't need to be measured. if (_adornedContentElement == null) { desiredSize = AdornedElement.RenderSize; constraint = desiredSize; } // Measure the child ((UIElement)GetVisualChild(0)).Measure(constraint); return desiredSize; } ////// Default control arrangement is to only arrange /// the first visual child. No transforms will be applied. /// protected override Size ArrangeOverride(Size size) { Size finalSize = base.ArrangeOverride(size); // In case we adorn ContentElement we have to update the rectangles if (_adornedContentElement != null) { if (_contentRects == null) { // Clear rects _canvasChildren.Clear(); } else { IContentHost contentHost = ContentHost; if (!(contentHost is Visual) || !AdornedElement.IsAncestorOf((Visual)contentHost)) { // Content elements is not in the tree, clear children and give up. _canvasChildren.Clear(); return new Size(); } Rect desiredRect = Rect.Empty; IEnumeratorenumerator = _contentRects.GetEnumerator(); if (_canvasChildren.Count == _contentRects.Count) { // Reuse the controls and update the controls position for (int i = 0; i < _canvasChildren.Count; i++) { enumerator.MoveNext(); Rect rect = enumerator.Current; rect = _hostToAdornedElement.TransformBounds(rect); Control control = (Control)_canvasChildren[i]; control.Width = rect.Width; control.Height = rect.Height; Canvas.SetLeft(control, rect.X); Canvas.SetTop(control, rect.Y); } _adorderChild.InvalidateArrange(); } else // Rebuild the visual tree to correspond to current bounding rectangles { _canvasChildren.Clear(); while (enumerator.MoveNext()) { Rect rect = enumerator.Current; rect = _hostToAdornedElement.TransformBounds(rect); Control control = new Control(); control.Style = _focusVisualStyle; control.Width = rect.Width; control.Height = rect.Height; Canvas.SetLeft(control, rect.X); Canvas.SetTop(control, rect.Y); _canvasChildren.Add(control); } } } } ((UIElement)GetVisualChild(0)).Arrange(new Rect(new Point(), finalSize)); return finalSize; } /// /// Derived classes override this property to enable the Visual code to enumerate /// the Visual children. Derived classes need to return the number of children /// from this method. /// /// By default a Visual does not have any children. /// /// Remark: /// During this virtual method the Visual tree must not be modified. /// protected override int VisualChildrenCount { get { return 1; // _adorderChild created in ctor. } } ////// Derived class must implement to support Visual children. The method must return /// the child at the specified index. Index must be between 0 and GetVisualChildrenCount-1. /// /// By default a Visual does not have any children. /// /// Remark: /// During this virtual call it is not valid to modify the Visual tree. /// protected override Visual GetVisualChild(int index) { if (index == 0) { return _adorderChild; } else { throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange)); } } private IContentHost ContentHost { get { // Re-query IContentHost if the old one was disposed if (_adornedContentElement != null && (_contentHostParent==null || VisualTreeHelper.GetParent(_contentHostParent as Visual) == null)) { _contentHostParent = MS.Internal.Documents.ContentHostHelper.FindContentHost(_adornedContentElement); } return _contentHostParent; } } ////// Says if the Adorner needs update based on the /// previously cached size if the AdornedElement. /// internal override bool NeedsUpdate(Size oldSize) { if (_adornedContentElement != null) { ReadOnlyCollectionoldRects = _contentRects; _contentRects = null; IContentHost contentHost = ContentHost; if (contentHost != null) { _contentRects = contentHost.GetRectangles(_adornedContentElement); } // The positions of the focus rects are dependent on the rects returned from // host.GetRectangles and the transform from the host to the adorned element GeneralTransform oldTransform = _hostToAdornedElement; if (contentHost is Visual && AdornedElement.IsAncestorOf((Visual)contentHost)) { _hostToAdornedElement = ((Visual)contentHost).TransformToAncestor(AdornedElement); } else { _hostToAdornedElement = Transform.Identity; } // See if these are the same transform if (oldTransform != _hostToAdornedElement) { // Allow two identical matrix transforms if (!(oldTransform is MatrixTransform) || !(_hostToAdornedElement is MatrixTransform) || !Matrix.Equals(((MatrixTransform)oldTransform).Matrix, ((MatrixTransform)_hostToAdornedElement).Matrix)) { // one is a general transform or the matrices are not equal, need to update return true; } } if (_contentRects != null && oldRects != null && _contentRects.Count == oldRects.Count) { for (int i=0; i _contentRects; } internal static UIElement GetParentUIElementFromContentElement(ContentElement ce) { IContentHost ichParent = null; return GetParentUIElementFromContentElement(ce, ref ichParent); } private static UIElement GetParentUIElementFromContentElement(ContentElement ce, ref IContentHost ichParent) { if (ce == null) return null; IContentHost ich = MS.Internal.Documents.ContentHostHelper.FindContentHost(ce); if (ichParent == null) ichParent = ich; DependencyObject parent = ich as DependencyObject; if(parent != null) { // Case 1: UIElement // return the element UIElement eParent = parent as UIElement; if(eParent != null) return eParent; // Case 2: Visual // Walk up the visual tree until we find UIElement Visual visualParent = parent as Visual; while (visualParent != null) { visualParent = VisualTreeHelper.GetParent(visualParent) as Visual; UIElement uielement = visualParent as UIElement; if (uielement != null) return uielement; } // Case 3: ContentElement ContentElement ceParent = parent as ContentElement; if(ceParent != null) return GetParentUIElementFromContentElement(ceParent, ref ichParent); } return null; } internal void HideFocusVisual() { // Remove the existing focus visual if (_focusVisualAdornerCache != null) { AdornerLayer adornerlayer = VisualTreeHelper.GetParent(_focusVisualAdornerCache) as AdornerLayer; if (adornerlayer != null) { adornerlayer.Remove(_focusVisualAdornerCache); } _focusVisualAdornerCache = null; } } /// /// Critical: This code accesses link demanded input manager /// TreatAsSafe: This code is ok to expose as it simply return boolean weather Keyboard is the last used device /// [SecurityCritical, SecurityTreatAsSafe] internal static bool IsKeyboardMostRecentInputDevice() { return InputManager.Current.MostRecentInputDevice is KeyboardDevice; } internal static bool AlwaysShowFocusVisual { get { return _alwaysShowFocusVisual; } set { _alwaysShowFocusVisual = value; } } private static bool _alwaysShowFocusVisual = SystemParameters.KeyboardCues; internal static void ShowFocusVisual() { Current.ShowFocusVisual(Keyboard.FocusedElement as DependencyObject); } private void ShowFocusVisual(DependencyObject element) { // Always hide the existing focus visual HideFocusVisual(); // Disable keyboard cues (accesskey underline) if keyboard device is not MostRecentInputDevice if (!IsKeyboardMostRecentInputDevice()) { EnableKeyboardCues(element, false); } // Show focus visual if system metric is true or keyboard is used last if (AlwaysShowFocusVisual || IsKeyboardMostRecentInputDevice()) { FrameworkElement fe = element as FrameworkElement; if (fe != null) { AdornerLayer adornerlayer = AdornerLayer.GetAdornerLayer(fe); if (adornerlayer == null) return; Style fvs = fe.FocusVisualStyle; // WORKAROUND: (Bug 1016350) If FocusVisualStyle is the "default" value // then we load the default FocusVisualStyle from ResourceDictionary. if (fvs == FrameworkElement.DefaultFocusVisualStyle) { fvs = SystemResources.FindResourceInternal(SystemParameters.FocusVisualStyleKey) as Style; } if (fvs != null) { _focusVisualAdornerCache = new FocusVisualAdorner(fe, fvs); adornerlayer.Add(_focusVisualAdornerCache); } } else // If not FrameworkElement { FrameworkContentElement fce = element as FrameworkContentElement; if (fce != null) { IContentHost parentICH = null; UIElement parentUIElement = GetParentUIElementFromContentElement(fce, ref parentICH); if (parentICH != null && parentUIElement != null) { AdornerLayer adornerlayer = AdornerLayer.GetAdornerLayer(parentUIElement); if (adornerlayer != null) { Style fvs = fce.FocusVisualStyle; // WORKAROUND: (Bug 1016350) If FocusVisualStyle is the "default" value // then we load the default FocusVisualStyle from ResourceDictionary. if (fvs == FrameworkElement.DefaultFocusVisualStyle) { fvs = SystemResources.FindResourceInternal(SystemParameters.FocusVisualStyleKey) as Style; } if (fvs != null) { _focusVisualAdornerCache = new FocusVisualAdorner(fce, parentUIElement, parentICH, fvs); adornerlayer.Add(_focusVisualAdornerCache); } } } } } } } private FocusVisualAdorner _focusVisualAdornerCache = null; #endregion FocusVisualStyle API #region Navigate helpers internal static void UpdateFocusedElement(DependencyObject focusTarget) { DependencyObject focusScope = FocusManager.GetFocusScope(focusTarget); if (focusScope != null && focusScope != focusTarget) { FocusManager.SetFocusedElement(focusScope, focusTarget as IInputElement); // Raise FocusEnterMainFocusScope event Visual visualRoot = GetVisualRoot(focusTarget); if (visualRoot != null && focusScope == visualRoot) { Current.NotifyFocusEnterMainFocusScope(visualRoot, EventArgs.Empty); } } } internal void UpdateActiveElement(DependencyObject activeElement) { // Update TabNavigation = Once groups UpdateActiveElement(activeElement, TabNavigationProperty); // Update ControlTabNavigation = Once groups UpdateActiveElement(activeElement, ControlTabNavigationProperty); } private void UpdateActiveElement(DependencyObject activeElement, DependencyProperty dp) { _navigationProperty = dp; DependencyObject container = GetGroupParent(activeElement); if (activeElement == container) return; // Update ActiveElement only if container has TabNavigation = Once if (GetKeyNavigationMode(container) == KeyboardNavigationMode.Once) { SetActiveElement(container, activeElement); } } // Called from FrameworkElement.MoveFocus internal bool Navigate(DependencyObject currentElement, TraversalRequest request) { return Navigate(currentElement, request, Keyboard.Modifiers); } private bool Navigate(DependencyObject currentElement, TraversalRequest request, ModifierKeys modifierKeys) { return Navigate(currentElement, request, modifierKeys, null); } private bool Navigate(DependencyObject currentElement, TraversalRequest request, ModifierKeys modifierKeys, DependencyObject firstElement) { Debug.Assert(currentElement != null, "currentElement should not be null"); DependencyObject nextTab = null; IKeyboardInputSink inputSink = null; switch (request.FocusNavigationDirection) { case FocusNavigationDirection.Next: _navigationProperty = (modifierKeys & ModifierKeys.Control) == ModifierKeys.Control ? ControlTabNavigationProperty : TabNavigationProperty; nextTab = GetNextTab(currentElement, GetGroupParent(currentElement, true /*includeCurrent*/), false); break; case FocusNavigationDirection.Previous: _navigationProperty = (modifierKeys & ModifierKeys.Control) == ModifierKeys.Control ? ControlTabNavigationProperty : TabNavigationProperty; nextTab = GetPrevTab(currentElement, null, false); break; case FocusNavigationDirection.First: _navigationProperty = (modifierKeys & ModifierKeys.Control) == ModifierKeys.Control ? ControlTabNavigationProperty : TabNavigationProperty; nextTab = GetNextTab(null, currentElement, true); break; case FocusNavigationDirection.Last: _navigationProperty = (modifierKeys & ModifierKeys.Control) == ModifierKeys.Control ? ControlTabNavigationProperty : TabNavigationProperty; nextTab = GetPrevTab(null, currentElement, true); break; case FocusNavigationDirection.Left: case FocusNavigationDirection.Right: case FocusNavigationDirection.Up: case FocusNavigationDirection.Down: _navigationProperty = DirectionalNavigationProperty; nextTab = GetNextInDirection(currentElement, request.FocusNavigationDirection); break; } // If there are no other tabstops, try to pass focus outside PresentationSource if (nextTab == null) { // If Wrapped is true we should not searach outside this container if (request.Wrapped || request.FocusNavigationDirection == FocusNavigationDirection.First || request.FocusNavigationDirection == FocusNavigationDirection.Last) return false; // Try to navigate outside the PresentationSource bool navigatedOutside = NavigateOutsidePresentationSource(currentElement, request); if (navigatedOutside) { return true; } else if (request.FocusNavigationDirection == FocusNavigationDirection.Next || request.FocusNavigationDirection == FocusNavigationDirection.Previous) { // In case focus cannot navigate outside - we should cycle Visual visualRoot = GetVisualRoot(currentElement); if (visualRoot != null) return Navigate(visualRoot, new TraversalRequest(request.FocusNavigationDirection == FocusNavigationDirection.Next ? FocusNavigationDirection.First : FocusNavigationDirection.Last)); } return false; } inputSink = nextTab as IKeyboardInputSink; if (inputSink == null) { // If target element does not support IKeyboardInputSink then we try to set focus // In TextBox scenario Focus() return false although the focus is set to TextBox content // So we need to verify IsKeyboardFocusWithin property (bugfix 954000) IInputElement iie = nextTab as IInputElement; iie.Focus(); return iie.IsKeyboardFocusWithin; } else { // If target element supports IKeyboardInputSink then we pass the focus there bool traversed = false; if (request.FocusNavigationDirection == FocusNavigationDirection.First || request.FocusNavigationDirection == FocusNavigationDirection.Next) { traversed = inputSink.TabInto(new TraversalRequest(FocusNavigationDirection.First)); } else if (request.FocusNavigationDirection == FocusNavigationDirection.Last || request.FocusNavigationDirection == FocusNavigationDirection.Previous) { traversed = inputSink.TabInto(new TraversalRequest(FocusNavigationDirection.Last)); } else // FocusNavigationDirection { TraversalRequest tr = new TraversalRequest(request.FocusNavigationDirection); tr.Wrapped = true; traversed = inputSink.TabInto(tr); } // If we fail to navigate into IKeyboardInputSink then move to the next element if (!traversed && firstElement != nextTab) { // Navigate to next element in the tree traversed = Navigate(nextTab, request, modifierKeys, firstElement == null ? nextTab : firstElement); } return traversed; } } ////// Critical: This code accesses PresentationSource. /// TreatAsSafe: This code causes navigation to different elements within an app. /// It does not expose the PresentationSource /// Critical: Asserting UnmanagedCode permission to obtain HwndSource.IKeyboardInputSink.KeyboardInputSite /// TreatAsSafe: Not leaking the InputKeyboardSite obtained under elevation. /// [SecurityCritical, SecurityTreatAsSafe] private bool NavigateOutsidePresentationSource(DependencyObject currentElement, TraversalRequest request) { Visual visual = currentElement as Visual; if (visual == null) { visual = GetParentUIElementFromContentElement(currentElement as ContentElement); if (visual == null) return false; } IKeyboardInputSink inputSink = PresentationSource.CriticalFromVisual(visual) as IKeyboardInputSink; if (inputSink != null) { IKeyboardInputSite ikis = null; new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // BlessedAssert try { ikis = inputSink.KeyboardInputSite; } finally { CodeAccessPermission.RevertAssert(); } if (ikis != null) return ikis.OnNoMoreTabStops(request); } return false; } internal static KeyboardNavigation Current { get { return FrameworkElement.KeyboardNavigation; } } ///////////////////////////////////////////////////////////////////// ////// Critical: accesses e.StagingItem.Input and asserts to retrieve HwndSource /// [SecurityCritical] private void PostProcessInput(object sender, ProcessInputEventArgs e) { // Call Forwarded ProcessInput(e.StagingItem.Input); } ///////////////////////////////////////////////////////////////////// ////// Critical: asserts to retrieve HwndSource /// [SecurityCritical] private void TranslateAccelerator(object sender, KeyEventArgs e) { // Call Forwarded ProcessInput(e); } ///////////////////////////////////////////////////////////////////// ////// Critical: asserts to retrieve HwndSource /// [SecurityCritical] private void ProcessInput(InputEventArgs inputEventArgs) { ProcessForMenuMode(inputEventArgs); ProcessForUIState(inputEventArgs); // Process keyboard navigation for keydown event for Tab,Left,Right,Up,Down keys. if(inputEventArgs.RoutedEvent != Keyboard.KeyDownEvent) return; KeyEventArgs keyEventArgs = (KeyEventArgs)inputEventArgs; if (keyEventArgs.Handled) return; DependencyObject sourceElement = keyEventArgs.OriginalSource as DependencyObject; // For Keyboard Interop with Avalon-inside-Avalon via HwndHost. // In Keyboard interop, the target (called OriginalSource here) is "forced" // to point at the HwndHost containing the Hwnd with focus. This allows // us to tunnel/bubble the keystroke across the outer HwndSource to the // child hwnd that has focus. (see HwndSource.TranslateAccelerator) // But this "forced" target is wrong for Tab Navigation; eg. tabbing // across an inner avalon under the HwndHost. For that we need the // real original target element, which we happen to find in KeyboardDevice.Target. // // sourceElement and innerElement I don't expect will ever be different // except in this case. And I added a check that the "forced" target // is an HwndHost for good measure. DependencyObject innerElement = keyEventArgs.KeyboardDevice.Target as DependencyObject; if( innerElement != null && sourceElement != innerElement ) { if(sourceElement is HwndHost) sourceElement = innerElement; } // When nothing has focus - we should start from the root of the visual tree if (sourceElement == null) { HwndSource hwndSource = keyEventArgs.UnsafeInputSource as HwndSource; if (hwndSource == null) return; sourceElement = hwndSource.RootVisual; if (sourceElement == null) return; } // Focus visual support switch (GetRealKey(keyEventArgs)) { case Key.LeftAlt: case Key.RightAlt: ShowFocusVisual(); EnableKeyboardCues(sourceElement, true); break; case Key.Tab: case Key.Right: case Key.Left: case Key.Up: case Key.Down: ShowFocusVisual(); break; } keyEventArgs.Handled = Navigate(sourceElement, keyEventArgs.Key, keyEventArgs.KeyboardDevice.Modifiers); } internal static void EnableKeyboardCues(DependencyObject element, bool enable) { Visual visual = element as Visual; if (visual == null) { visual = GetParentUIElementFromContentElement(element as ContentElement); if (visual == null) return; } Visual rootVisual = GetVisualRoot(visual); if (rootVisual != null) { rootVisual.SetValue(ShowKeyboardCuesProperty, enable ? BooleanBoxes.TrueBox : BooleanBoxes.FalseBox); } } internal static FocusNavigationDirection KeyToTraversalDirection(Key key) { switch (key) { case Key.Left: return FocusNavigationDirection.Left; case Key.Right: return FocusNavigationDirection.Right; case Key.Up: return FocusNavigationDirection.Up; case Key.Down: return FocusNavigationDirection.Down; } throw new NotSupportedException(); } internal DependencyObject PredictFocusedElement(DependencyObject sourceElement, FocusNavigationDirection direction) { if (sourceElement == null) { return null; } _navigationProperty = DirectionalNavigationProperty; _verticalBaseline = BASELINE_DEFAULT; _horizontalBaseline = BASELINE_DEFAULT; return GetNextInDirection(sourceElement, direction); } internal bool Navigate(DependencyObject sourceElement, Key key, ModifierKeys modifiers) { bool success = false; switch (key) { // Logical (Tab) navigation case Key.Tab: success = Navigate(sourceElement, new TraversalRequest(((modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) ? FocusNavigationDirection.Previous : FocusNavigationDirection.Next), modifiers); break; case Key.Right: success = Navigate(sourceElement, new TraversalRequest(FocusNavigationDirection.Right), modifiers); break; case Key.Left: success = Navigate(sourceElement, new TraversalRequest(FocusNavigationDirection.Left), modifiers); break; case Key.Up: success = Navigate(sourceElement, new TraversalRequest(FocusNavigationDirection.Up), modifiers); break; case Key.Down: success = Navigate(sourceElement, new TraversalRequest(FocusNavigationDirection.Down), modifiers); break; } return success; } #endregion Navigate helpers #region Tree navigation // Filter the visual tree and return true if: // 1. visual is visible UIElement // 2. visual is visible UIElement3D // 3. visual is IContentHost but not UIElementIsland // Note: UIElementIsland is a special element that has only one child and should be excluded private bool IsInNavigationTree(DependencyObject visual) { UIElement uiElement = visual as UIElement; if (uiElement != null && uiElement.IsVisible) return true; if (visual is IContentHost && !(visual is MS.Internal.Documents.UIElementIsland)) return true; UIElement3D uiElement3D = visual as UIElement3D; if (uiElement3D != null && uiElement3D.IsVisible) return true; return false; } private DependencyObject GetPreviousSibling(DependencyObject e) { DependencyObject parent = GetParent(e); // If parent is IContentHost - get next from the enumerator IContentHost ich = parent as IContentHost; if (ich != null) { IInputElement previousElement = null; IEnumeratorenumerator = ich.HostedElements; while (enumerator.MoveNext()) { IInputElement current = enumerator.Current; if (current == e) return previousElement as DependencyObject; if (current is UIElement || current is UIElement3D) previousElement = current; else { ContentElement ce = current as ContentElement; if (ce != null && IsTabStop(ce)) previousElement = current; } } return null; } else { // If parent is UIElement(3D) - return visual sibling DependencyObject parentAsUIElement = parent as UIElement; if (parentAsUIElement == null) { parentAsUIElement = parent as UIElement3D; } DependencyObject elementAsVisual = e as Visual; if (elementAsVisual == null) { elementAsVisual = e as Visual3D; } if (parentAsUIElement != null && elementAsVisual != null) { int count = VisualTreeHelper.GetChildrenCount(parentAsUIElement); DependencyObject prev = null; for(int i = 0; i < count; i++) { DependencyObject vchild = VisualTreeHelper.GetChild(parentAsUIElement, i); if(vchild == elementAsVisual) break; if (IsInNavigationTree(vchild)) prev = vchild; } return prev; } } return null; } private DependencyObject GetNextSibling(DependencyObject e) { DependencyObject parent = GetParent(e); // If parent is IContentHost - get next from the enumerator IContentHost ich = parent as IContentHost; if (ich != null) { IEnumerator enumerator = ich.HostedElements; bool found = false; while (enumerator.MoveNext()) { IInputElement current = enumerator.Current; if (found) { if (current is UIElement || current is UIElement3D) return current as DependencyObject; else { ContentElement ce = current as ContentElement; if (ce != null && IsTabStop(ce)) return ce; } } else if (current == e) { found = true; } } } else { // If parent is UIElement(3D) - return visual sibling DependencyObject parentAsUIElement = parent as UIElement; if (parentAsUIElement == null) { parentAsUIElement = parent as UIElement3D; } DependencyObject elementAsVisual = e as Visual; if (elementAsVisual == null) { elementAsVisual = e as Visual3D; } if (parentAsUIElement != null && elementAsVisual != null) { int count = VisualTreeHelper.GetChildrenCount(parentAsUIElement); int i = 0; //go till itself for(; i < count; i++) { DependencyObject vchild = VisualTreeHelper.GetChild(parentAsUIElement, i); if(vchild == elementAsVisual) break; } i++; //search ahead for(; i < count; i++) { DependencyObject visual = VisualTreeHelper.GetChild(parentAsUIElement, i); if (IsInNavigationTree(visual)) return visual; } } } return null; } // For Control+Tab navigation or TabNavigation when fe is not a FocusScope: // Scenarios: // 1. UserControl can set its FocusedElement to delegate focus when Tab navigation happens // 2. ToolBar or Menu (which have IsFocusScope=true) both have FocusedElement but included only in Control+Tab navigation private DependencyObject FocusedElement(DependencyObject e) { IInputElement iie = e as IInputElement; // Focus delegation is enabled only if keyboard focus is outside the container if (iie != null && !iie.IsKeyboardFocusWithin) { DependencyObject focusedElement = FocusManager.GetFocusedElement(e) as DependencyObject; if (focusedElement != null) { if (_navigationProperty == ControlTabNavigationProperty || !IsFocusScope(e)) { // Verify if focusedElement is a visual descendant of e Visual visualFocusedElement = focusedElement as Visual; if (visualFocusedElement == null) { Visual3D visual3DFocusedElement = focusedElement as Visual3D; if (visual3DFocusedElement == null) { visualFocusedElement = GetParentUIElementFromContentElement(focusedElement as ContentElement); } else { if (visual3DFocusedElement.IsDescendantOf(e)) { return focusedElement; } } } if (visualFocusedElement.IsDescendantOf(e)) { return focusedElement; } } } } return null; } // We traverse only UIElement(3D) or ContentElement private DependencyObject GetFirstChild(DependencyObject e) { // If the element has a FocusedElement it should be its first child DependencyObject focusedElement = FocusedElement(e); if (focusedElement != null) return focusedElement; // If the element is IContentHost - return the first child IContentHost ich = e as IContentHost; if (ich != null) { IEnumerator enumerator = ich.HostedElements; while (enumerator.MoveNext()) { IInputElement current = enumerator.Current; if (current is UIElement || current is UIElement3D) { return current as DependencyObject; } else { ContentElement ce = current as ContentElement; if (ce != null && IsTabStop(ce)) return ce; } } return null; } // Return the first visible UIElement(3D) or IContentHost DependencyObject uiElement = e as UIElement; if (uiElement == null) { uiElement = e as UIElement3D; } if (uiElement == null || UIElementHelper.IsVisible(uiElement)) { DependencyObject elementAsVisual = e as Visual; if (elementAsVisual == null) { elementAsVisual = e as Visual3D; } if (elementAsVisual != null) { int count = VisualTreeHelper.GetChildrenCount(elementAsVisual); for (int i = 0; i < count; i++) { DependencyObject visual = VisualTreeHelper.GetChild(elementAsVisual, i); if (IsInNavigationTree(visual)) return visual; else { DependencyObject firstChild = GetFirstChild(visual); if (firstChild != null) return firstChild; } } } } // If element is ContentElement for example return null; } private DependencyObject GetLastChild(DependencyObject e) { // If the element has a FocusedElement it should be its last child DependencyObject focusedElement = FocusedElement(e); if (focusedElement != null) return focusedElement; // If the element is IContentHost - return the last child IContentHost ich = e as IContentHost; if (ich != null) { IEnumerator enumerator = ich.HostedElements; IInputElement last = null; while (enumerator.MoveNext()) { IInputElement current = enumerator.Current; if (current is UIElement || current is UIElement3D) last = current; else { ContentElement ce = current as ContentElement; if (ce != null && IsTabStop(ce)) last = current; } } return last as DependencyObject; } // Return the last visible UIElement(3D) or IContentHost DependencyObject uiElement = e as UIElement; if (uiElement == null) { uiElement = e as UIElement3D; } if (uiElement == null || UIElementHelper.IsVisible(uiElement)) { DependencyObject elementAsVisual = e as Visual; if (elementAsVisual == null) { elementAsVisual = e as Visual3D; } if (elementAsVisual != null) { int count = VisualTreeHelper.GetChildrenCount(elementAsVisual); for (int i = count - 1; i >= 0; i--) { DependencyObject visual = VisualTreeHelper.GetChild(elementAsVisual, i); if (IsInNavigationTree(visual)) return visual; else { DependencyObject lastChild = GetLastChild(visual); if (lastChild != null) return lastChild; } } } } return null; } private DependencyObject GetParent(DependencyObject e) { // For Visual - go up the visual parent chain until we find Visual, Visual3D or IContentHost if (e is Visual || e is Visual3D) { DependencyObject visual = e; while ((visual = VisualTreeHelper.GetParent(visual)) != null) { // if (IsInNavigationTree(visual)) return visual; } } else { // For ContentElement - return the host element (which is IContentHost) ContentElement contentElement = e as ContentElement; if (contentElement != null) { return MS.Internal.Documents.ContentHostHelper.FindContentHost(contentElement) as DependencyObject; } } return null; } /***************************************************************************\ * * GetNextInTree(DependencyObject e, DependencyObject container) * Search the subtree with container root; Don't go inside TabGroups * * Return the next Element in tree in depth order (self-child-sibling). * 1 * / \ * 2 5 * / \ * 3 4 * \***************************************************************************/ private DependencyObject GetNextInTree(DependencyObject e, DependencyObject container) { Debug.Assert(e != null, "e should not be null"); Debug.Assert(container != null, "container should not be null"); DependencyObject result = null; if (e == container || !IsGroup(e)) result = GetFirstChild(e); if (result != null || e == container) return result; DependencyObject parent = e; do { DependencyObject sibling = GetNextSibling(parent); if (sibling != null) return sibling; parent = GetParent(parent); } while (parent != null && parent != container); return null; } /***************************************************************************\ * * GetPreviousInTree(DependencyObject e, DependencyObject container) * Don't go inside TabGroups * Return the previous Element in tree in depth order (self-child-sibling). * 5 * / \ * 4 1 * / \ * 3 2 \***************************************************************************/ private DependencyObject GetPreviousInTree(DependencyObject e, DependencyObject container) { if (e == container) return null; DependencyObject result = GetPreviousSibling(e); if (result != null) { if (IsGroup(result)) return result; else return GetLastInTree(result); } else return GetParent(e); } // Find the last element in the subtree private DependencyObject GetLastInTree(DependencyObject container) { DependencyObject result; do { result = container; container = GetLastChild(container); } while (container != null && !IsGroup(container)); if (container != null) return container; return result; } private DependencyObject GetGroupParent(DependencyObject e) { return GetGroupParent(e, false /*includeCurrent*/); } // Go up thru the parent chain until we find TabNavigation != Continue // In case all parents are Continue then return the root private DependencyObject GetGroupParent(DependencyObject e, bool includeCurrent) { Debug.Assert(e != null, "e cannot be null"); DependencyObject result = e; // Keep the last non null element // If we don't want to include the current element, // start at the parent of the element. If the element // is the root, then just return it as the group parent. if (!includeCurrent) { result = e; e = GetParent(e); if (e == null) { return result; } } while (e != null) { if (IsGroup(e)) return e; result = e; e = GetParent(e); } return result; } #endregion Tree navigation #region Logical Navigation private bool IsTabStop(DependencyObject e) { FrameworkElement fe = e as FrameworkElement; if (fe != null) return (fe.Focusable && (bool)fe.GetValue(IsTabStopProperty)) && fe.IsEnabled && fe.IsVisible; FrameworkContentElement fce = e as FrameworkContentElement; return fce != null && fce.Focusable && (bool)fce.GetValue(IsTabStopProperty) && fce.IsEnabled; } private bool IsGroup(DependencyObject e) { return GetKeyNavigationMode(e) != KeyboardNavigationMode.Continue; } private KeyboardNavigationMode GetKeyNavigationMode(DependencyObject e) { return (KeyboardNavigationMode)e.GetValue(_navigationProperty); } private bool IsTabStopOrGroup(DependencyObject e) { return IsTabStop(e) || IsGroup(e); } private static int GetTabIndexHelper(DependencyObject d) { return (int)d.GetValue(TabIndexProperty); } #region Tab Navigation // Find the element with highest priority (lowest index) inside the group private DependencyObject GetFirstTabInGroup(DependencyObject container) { DependencyObject firstTabElement = null; int minIndexFirstTab = Int32.MinValue; DependencyObject currElement = container; while ((currElement = GetNextInTree(currElement, container)) != null) { if (IsTabStopOrGroup(currElement)) { int currPriority = GetTabIndexHelper(currElement); if (currPriority < minIndexFirstTab || firstTabElement == null) { minIndexFirstTab = currPriority; firstTabElement = currElement; } } } return firstTabElement; } // Find the element with the same TabIndex after the current element private DependencyObject GetNextTabWithSameIndex(DependencyObject e, DependencyObject container) { int elementTabPriority = GetTabIndexHelper(e); DependencyObject currElement = e; while ((currElement = GetNextInTree(currElement, container)) != null) { if (IsTabStopOrGroup(currElement) && GetTabIndexHelper(currElement) == elementTabPriority) { return currElement; } } return null; } // Find the element with the next TabIndex after the current element private DependencyObject GetNextTabWithNextIndex(DependencyObject e, DependencyObject container, KeyboardNavigationMode tabbingType) { // Find the next min index in the tree // min (index>currentTabIndex) DependencyObject nextTabElement = null; DependencyObject firstTabElement = null; int minIndexFirstTab = Int32.MinValue; int minIndex = Int32.MinValue; int elementTabPriority = GetTabIndexHelper(e); DependencyObject currElement = container; while ((currElement = GetNextInTree(currElement, container)) != null) { if (IsTabStopOrGroup(currElement)) { int currPriority = GetTabIndexHelper(currElement); if (currPriority > elementTabPriority) { if (currPriority < minIndex || nextTabElement == null) { minIndex = currPriority; nextTabElement = currElement; } } if (currPriority < minIndexFirstTab || firstTabElement == null) { minIndexFirstTab = currPriority; firstTabElement = currElement; } } } // Cycle groups: if not found - return first element if (tabbingType == KeyboardNavigationMode.Cycle && nextTabElement == null) nextTabElement = firstTabElement; return nextTabElement; } private DependencyObject GetNextTabInGroup(DependencyObject e, DependencyObject container, KeyboardNavigationMode tabbingType) { // None groups: Tab navigation is not supported if (tabbingType == KeyboardNavigationMode.None) return null; // e == null or e == container -> return the first TabStopOrGroup if (e == null || e == container) { return GetFirstTabInGroup(container); } if (tabbingType == KeyboardNavigationMode.Once) return null; DependencyObject nextTabElement = GetNextTabWithSameIndex(e, container); if (nextTabElement != null) return nextTabElement; return GetNextTabWithNextIndex(e, container, tabbingType); } private DependencyObject GetNextTab(DependencyObject e, DependencyObject container, bool goDownOnly) { Debug.Assert(container != null, "container should not be null"); KeyboardNavigationMode tabbingType = GetKeyNavigationMode(container); if (e == null) { if (IsTabStop(container)) return container; // Using ActiveElement if set DependencyObject activeElement = GetActiveElement(container); if (activeElement != null) return GetNextTab(null, activeElement, true); } else { if (tabbingType == KeyboardNavigationMode.Once || tabbingType == KeyboardNavigationMode.None) { if (container != e) { if (goDownOnly) return null; DependencyObject parentContainer = GetGroupParent(container); return GetNextTab(container, parentContainer, goDownOnly); } } } // All groups DependencyObject loopStartElement = null; DependencyObject nextTabElement = e; KeyboardNavigationMode currentTabbingType = tabbingType; // Search down inside the container while ((nextTabElement = GetNextTabInGroup(nextTabElement, container, currentTabbingType)) != null) { Debug.Assert(IsTabStopOrGroup(nextTabElement), "nextTabElement should be IsTabStop or group"); // Avoid the endless loop here for Cycle groups if (loopStartElement == nextTabElement) break; if (loopStartElement == null) loopStartElement = nextTabElement; DependencyObject firstTabElementInside = GetNextTab(null, nextTabElement, true); if (firstTabElementInside != null) return firstTabElementInside; // If we want to continue searching inside the Once groups, we should change the navigation mode if (currentTabbingType == KeyboardNavigationMode.Once) currentTabbingType = KeyboardNavigationMode.Contained; } // If there is no next element in the group (nextTabElement == null) // Search up in the tree if allowed // if (!goDownOnly && currentTabbingType != KeyboardNavigationMode.Contained && GetParent(container) != null) { return GetNextTab(container, GetGroupParent(container), false); } return null; } #endregion Tab Navigation #region Shift+Tab Navigation private DependencyObject GetLastTabInGroup(DependencyObject container) { DependencyObject lastTabElement = null; int maxIndexFirstTab = Int32.MaxValue; DependencyObject currElement = GetLastInTree(container); while (currElement != null && currElement != container) { if (IsTabStopOrGroup(currElement)) { int currPriority = GetTabIndexHelper(currElement); if (currPriority > maxIndexFirstTab || lastTabElement == null) { maxIndexFirstTab = currPriority; lastTabElement = currElement; } } currElement = GetPreviousInTree(currElement, container); } return lastTabElement; } // Look for element with the same TabIndex before the current element private DependencyObject GetPrevTabWithSameIndex(DependencyObject e, DependencyObject container) { int elementTabPriority = GetTabIndexHelper(e); DependencyObject currElement = GetPreviousInTree(e, container); while (currElement != null) { if (IsTabStopOrGroup(currElement) && GetTabIndexHelper(currElement) == elementTabPriority && currElement != container) { return currElement; } currElement = GetPreviousInTree(currElement, container); } return null; } private DependencyObject GetPrevTabWithPrevIndex(DependencyObject e, DependencyObject container, KeyboardNavigationMode tabbingType) { // Find the next max index in the tree // max (index maxIndex || nextTabElement == null) { maxIndex = currPriority; nextTabElement = currElement; } } if (currPriority > maxIndexFirstTab || lastTabElement == null) { maxIndexFirstTab = currPriority; lastTabElement = currElement; } } currElement = GetPreviousInTree(currElement, container); } // Cycle groups: if not found - return first element if (tabbingType == KeyboardNavigationMode.Cycle && nextTabElement == null) nextTabElement = lastTabElement; return nextTabElement; } private DependencyObject GetPrevTabInGroup(DependencyObject e, DependencyObject container, KeyboardNavigationMode tabbingType) { // None groups: Tab navigation is not supported if (tabbingType == KeyboardNavigationMode.None) return null; // Search the last index inside the group if (e==null) { return GetLastTabInGroup(container); } if (tabbingType == KeyboardNavigationMode.Once) return null; if (e == container) return null; DependencyObject nextTabElement = GetPrevTabWithSameIndex(e, container); if (nextTabElement != null) return nextTabElement; return GetPrevTabWithPrevIndex(e, container, tabbingType); } private DependencyObject GetPrevTab(DependencyObject e, DependencyObject container, bool goDownOnly) { Debug.Assert(e != null || container != null, "e or container should not be null"); if (container == null) container = GetGroupParent(e); KeyboardNavigationMode tabbingType = GetKeyNavigationMode(container); if (e == null) { // Using ActiveElement if set DependencyObject activeElement = GetActiveElement(container); if (activeElement != null) return GetPrevTab(null, activeElement, true); else { // If we Shift+Tab on a container with KeyboardNavigationMode=Once, and ActiveElement is null // then we want to go to the fist item (not last) within the container if (tabbingType == KeyboardNavigationMode.Once) { DependencyObject firstTabElement = GetNextTabInGroup(null, container, tabbingType); if (firstTabElement == null) { if (IsTabStop(container)) return container; if (goDownOnly) return null; return GetPrevTab(container, null, false); } else { return GetPrevTab(null, firstTabElement, true); } } } } else { if (tabbingType == KeyboardNavigationMode.Once || tabbingType == KeyboardNavigationMode.None) { if (goDownOnly || container==e) return null; // FocusedElement should not be e otherwise we will delegate focus to the same element if (IsTabStop(container)) return container; return GetPrevTab(container, null, false); } } // All groups (except Once) - continue DependencyObject loopStartElement = null; DependencyObject nextTabElement = e; // Look for element with the same TabIndex before the current element while ((nextTabElement = GetPrevTabInGroup(nextTabElement, container, tabbingType)) != null) { if (nextTabElement == container && tabbingType == KeyboardNavigationMode.Local) break; // At this point nextTabElement is TabStop or TabGroup // In case it is a TabStop only return the element if (IsTabStop(nextTabElement) && !IsGroup(nextTabElement)) return nextTabElement; // Avoid the endless loop here if (loopStartElement == nextTabElement) break; if (loopStartElement == null) loopStartElement = nextTabElement; // At this point nextTabElement is TabGroup DependencyObject lastTabElementInside = GetPrevTab(null, nextTabElement, true); if (lastTabElementInside != null) return lastTabElementInside; } if (tabbingType == KeyboardNavigationMode.Contained) return null; if (e != container && IsTabStop(container)) return container; // If end of the subtree is reached or there no other elements above if (!goDownOnly && GetParent(container) != null) { return GetPrevTab(container, null, false); } return null; } #endregion Shift+Tab Navigation #endregion Logical Navigation #region Directional Navigation // return the element rectange relative to the root internal static Rect GetRectangle(DependencyObject element) { UIElement uiElement = element as UIElement; if (uiElement != null && uiElement.IsArrangeValid) { Visual rootVisual = GetVisualRoot(uiElement); if (rootVisual != null) { GeneralTransform transform = uiElement.TransformToAncestor(rootVisual); Thickness deflateThickness = (Thickness)uiElement.GetValue(DirectionalNavigationMarginProperty); double x = -deflateThickness.Left; double y = -deflateThickness.Top; double width = uiElement.RenderSize.Width + deflateThickness.Left + deflateThickness.Right; double height = uiElement.RenderSize.Height + deflateThickness.Top + deflateThickness.Bottom; if (width < 0) { x = uiElement.RenderSize.Width * 0.5; width = 0d; } if (height < 0) { y = uiElement.RenderSize.Height * 0.5; height = 0d; } return transform.TransformBounds(new Rect(x, y, width, height)); } } else { ContentElement ce = element as ContentElement; if (ce != null) { IContentHost parentICH = null; UIElement parentUIElement = GetParentUIElementFromContentElement(ce, ref parentICH); Visual parent = parentICH as Visual; if (parentICH != null && parent != null && parentUIElement != null) { Visual rootVisual = GetVisualRoot(parent); if (rootVisual != null && parentUIElement.IsMeasureValid) { // Note: Here we consider only the fist rectangle // Do we need to consider all of them as one combined rectangle? ReadOnlyCollection rects = parentICH.GetRectangles(ce); IEnumerator enumerator = rects.GetEnumerator(); if (enumerator.MoveNext()) { GeneralTransform transform = parent.TransformToAncestor(rootVisual); Rect rect = enumerator.Current; return transform.TransformBounds(rect); } } } } else { UIElement3D uiElement3D = element as UIElement3D; if (uiElement3D != null) { Visual rootVisual = GetVisualRoot(uiElement3D); Visual containingVisual2D = VisualTreeHelper.GetContainingVisual2D(uiElement3D); if (rootVisual != null && containingVisual2D != null) { Rect rectElement = uiElement3D.Visual2DContentBounds; GeneralTransform transform = containingVisual2D.TransformToAncestor(rootVisual); return transform.TransformBounds(rectElement); } } } } return Rect.Empty; } // distance between two points private double GetDistance(Point p1, Point p2) { double deltaX = p1.X - p2.X; double deltaY = p1.Y - p2.Y; return Math.Sqrt(deltaX * deltaX + deltaY * deltaY); } private double GetPerpDistance(Rect sourceRect, Rect targetRect, FocusNavigationDirection direction) { switch (direction) { case FocusNavigationDirection.Right : return targetRect.Left - sourceRect.Left; case FocusNavigationDirection.Left : return sourceRect.Right - targetRect.Right; case FocusNavigationDirection.Up : return sourceRect.Bottom - targetRect.Bottom; case FocusNavigationDirection.Down : return targetRect.Top - sourceRect.Top; default : throw new System.ComponentModel.InvalidEnumArgumentException("direction", (int)direction, typeof(FocusNavigationDirection)); } } // Example when moving down: // distance between sourceRect.TopLeft (or Y=vertical baseline) // and targetRect.TopLeft private double GetDistance(Rect sourceRect, Rect targetRect, FocusNavigationDirection direction) { Point startPoint; Point endPoint; switch (direction) { case FocusNavigationDirection.Right : startPoint = sourceRect.TopLeft; if (_horizontalBaseline != BASELINE_DEFAULT) startPoint.Y = _horizontalBaseline; endPoint = targetRect.TopLeft; break; case FocusNavigationDirection.Left : startPoint = sourceRect.TopRight; if (_horizontalBaseline != BASELINE_DEFAULT) startPoint.Y = _horizontalBaseline; endPoint = targetRect.TopRight; break; case FocusNavigationDirection.Up : startPoint = sourceRect.BottomLeft; if (_verticalBaseline != BASELINE_DEFAULT) startPoint.X = _verticalBaseline; endPoint = targetRect.BottomLeft; break; case FocusNavigationDirection.Down : startPoint = sourceRect.TopLeft; if (_verticalBaseline != BASELINE_DEFAULT) startPoint.X = _verticalBaseline; endPoint = targetRect.TopLeft; break; default : throw new System.ComponentModel.InvalidEnumArgumentException("direction", (int)direction, typeof(FocusNavigationDirection)); } return GetDistance(startPoint, endPoint); } // Example when moving down: // true if the top of the toRect is below the bottom of fromRect private bool IsInDirection(Rect fromRect, Rect toRect, FocusNavigationDirection direction) { switch (direction) { case FocusNavigationDirection.Right: return DoubleUtil.LessThanOrClose(fromRect.Right, toRect.Left); case FocusNavigationDirection.Left: return DoubleUtil.GreaterThanOrClose(fromRect.Left, toRect.Right); case FocusNavigationDirection.Up : return DoubleUtil.GreaterThanOrClose(fromRect.Top, toRect.Bottom); case FocusNavigationDirection.Down : return DoubleUtil.LessThanOrClose(fromRect.Bottom, toRect.Top); default: throw new System.ComponentModel.InvalidEnumArgumentException("direction", (int)direction, typeof(FocusNavigationDirection)); } } // The element is focus scope if IsFocusScope is true or it is the visual tree root private bool IsFocusScope(DependencyObject e) { return FocusManager.GetIsFocusScope(e) || GetParent(e) == null; } private bool IsAncestorOf(DependencyObject sourceElement, DependencyObject targetElement) { Visual sourceVisual = sourceElement as Visual; Visual targetVisual = targetElement as Visual; if (sourceVisual == null || targetVisual == null) return false; return sourceVisual.IsAncestorOf(targetVisual); } // Example: When moving down: // Range is the sourceRect width extended to the vertical baseline // targetRect.Top > sourceRect.Top (target is below the source) // targetRect.Right > sourceRect.Left || targetRect.Left < sourceRect.Right private bool IsInRange(DependencyObject sourceElement, DependencyObject targetElement, Rect sourceRect, Rect targetRect, FocusNavigationDirection direction, double startRange, double endRange) { switch (direction) { case FocusNavigationDirection.Right : case FocusNavigationDirection.Left : if (_horizontalBaseline != BASELINE_DEFAULT) { startRange = Math.Min(startRange, _horizontalBaseline); endRange = Math.Max(endRange, _horizontalBaseline); } if (DoubleUtil.GreaterThan(targetRect.Bottom, startRange) && DoubleUtil.LessThan(targetRect.Top, endRange)) { // If there is no sourceElement - checking the range is enough if (sourceElement == null) return true; if (direction == FocusNavigationDirection.Right) return DoubleUtil.GreaterThan(targetRect.Left, sourceRect.Left) || (DoubleUtil.AreClose(targetRect.Left, sourceRect.Left) && IsAncestorOf(sourceElement, targetElement)); else return DoubleUtil.LessThan(targetRect.Right, sourceRect.Right) || (DoubleUtil.AreClose(targetRect.Right, sourceRect.Right) && IsAncestorOf(sourceElement, targetElement)); } break; case FocusNavigationDirection.Up : case FocusNavigationDirection.Down : if (_verticalBaseline != BASELINE_DEFAULT) { startRange = Math.Min(startRange, _verticalBaseline); endRange = Math.Max(endRange, _verticalBaseline); } if (DoubleUtil.GreaterThan(targetRect.Right, startRange) && DoubleUtil.LessThan(targetRect.Left, endRange)) { // If there is no sourceElement - checking the range is enough if (sourceElement == null) return true; if (direction == FocusNavigationDirection.Down) return DoubleUtil.GreaterThan(targetRect.Top, sourceRect.Top) || (DoubleUtil.AreClose (targetRect.Top, sourceRect.Top) && IsAncestorOf(sourceElement, targetElement)); else return DoubleUtil.LessThan(targetRect.Bottom, sourceRect.Bottom) || (DoubleUtil.AreClose(targetRect.Bottom, sourceRect.Bottom) && IsAncestorOf(sourceElement, targetElement)); } break; default : throw new System.ComponentModel.InvalidEnumArgumentException("direction", (int)direction, typeof(FocusNavigationDirection)); } return false; } private DependencyObject GetNextInDirection(DependencyObject sourceElement, FocusNavigationDirection direction) { _containerHashtable.Clear(); DependencyObject targetElement = MoveNext(sourceElement, null, direction, BASELINE_DEFAULT, BASELINE_DEFAULT); if (targetElement != null) { UIElement sourceUIElement = sourceElement as UIElement; if (sourceUIElement != null) sourceUIElement.RemoveHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus)); else { ContentElement sourceContentElement = sourceElement as ContentElement; if (sourceContentElement != null) sourceContentElement.RemoveHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus)); } UIElement targetUIElement = targetElement as UIElement; if (targetUIElement == null) targetUIElement = GetParentUIElementFromContentElement(targetElement as ContentElement); else { ContentElement targetContentElement = targetElement as ContentElement; if (targetContentElement != null) { // When Focus is changed we need to reset the base line targetContentElement.AddHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus), true); } } if (targetUIElement != null) { // When layout is changed we need to reset the base line // Set up a layout invalidation listener. targetUIElement.LayoutUpdated += new EventHandler(OnLayoutUpdated); // When Focus is changed we need to reset the base line if (targetElement == targetUIElement) targetUIElement.AddHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus), true); } } _containerHashtable.Clear(); return targetElement; } // LayoutUpdated handler. private void OnLayoutUpdated(object sender, EventArgs e) { UIElement uiElement = sender as UIElement; // Disconnect the layout listener. if (uiElement != null) { uiElement.LayoutUpdated -= new EventHandler(OnLayoutUpdated); } _verticalBaseline = BASELINE_DEFAULT; _horizontalBaseline = BASELINE_DEFAULT; } private void _LostFocus(object sender, KeyboardFocusChangedEventArgs e) { _verticalBaseline = BASELINE_DEFAULT; _horizontalBaseline = BASELINE_DEFAULT; if (sender is UIElement) ((UIElement)sender).RemoveHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus)); else if (sender is ContentElement) ((ContentElement)sender).RemoveHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus)); } private bool IsEndlessLoop(DependencyObject element, DependencyObject container) { object elementObject = element != null ? (object)element : _fakeNull; // If entry exists then we have endless loop Hashtable elementTable = _containerHashtable[container] as Hashtable; if (elementTable != null) { if (elementTable[elementObject] != null) return true; } else { // Adding the entry to the collection elementTable = new Hashtable(10); _containerHashtable[container] = elementTable; } elementTable[elementObject] = BooleanBoxes.TrueBox; return false; } private void ResetBaseLines(double value, bool horizontalDirection) { if (horizontalDirection) { _verticalBaseline = BASELINE_DEFAULT; if (_horizontalBaseline == BASELINE_DEFAULT) _horizontalBaseline = value; } else // vertical direction { _horizontalBaseline = BASELINE_DEFAULT; if (_verticalBaseline == BASELINE_DEFAULT) _verticalBaseline = value; } } private DependencyObject FindNextInDirection(DependencyObject sourceElement, Rect sourceRect, DependencyObject container, FocusNavigationDirection direction, double startRange, double endRange) { DependencyObject result = null; Rect resultRect = Rect.Empty; double resultScore = 0d; bool searchInsideContainer = sourceElement == null; DependencyObject currElement = container; while ((currElement = GetNextInTree(currElement, container)) != null) { if (currElement != sourceElement && IsTabStopOrGroup(currElement)) { Rect currentRect = GetRectangle(currElement); bool isInDirection = IsInDirection(sourceRect, currentRect, direction); bool isInRange = IsInRange(sourceElement, currElement, sourceRect, currentRect, direction, startRange, endRange); if (searchInsideContainer || isInDirection || isInRange) { double score = isInRange ? GetPerpDistance(sourceRect, currentRect, direction) : GetDistance(sourceRect, currentRect, direction); // Keep the first element in the result if (result == null) { result = currElement; resultRect = currentRect; resultScore = score; } else if (DoubleUtil.LessThan(score, resultScore) || (DoubleUtil.AreClose(score, resultScore) && GetDistance(sourceRect, resultRect, direction) > GetDistance(sourceRect, currentRect, direction))) { result = currElement; resultRect = currentRect; resultScore = score; } } } } return result; } private DependencyObject MoveNext(DependencyObject sourceElement, DependencyObject container, FocusNavigationDirection direction, double startRange, double endRange) { Debug.Assert(!(sourceElement == null && container == null), "Both sourceElement and container cannot be null"); if (container == null) { container = GetGroupParent(sourceElement); Debug.Assert(container != null, "container cannot be null"); } // If we get to the tree root, return null if (container == sourceElement) return null; if (IsEndlessLoop(sourceElement, container)) return null; KeyboardNavigationMode mode = GetKeyNavigationMode(container); bool searchInsideContainer = (sourceElement == null); // Don't navigate inside None containers if (mode == KeyboardNavigationMode.None && searchInsideContainer) return null; Rect sourceRect = GetRectangle(searchInsideContainer ? container : sourceElement ); bool horizontalDirection = direction == FocusNavigationDirection.Right || direction == FocusNavigationDirection.Left; // Reset the baseline when we change the direction ResetBaseLines(horizontalDirection ? sourceRect.Top : sourceRect.Left, horizontalDirection); // If range is not set - use source rect if (startRange == BASELINE_DEFAULT || endRange == BASELINE_DEFAULT) { startRange = horizontalDirection ? sourceRect.Top : sourceRect.Left; endRange = horizontalDirection ? sourceRect.Bottom : sourceRect.Right; } // Navigate outside the container if (mode == KeyboardNavigationMode.Once && !searchInsideContainer) return MoveNext(container, null, direction, startRange, endRange); DependencyObject result = FindNextInDirection(sourceElement, sourceRect, container, direction, startRange, endRange); // If there is no next element in current container if (result == null) { switch (mode) { case KeyboardNavigationMode.Cycle: return MoveNext(null, container, direction, startRange, endRange); case KeyboardNavigationMode.Contained: return null; default: // Continue, Once, None, Local - search outside the container return MoveNext(container, null, direction, startRange, endRange); } } // If the element is focusable and IsTabStop is true if (IsTabStop(result)) return result; // Using ActiveElement if set DependencyObject activeElement = GetActiveElementChain(result); if (activeElement != null) return activeElement; // Try to find focus inside the element // result is not TabStop, which means it is a group DependencyObject insideElement = MoveNext(null, result, direction, startRange, endRange); if (insideElement != null) return insideElement; return MoveNext(result, null, direction, startRange, endRange); } private DependencyObject GetActiveElementChain(DependencyObject element) { DependencyObject validActiveElement = null; DependencyObject activeElement = element; while ((activeElement = GetActiveElement(activeElement)) != null) { if (IsTabStop(activeElement)) validActiveElement = activeElement; } return validActiveElement; } #endregion Directional Navigation #region Global tracking for entering MenuMode // ISSUE: how do we deal with deactivate? ///////////////////////////////////////////////////////////////////// /// /// Critical: accesses e.StagingItem.Input /// [SecurityCritical] private void ProcessForMenuMode(InputEventArgs inputEventArgs) { // When ALT or F10 key up happens we should fire the EnterMenuMode event. // We should not fire if: // * there were any handled input events in between the key down and corresponding key up. // * another unmatched keydown or keyup happened // * an unhandled mouse down/up happens if (inputEventArgs.RoutedEvent == Keyboard.LostKeyboardFocusEvent) { KeyboardFocusChangedEventArgs args = inputEventArgs as KeyboardFocusChangedEventArgs; if (((args != null) && (args.NewFocus == null)) || inputEventArgs.Handled) { // Focus went to null, stop tracking the last key down _lastKeyPressed = Key.None; } } // If a key is pressed down, remember it until the corresponding // key up. Ignore repeated keydowns. else if (inputEventArgs.RoutedEvent == Keyboard.KeyDownEvent) { if (inputEventArgs.Handled) _lastKeyPressed = Key.None; else { KeyEventArgs keyEventArgs = inputEventArgs as KeyEventArgs; if (!keyEventArgs.IsRepeat) { if (_lastKeyPressed == Key.None) { if ((Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Shift | ModifierKeys.Windows)) == ModifierKeys.None) { _lastKeyPressed = GetRealKey(keyEventArgs); } } else { // Another key was pressed down in between the one that we're tracking, so reset. _lastKeyPressed = Key.None; } // Clear this bit, Win32 will see message and clear QF_FMENUSTATUS. _win32MenuModeWorkAround = false; } } } // If a key up is received and matches the last key down // and is a key that would cause us to enter menumode, // raise the (internal) EnterMenuMode event. else if (inputEventArgs.RoutedEvent == Keyboard.KeyUpEvent) { if (!inputEventArgs.Handled) { KeyEventArgs keyEventArgs = inputEventArgs as KeyEventArgs; Key realKey = GetRealKey(keyEventArgs); if (realKey == _lastKeyPressed && IsMenuKey(realKey)) { EnableKeyboardCues(keyEventArgs.Source as DependencyObject, true); keyEventArgs.Handled = OnEnterMenuMode(keyEventArgs.Source); } if (_win32MenuModeWorkAround) { if (IsMenuKey(realKey)) { _win32MenuModeWorkAround = false; // Mark the event args as handled so that Win32 never // sees this key up and doesn't enter menu-mode. keyEventArgs.Handled = true; } } // If someone was listening for MenuMode and did something, // we need to make sure we don't let Win32 enter menu mode. else if (keyEventArgs.Handled) { // Set this bit to true, this means that we will handle // the next ALT-up if no one else does. _win32MenuModeWorkAround = true; } } // No matter what we should reset and not track the last key anymore. _lastKeyPressed = Key.None; } // The following input events act to "cancel" the EnterMenuMode event else if (inputEventArgs.RoutedEvent == Mouse.MouseDownEvent || inputEventArgs.RoutedEvent == Mouse.MouseUpEvent) { _lastKeyPressed = Key.None; // Win32 will see this message and will set QF_FMENUSTATUS to false. _win32MenuModeWorkAround = false; } } private bool IsMenuKey(Key key) { return (key == Key.LeftAlt || key == Key.RightAlt || key == Key.F10); } private Key GetRealKey(KeyEventArgs e) { return (e.Key == Key.System) ? e.SystemKey : e.Key; } ////// SecurityCritical:This code gets PresentationSource and passes it to event handlers /// TreatAsSafe: This code is safe inspite of passing the object because of 3 reasons /// 1. We have a demand on adding the event handler so that no one external can attach /// 2. The one event handler that we are aware of does not expose the object /// 3. This code in the worst case will cause your app to go to menu mode /// [SecurityCritical,SecurityTreatAsSafe] private bool OnEnterMenuMode(object eventSource) { if (_weakEnterMenuModeHandlers == null) return false; lock (_weakEnterMenuModeHandlers) { if (_weakEnterMenuModeHandlers.Count == 0) { return false; } // Bug 940610: no way to get PresentationSource of event in PostProcessInput // WORKAROUND: For now I will try to get the source of the event with // PresentationSource.FromVisual. If that fails, try to get the // source of the active window. PresentationSource source = null; if (eventSource != null) { Visual eventSourceVisual = eventSource as Visual; source = (eventSourceVisual != null) ? PresentationSource.CriticalFromVisual(eventSourceVisual) : null; } else { // If Keyboard.FocusedElement is null we'll have to fall back here. IntPtr activeWindow = MS.Win32.UnsafeNativeMethods.GetActiveWindow(); if (activeWindow != IntPtr.Zero) { source = HwndSource.CriticalFromHwnd(activeWindow); } } // Can't fire the event if the event didn't happen in any source if (source == null) { return false; } EventArgs e = EventArgs.Empty; bool handled = false; for (int i = 0; i < _weakEnterMenuModeHandlers.Count; i++) { EnterMenuModeEventHandler currentHandler = _weakEnterMenuModeHandlers[i].Target as EnterMenuModeEventHandler; if (currentHandler != null) { if (currentHandler(source, e)) { handled = true; break; } } else { _weakEnterMenuModeHandlers.RemoveAt(i); i--; } } return handled; } } ////// Called when ALT or F10 is pressed anywhere in the global scope /// ////// Critical: This code causes the handler attached to get an object of type presentationsource /// The add is critical, the remove is ok /// TreatAsSafe: There is a demand on this /// internal event EnterMenuModeEventHandler EnterMenuMode { [SecurityCritical,SecurityTreatAsSafe] add { SecurityHelper.DemandUIWindowPermission(); if (_weakEnterMenuModeHandlers == null) _weakEnterMenuModeHandlers = new List(1); lock (_weakEnterMenuModeHandlers) { // Cleanup the list in case some of the weakEnterMenuModeHandlers is disposed for (int i = 0; i < _weakEnterMenuModeHandlers.Count; i++) { if (!(_weakEnterMenuModeHandlers[i].Target is EnterMenuModeEventHandler)) { _weakEnterMenuModeHandlers.RemoveAt(i); i--; } } _weakEnterMenuModeHandlers.Add(new WeakReference(value)); } } remove { if (_weakEnterMenuModeHandlers != null) { lock (_weakEnterMenuModeHandlers) { for (int i = 0; i < _weakEnterMenuModeHandlers.Count; i++) { EnterMenuModeEventHandler current = _weakEnterMenuModeHandlers[i].Target as EnterMenuModeEventHandler; if (current == null || current == value) { _weakEnterMenuModeHandlers.RemoveAt(i); i--; } } } } } } internal delegate bool EnterMenuModeEventHandler(object sender, EventArgs e); // Used to track what the last key was pressed so that // we can fire the EnterMenuMode event. // Will be reset to Key.None when an unmatched KeyUp or other input event happens private Key _lastKeyPressed = Key.None; // List of WeakReferences to delegates to be invoked when EnterMenuMode happens private List _weakEnterMenuModeHandlers; // Fix for bug 936302: (JevanSa) // The DefaultWindowProcWorker (windows/core/ntuser/kernel/dwp.c) // listens for ALT down followed by ALT up with nothing in between. // When ALT goes down they set QF_FMENUSTATUS. When ALT up happens, // if QF_FMENUSTATUS is still set, they open the system menu (or // menu for the window if there is one). If any keystrokes happen // in between, they clear QF_FMENUSTATUS. // // Consider the following sequence: // 1) KeyDown(Alt) - neither Win32 nor Avalon respond // 2) KeyUp(Alt) - Avalon handles the event, Win32 is skipped // 3) KeyDown(Alt) - Avalon handles the event, Win32 is skipped // 4) KeyUp(Alt) - Avalon does not respond, Win32 handles the message // (and enters "Invisible" MenuMode) // // Here, from the point of view of the DWP, there was just ALT down // followed by ALT up. We must fool the DWP somehow so that they // clear clear the QF_FMENUSTATUS bit before #4. // // Currently the best way DwayneN and I have come up with is to // mark the event has handled in case #4 so that the DWP // never sees the ALT up in #4. We set this bit when #2 happens. // If we see an unhandled ALT-up and this bit is set, we mark the // event as handled. If we see any unhandled key down or mouse up/down // we can clear this bit. private bool _win32MenuModeWorkAround; #endregion #region UIState /// /// Critical: accesses the RawUIStateInputReport /// [SecurityCritical] private void ProcessForUIState(InputEventArgs inputEventArgs) { PresentationSource source; RawUIStateInputReport report = ExtractRawUIStateInputReport(inputEventArgs, InputManager.InputReportEvent); if (report != null && (source = report.InputSource) != null) { // handle accelerator cue display if ((report.Targets & RawUIStateTargets.HideAccelerators) != 0) { Visual root = source.RootVisual; bool enable = (report.Action == RawUIStateActions.Clear); EnableKeyboardCues(root, enable); } } } ////// Critical: accesses the RawUIStateInputReport /// [SecurityCritical] private RawUIStateInputReport ExtractRawUIStateInputReport(InputEventArgs e, RoutedEvent Event) { RawUIStateInputReport uiStateInputReport = null; InputReportEventArgs input = e as InputReportEventArgs; if (input != null) { if (input.Report.Type == InputType.Keyboard && input.RoutedEvent == Event) { uiStateInputReport = input.Report as RawUIStateInputReport; } } return uiStateInputReport; } #endregion UIState #region FocusEnterMainFocusScope weak event // The event is raised when KeyboardFocus enters the main focus scope (visual tree root) // Selector and TreeView listen for this event to update their ActiveSelection property internal event EventHandler FocusEnterMainFocusScope { add { lock (_weakFocusEnterMainFocusScopeHandlers) { _weakFocusEnterMainFocusScopeHandlers.Add(new WeakReference(value)); } } remove { lock (_weakFocusEnterMainFocusScopeHandlers) { for (int i = 0; i < _weakFocusEnterMainFocusScopeHandlers.Count; i++) { object handler = _weakFocusEnterMainFocusScopeHandlers[i].Target; if (handler == null || (EventHandler)handler == value) { _weakFocusEnterMainFocusScopeHandlers.RemoveAt(i); i--; } } } } } private void NotifyFocusEnterMainFocusScope(object sender, EventArgs e) { if (_weakFocusEnterMainFocusScopeHandlers != null) { for (int i = 0; i < _weakFocusEnterMainFocusScopeHandlers.Count; i++) { EventHandler handler = _weakFocusEnterMainFocusScopeHandlers[i].Target as EventHandler; if (handler != null) { handler(sender, e); } else { _weakFocusEnterMainFocusScopeHandlers.RemoveAt(i); i--; } } } } private List_weakFocusEnterMainFocusScopeHandlers = new List (1); #endregion #region Data private const double BASELINE_DEFAULT = Double.MinValue; private double _verticalBaseline = BASELINE_DEFAULT; private double _horizontalBaseline = BASELINE_DEFAULT; private DependencyProperty _navigationProperty = null; private Hashtable _containerHashtable = new Hashtable(10); private static object _fakeNull = new object(); #endregion Data } } // 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.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Windows.Threading; using System.Threading; using System.Windows; using System.Windows.Documents; using System.Windows.Interop; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Media3D; using System.Security; using System.Security.Permissions; using MS.Utility; using MS.Internal.Controls; using MS.Internal; using MS.Internal.PresentationFramework; using MS.Internal.KnownBoxes; using Microsoft.Win32; namespace System.Windows.Input { #region public enum types /// /// These options specify how the container will move the focus when tab and directional navigation occurs /// public enum KeyboardNavigationMode { ////// The container does not handle the keyboard navigation; /// each element receives keyboard focus as long as it is a key navigation stop. /// Continue, ////// The container and all of its child elements as a whole only receive focus once. /// Either the first tree child or the ActiveElement receive focus /// Once, ////// Depending on the direction of the navigation, /// the focus returns to the first or the last item when the end or /// the beginning of the container is reached, respectively. /// Cycle, ////// No keyboard navigation is allowed inside this container /// None, ////// Like cycle but does not move past the beginning or end of the container. /// Contained, ////// TabIndexes are considered on local subtree only inside this container /// Local, // NOTE: if you add or remove any values in this enum, be sure to update KeyboardNavigation.IsValidKeyNavigationMode() } #endregion public enum types ////// KeyboardNavigation class provide methods for logical (Tab) and directional (arrow) navigation between focusable controls /// public sealed class KeyboardNavigation { #region Constructors ////// Critical - this function elevates via a call to InputManager.Current /// TreatAsSafe: This code simply attaches a call back which is private /// [SecurityCritical,SecurityTreatAsSafe] internal KeyboardNavigation() { InputManager inputManager = InputManager.Current; inputManager.PostProcessInput += new ProcessInputEventHandler(PostProcessInput); inputManager.TranslateAccelerator += new KeyEventHandler(TranslateAccelerator); } #endregion Constructors #region public API #region Properties private static readonly DependencyProperty TabOnceActiveElementProperty = DependencyProperty.RegisterAttached("TabOnceActiveElement", typeof(WeakReference), typeof(KeyboardNavigation)); internal static DependencyObject GetTabOnceActiveElement(DependencyObject d) { WeakReference weakRef = (WeakReference)d.GetValue(TabOnceActiveElementProperty); if (weakRef != null && weakRef.IsAlive) { DependencyObject activeElement = weakRef.Target as DependencyObject; // Verify if the element is still in the same visual tree if (GetVisualRoot(activeElement) == GetVisualRoot(d)) return activeElement; else d.SetValue(TabOnceActiveElementProperty, null); } return null; } internal static void SetTabOnceActiveElement(DependencyObject d, DependencyObject value) { d.SetValue(TabOnceActiveElementProperty, new WeakReference(value)); } internal static readonly DependencyProperty ControlTabOnceActiveElementProperty = DependencyProperty.RegisterAttached("ControlTabOnceActiveElement", typeof(WeakReference), typeof(KeyboardNavigation)); private static DependencyObject GetControlTabOnceActiveElement(DependencyObject d) { WeakReference weakRef = (WeakReference)d.GetValue(ControlTabOnceActiveElementProperty); if (weakRef != null && weakRef.IsAlive) { DependencyObject activeElement = weakRef.Target as DependencyObject; // Verify if the element is still in the same visual tree if (GetVisualRoot(activeElement) == GetVisualRoot(d)) return activeElement; else d.SetValue(ControlTabOnceActiveElementProperty, null); } return null; } private static void SetControlTabOnceActiveElement(DependencyObject d, DependencyObject value) { d.SetValue(ControlTabOnceActiveElementProperty, new WeakReference(value)); } private DependencyObject GetActiveElement(DependencyObject d) { return _navigationProperty == ControlTabNavigationProperty ? GetControlTabOnceActiveElement(d) : GetTabOnceActiveElement(d); } private void SetActiveElement(DependencyObject d, DependencyObject value) { if (_navigationProperty == TabNavigationProperty) SetTabOnceActiveElement(d, value); else SetControlTabOnceActiveElement(d, value); } ////// Critical: This code retrieves PresentationSource which is a protected resource /// TreatAsSafe: It returns rootvisual which is ok and it does not expose the PresentationSource /// [SecurityCritical, SecurityTreatAsSafe] internal static Visual GetVisualRoot(DependencyObject d) { if (d is Visual || d is Visual3D) { PresentationSource source = PresentationSource.CriticalFromVisual(d); if (source != null) return source.RootVisual; } else { FrameworkContentElement fce = d as FrameworkContentElement; if (fce != null) return GetVisualRoot(fce.Parent); } return null; } // This internal property is used by GetRectagle method to deflate the bounding box of the element // If we expose this in the future - make sure it works with ContentElements too internal static readonly DependencyProperty DirectionalNavigationMarginProperty = DependencyProperty.RegisterAttached("DirectionalNavigationMargin", typeof(Thickness), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(new Thickness())); ////// The DependencyProperty for the TabIndex property. /// Flags: Can be used in style rules /// Default Value: Int32.MaxValue /// public static readonly DependencyProperty TabIndexProperty = DependencyProperty.RegisterAttached( "TabIndex", typeof(int), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(Int32.MaxValue)); ////// The DependencyProperty for the IsTabStop property. /// Flags: Can be used in style rules /// Default Value: true /// public static readonly DependencyProperty IsTabStopProperty = DependencyProperty.RegisterAttached( "IsTabStop", typeof(bool), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); ////// Controls the behavior of logical navigation on the children of the element this property is set on. /// TabNavigation is invoked with the TAB key. /// [CustomCategory("Accessibility")] [Localizability(LocalizationCategory.NeverLocalize)] [CommonDependencyProperty] public static readonly DependencyProperty TabNavigationProperty = DependencyProperty.RegisterAttached( "TabNavigation", typeof(KeyboardNavigationMode), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(KeyboardNavigationMode.Continue), new ValidateValueCallback(IsValidKeyNavigationMode)); ////// Controls the behavior of logical navigation on the children of the element this property is set on. /// ControlTabNavigation is invoked with the CTRL+TAB key. /// [CustomCategory("Accessibility")] [Localizability(LocalizationCategory.NeverLocalize)] [CommonDependencyProperty] public static readonly DependencyProperty ControlTabNavigationProperty = DependencyProperty.RegisterAttached( "ControlTabNavigation", typeof(KeyboardNavigationMode), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(KeyboardNavigationMode.Continue), new ValidateValueCallback(IsValidKeyNavigationMode)); ////// Controls the behavior of directional navigation on the children of the element this property is set on. /// Directional navigation is invoked with the arrow keys. /// [CustomCategory("Accessibility")] [Localizability(LocalizationCategory.NeverLocalize)] [CommonDependencyProperty] public static readonly DependencyProperty DirectionalNavigationProperty = DependencyProperty.RegisterAttached( "DirectionalNavigation", typeof(KeyboardNavigationMode), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(KeyboardNavigationMode.Continue), new ValidateValueCallback(IsValidKeyNavigationMode)); ////// Attached property set on elements registered with AccessKeyManager when AccessKeyCues should be shown. /// internal static readonly DependencyProperty ShowKeyboardCuesProperty = DependencyProperty.RegisterAttached( "ShowKeyboardCues", typeof(bool), typeof(KeyboardNavigation), new FrameworkPropertyMetadata( BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior, null /* No PropertyChangedCallback */, new CoerceValueCallback(CoerceShowKeyboardCues))); // Coercion for ShowKeyboardCuesProperty private static object CoerceShowKeyboardCues(DependencyObject d, object value) { // Always return true if the user has requested that KeyboardCues always // be on (accessibility setting). return SystemParameters.KeyboardCues ? BooleanBoxes.TrueBox : value; } ////// Indicates if VK_Return character is accepted by a control /// /// Default: false. /// public static readonly DependencyProperty AcceptsReturnProperty = DependencyProperty.RegisterAttached( "AcceptsReturn", typeof(bool), typeof(KeyboardNavigation), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox)); #region Workaround for Bug 908235 -- when focus change events go to PostProcessInput we don't need this glue. internal event KeyboardFocusChangedEventHandler FocusChanged { add { lock (_weakFocusChangedHandlers) { _weakFocusChangedHandlers.Add(new WeakReference(value)); } } remove { lock (_weakFocusChangedHandlers) { for (int i = 0; i < _weakFocusChangedHandlers.Count; i++) { object handler = _weakFocusChangedHandlers[i].Target; if (handler == null || (KeyboardFocusChangedEventHandler)handler == value) { _weakFocusChangedHandlers.RemoveAt(i); i--; } } } } } internal void NotifyFocusChanged(object sender, KeyboardFocusChangedEventArgs e) { if (_weakFocusChangedHandlers != null) { for (int i = 0; i < _weakFocusChangedHandlers.Count; i++) { KeyboardFocusChangedEventHandler handler = _weakFocusChangedHandlers[i].Target as KeyboardFocusChangedEventHandler; if (handler != null) { handler(sender, e); } else { _weakFocusChangedHandlers.RemoveAt(i); i--; } } } } private List_weakFocusChangedHandlers = new List (1); #endregion #endregion Properties #region methods /// /// Writes the attached property TabIndex to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetTabIndex(DependencyObject element, int index) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(TabIndexProperty, index); } /// /// Reads the attached property TabIndex from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static int GetTabIndex(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return GetTabIndexHelper(element); } /// /// Writes the attached property IsTabStop to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetIsTabStop(DependencyObject element, bool isTabStop) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(IsTabStopProperty, BooleanBoxes.Box(isTabStop)); } /// /// Reads the attached property IsTabStop from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static bool GetIsTabStop(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool)element.GetValue(IsTabStopProperty); } /// /// Writes the attached property TabNavigation to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetTabNavigation(DependencyObject element, KeyboardNavigationMode mode) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(TabNavigationProperty, mode); } /// /// Reads the attached property TabNavigation from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[CustomCategory("Accessibility")] [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static KeyboardNavigationMode GetTabNavigation(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (KeyboardNavigationMode)element.GetValue(TabNavigationProperty); } /// /// Writes the attached property ControlTabNavigation to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetControlTabNavigation(DependencyObject element, KeyboardNavigationMode mode) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(ControlTabNavigationProperty, mode); } /// /// Reads the attached property ControlTabNavigation from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[CustomCategory("Accessibility")] [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static KeyboardNavigationMode GetControlTabNavigation(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (KeyboardNavigationMode)element.GetValue(ControlTabNavigationProperty); } /// /// Writes the attached property DirectionalNavigation to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetDirectionalNavigation(DependencyObject element, KeyboardNavigationMode mode) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(DirectionalNavigationProperty, mode); } /// /// Reads the attached property DirectionalNavigation from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[CustomCategory("Accessibility")] [AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static KeyboardNavigationMode GetDirectionalNavigation(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (KeyboardNavigationMode)element.GetValue(DirectionalNavigationProperty); } /// /// Writes the attached property AcceptsReturn to the given element. /// /// The element to which to write the attached property. /// The property value to set ///public static void SetAcceptsReturn(DependencyObject element, bool enabled) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(AcceptsReturnProperty, BooleanBoxes.Box(enabled)); } /// /// Reads the attached property AcceptsReturn from the given element. /// /// The element from which to read the attached property. ///The property's value. ///[AttachedPropertyBrowsableForType(typeof(DependencyObject))] public static bool GetAcceptsReturn(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool)element.GetValue(AcceptsReturnProperty); } private static bool IsValidKeyNavigationMode(object o) { KeyboardNavigationMode value = (KeyboardNavigationMode)o; return value == KeyboardNavigationMode.Contained || value == KeyboardNavigationMode.Continue || value == KeyboardNavigationMode.Cycle || value == KeyboardNavigationMode.None || value == KeyboardNavigationMode.Once || value == KeyboardNavigationMode.Local; } #endregion methods #endregion public API #region FocusVisualStyle API // This class is used by AdornerLayer which adds it to its visual tree // Once AdornerLayer and Adorner are UIElement we can remove this class and // apply FrameworkElement.FocusVisualStyle directly to AdornerLayer // Note:- This class is sealed because it calls OnVisualChildrenChanged virtual in the // constructor and it does not override it, but derived classes could. private sealed class FocusVisualAdorner: Adorner { public FocusVisualAdorner(UIElement adornedElement, Style focusVisualStyle) : base(adornedElement) { Debug.Assert(adornedElement != null, "adornedElement should not be null"); Debug.Assert(focusVisualStyle != null, "focusVisual should not be null"); Control control = new Control(); control.Style = focusVisualStyle; _adorderChild = control; IsClipEnabled = true; IsHitTestVisible = false; IsEnabled = false; AddVisualChild(_adorderChild); } public FocusVisualAdorner(ContentElement adornedElement, UIElement adornedElementParent, IContentHost contentHostParent, Style focusVisualStyle) : base(adornedElementParent) { Debug.Assert(adornedElement != null, "adornedElement should not be null"); Debug.Assert(adornedElementParent != null, "adornedElementParent should not be null"); Debug.Assert(contentHostParent != null, "contentHostParent should not be null"); Debug.Assert(contentHostParent is Visual, "contentHostParent should be Visual"); Debug.Assert(focusVisualStyle != null, "focusVisual should not be null"); _contentHostParent = contentHostParent; _adornedContentElement = adornedElement; _focusVisualStyle = focusVisualStyle; Canvas canvas = new Canvas(); _canvasChildren = canvas.Children; _adorderChild = canvas; AddVisualChild(_adorderChild); IsClipEnabled = true; IsHitTestVisible = false; IsEnabled = false; } /// /// Measure adorner. Default behavior is to size to match the adorned element. /// protected override Size MeasureOverride(Size constraint) { Size desiredSize = new Size(); // If the focus visual is adorning a content element, // the child will be a canvas that doesn't need to be measured. if (_adornedContentElement == null) { desiredSize = AdornedElement.RenderSize; constraint = desiredSize; } // Measure the child ((UIElement)GetVisualChild(0)).Measure(constraint); return desiredSize; } ////// Default control arrangement is to only arrange /// the first visual child. No transforms will be applied. /// protected override Size ArrangeOverride(Size size) { Size finalSize = base.ArrangeOverride(size); // In case we adorn ContentElement we have to update the rectangles if (_adornedContentElement != null) { if (_contentRects == null) { // Clear rects _canvasChildren.Clear(); } else { IContentHost contentHost = ContentHost; if (!(contentHost is Visual) || !AdornedElement.IsAncestorOf((Visual)contentHost)) { // Content elements is not in the tree, clear children and give up. _canvasChildren.Clear(); return new Size(); } Rect desiredRect = Rect.Empty; IEnumeratorenumerator = _contentRects.GetEnumerator(); if (_canvasChildren.Count == _contentRects.Count) { // Reuse the controls and update the controls position for (int i = 0; i < _canvasChildren.Count; i++) { enumerator.MoveNext(); Rect rect = enumerator.Current; rect = _hostToAdornedElement.TransformBounds(rect); Control control = (Control)_canvasChildren[i]; control.Width = rect.Width; control.Height = rect.Height; Canvas.SetLeft(control, rect.X); Canvas.SetTop(control, rect.Y); } _adorderChild.InvalidateArrange(); } else // Rebuild the visual tree to correspond to current bounding rectangles { _canvasChildren.Clear(); while (enumerator.MoveNext()) { Rect rect = enumerator.Current; rect = _hostToAdornedElement.TransformBounds(rect); Control control = new Control(); control.Style = _focusVisualStyle; control.Width = rect.Width; control.Height = rect.Height; Canvas.SetLeft(control, rect.X); Canvas.SetTop(control, rect.Y); _canvasChildren.Add(control); } } } } ((UIElement)GetVisualChild(0)).Arrange(new Rect(new Point(), finalSize)); return finalSize; } /// /// Derived classes override this property to enable the Visual code to enumerate /// the Visual children. Derived classes need to return the number of children /// from this method. /// /// By default a Visual does not have any children. /// /// Remark: /// During this virtual method the Visual tree must not be modified. /// protected override int VisualChildrenCount { get { return 1; // _adorderChild created in ctor. } } ////// Derived class must implement to support Visual children. The method must return /// the child at the specified index. Index must be between 0 and GetVisualChildrenCount-1. /// /// By default a Visual does not have any children. /// /// Remark: /// During this virtual call it is not valid to modify the Visual tree. /// protected override Visual GetVisualChild(int index) { if (index == 0) { return _adorderChild; } else { throw new ArgumentOutOfRangeException("index", index, SR.Get(SRID.Visual_ArgumentOutOfRange)); } } private IContentHost ContentHost { get { // Re-query IContentHost if the old one was disposed if (_adornedContentElement != null && (_contentHostParent==null || VisualTreeHelper.GetParent(_contentHostParent as Visual) == null)) { _contentHostParent = MS.Internal.Documents.ContentHostHelper.FindContentHost(_adornedContentElement); } return _contentHostParent; } } ////// Says if the Adorner needs update based on the /// previously cached size if the AdornedElement. /// internal override bool NeedsUpdate(Size oldSize) { if (_adornedContentElement != null) { ReadOnlyCollectionoldRects = _contentRects; _contentRects = null; IContentHost contentHost = ContentHost; if (contentHost != null) { _contentRects = contentHost.GetRectangles(_adornedContentElement); } // The positions of the focus rects are dependent on the rects returned from // host.GetRectangles and the transform from the host to the adorned element GeneralTransform oldTransform = _hostToAdornedElement; if (contentHost is Visual && AdornedElement.IsAncestorOf((Visual)contentHost)) { _hostToAdornedElement = ((Visual)contentHost).TransformToAncestor(AdornedElement); } else { _hostToAdornedElement = Transform.Identity; } // See if these are the same transform if (oldTransform != _hostToAdornedElement) { // Allow two identical matrix transforms if (!(oldTransform is MatrixTransform) || !(_hostToAdornedElement is MatrixTransform) || !Matrix.Equals(((MatrixTransform)oldTransform).Matrix, ((MatrixTransform)_hostToAdornedElement).Matrix)) { // one is a general transform or the matrices are not equal, need to update return true; } } if (_contentRects != null && oldRects != null && _contentRects.Count == oldRects.Count) { for (int i=0; i _contentRects; } internal static UIElement GetParentUIElementFromContentElement(ContentElement ce) { IContentHost ichParent = null; return GetParentUIElementFromContentElement(ce, ref ichParent); } private static UIElement GetParentUIElementFromContentElement(ContentElement ce, ref IContentHost ichParent) { if (ce == null) return null; IContentHost ich = MS.Internal.Documents.ContentHostHelper.FindContentHost(ce); if (ichParent == null) ichParent = ich; DependencyObject parent = ich as DependencyObject; if(parent != null) { // Case 1: UIElement // return the element UIElement eParent = parent as UIElement; if(eParent != null) return eParent; // Case 2: Visual // Walk up the visual tree until we find UIElement Visual visualParent = parent as Visual; while (visualParent != null) { visualParent = VisualTreeHelper.GetParent(visualParent) as Visual; UIElement uielement = visualParent as UIElement; if (uielement != null) return uielement; } // Case 3: ContentElement ContentElement ceParent = parent as ContentElement; if(ceParent != null) return GetParentUIElementFromContentElement(ceParent, ref ichParent); } return null; } internal void HideFocusVisual() { // Remove the existing focus visual if (_focusVisualAdornerCache != null) { AdornerLayer adornerlayer = VisualTreeHelper.GetParent(_focusVisualAdornerCache) as AdornerLayer; if (adornerlayer != null) { adornerlayer.Remove(_focusVisualAdornerCache); } _focusVisualAdornerCache = null; } } /// /// Critical: This code accesses link demanded input manager /// TreatAsSafe: This code is ok to expose as it simply return boolean weather Keyboard is the last used device /// [SecurityCritical, SecurityTreatAsSafe] internal static bool IsKeyboardMostRecentInputDevice() { return InputManager.Current.MostRecentInputDevice is KeyboardDevice; } internal static bool AlwaysShowFocusVisual { get { return _alwaysShowFocusVisual; } set { _alwaysShowFocusVisual = value; } } private static bool _alwaysShowFocusVisual = SystemParameters.KeyboardCues; internal static void ShowFocusVisual() { Current.ShowFocusVisual(Keyboard.FocusedElement as DependencyObject); } private void ShowFocusVisual(DependencyObject element) { // Always hide the existing focus visual HideFocusVisual(); // Disable keyboard cues (accesskey underline) if keyboard device is not MostRecentInputDevice if (!IsKeyboardMostRecentInputDevice()) { EnableKeyboardCues(element, false); } // Show focus visual if system metric is true or keyboard is used last if (AlwaysShowFocusVisual || IsKeyboardMostRecentInputDevice()) { FrameworkElement fe = element as FrameworkElement; if (fe != null) { AdornerLayer adornerlayer = AdornerLayer.GetAdornerLayer(fe); if (adornerlayer == null) return; Style fvs = fe.FocusVisualStyle; // WORKAROUND: (Bug 1016350) If FocusVisualStyle is the "default" value // then we load the default FocusVisualStyle from ResourceDictionary. if (fvs == FrameworkElement.DefaultFocusVisualStyle) { fvs = SystemResources.FindResourceInternal(SystemParameters.FocusVisualStyleKey) as Style; } if (fvs != null) { _focusVisualAdornerCache = new FocusVisualAdorner(fe, fvs); adornerlayer.Add(_focusVisualAdornerCache); } } else // If not FrameworkElement { FrameworkContentElement fce = element as FrameworkContentElement; if (fce != null) { IContentHost parentICH = null; UIElement parentUIElement = GetParentUIElementFromContentElement(fce, ref parentICH); if (parentICH != null && parentUIElement != null) { AdornerLayer adornerlayer = AdornerLayer.GetAdornerLayer(parentUIElement); if (adornerlayer != null) { Style fvs = fce.FocusVisualStyle; // WORKAROUND: (Bug 1016350) If FocusVisualStyle is the "default" value // then we load the default FocusVisualStyle from ResourceDictionary. if (fvs == FrameworkElement.DefaultFocusVisualStyle) { fvs = SystemResources.FindResourceInternal(SystemParameters.FocusVisualStyleKey) as Style; } if (fvs != null) { _focusVisualAdornerCache = new FocusVisualAdorner(fce, parentUIElement, parentICH, fvs); adornerlayer.Add(_focusVisualAdornerCache); } } } } } } } private FocusVisualAdorner _focusVisualAdornerCache = null; #endregion FocusVisualStyle API #region Navigate helpers internal static void UpdateFocusedElement(DependencyObject focusTarget) { DependencyObject focusScope = FocusManager.GetFocusScope(focusTarget); if (focusScope != null && focusScope != focusTarget) { FocusManager.SetFocusedElement(focusScope, focusTarget as IInputElement); // Raise FocusEnterMainFocusScope event Visual visualRoot = GetVisualRoot(focusTarget); if (visualRoot != null && focusScope == visualRoot) { Current.NotifyFocusEnterMainFocusScope(visualRoot, EventArgs.Empty); } } } internal void UpdateActiveElement(DependencyObject activeElement) { // Update TabNavigation = Once groups UpdateActiveElement(activeElement, TabNavigationProperty); // Update ControlTabNavigation = Once groups UpdateActiveElement(activeElement, ControlTabNavigationProperty); } private void UpdateActiveElement(DependencyObject activeElement, DependencyProperty dp) { _navigationProperty = dp; DependencyObject container = GetGroupParent(activeElement); if (activeElement == container) return; // Update ActiveElement only if container has TabNavigation = Once if (GetKeyNavigationMode(container) == KeyboardNavigationMode.Once) { SetActiveElement(container, activeElement); } } // Called from FrameworkElement.MoveFocus internal bool Navigate(DependencyObject currentElement, TraversalRequest request) { return Navigate(currentElement, request, Keyboard.Modifiers); } private bool Navigate(DependencyObject currentElement, TraversalRequest request, ModifierKeys modifierKeys) { return Navigate(currentElement, request, modifierKeys, null); } private bool Navigate(DependencyObject currentElement, TraversalRequest request, ModifierKeys modifierKeys, DependencyObject firstElement) { Debug.Assert(currentElement != null, "currentElement should not be null"); DependencyObject nextTab = null; IKeyboardInputSink inputSink = null; switch (request.FocusNavigationDirection) { case FocusNavigationDirection.Next: _navigationProperty = (modifierKeys & ModifierKeys.Control) == ModifierKeys.Control ? ControlTabNavigationProperty : TabNavigationProperty; nextTab = GetNextTab(currentElement, GetGroupParent(currentElement, true /*includeCurrent*/), false); break; case FocusNavigationDirection.Previous: _navigationProperty = (modifierKeys & ModifierKeys.Control) == ModifierKeys.Control ? ControlTabNavigationProperty : TabNavigationProperty; nextTab = GetPrevTab(currentElement, null, false); break; case FocusNavigationDirection.First: _navigationProperty = (modifierKeys & ModifierKeys.Control) == ModifierKeys.Control ? ControlTabNavigationProperty : TabNavigationProperty; nextTab = GetNextTab(null, currentElement, true); break; case FocusNavigationDirection.Last: _navigationProperty = (modifierKeys & ModifierKeys.Control) == ModifierKeys.Control ? ControlTabNavigationProperty : TabNavigationProperty; nextTab = GetPrevTab(null, currentElement, true); break; case FocusNavigationDirection.Left: case FocusNavigationDirection.Right: case FocusNavigationDirection.Up: case FocusNavigationDirection.Down: _navigationProperty = DirectionalNavigationProperty; nextTab = GetNextInDirection(currentElement, request.FocusNavigationDirection); break; } // If there are no other tabstops, try to pass focus outside PresentationSource if (nextTab == null) { // If Wrapped is true we should not searach outside this container if (request.Wrapped || request.FocusNavigationDirection == FocusNavigationDirection.First || request.FocusNavigationDirection == FocusNavigationDirection.Last) return false; // Try to navigate outside the PresentationSource bool navigatedOutside = NavigateOutsidePresentationSource(currentElement, request); if (navigatedOutside) { return true; } else if (request.FocusNavigationDirection == FocusNavigationDirection.Next || request.FocusNavigationDirection == FocusNavigationDirection.Previous) { // In case focus cannot navigate outside - we should cycle Visual visualRoot = GetVisualRoot(currentElement); if (visualRoot != null) return Navigate(visualRoot, new TraversalRequest(request.FocusNavigationDirection == FocusNavigationDirection.Next ? FocusNavigationDirection.First : FocusNavigationDirection.Last)); } return false; } inputSink = nextTab as IKeyboardInputSink; if (inputSink == null) { // If target element does not support IKeyboardInputSink then we try to set focus // In TextBox scenario Focus() return false although the focus is set to TextBox content // So we need to verify IsKeyboardFocusWithin property (bugfix 954000) IInputElement iie = nextTab as IInputElement; iie.Focus(); return iie.IsKeyboardFocusWithin; } else { // If target element supports IKeyboardInputSink then we pass the focus there bool traversed = false; if (request.FocusNavigationDirection == FocusNavigationDirection.First || request.FocusNavigationDirection == FocusNavigationDirection.Next) { traversed = inputSink.TabInto(new TraversalRequest(FocusNavigationDirection.First)); } else if (request.FocusNavigationDirection == FocusNavigationDirection.Last || request.FocusNavigationDirection == FocusNavigationDirection.Previous) { traversed = inputSink.TabInto(new TraversalRequest(FocusNavigationDirection.Last)); } else // FocusNavigationDirection { TraversalRequest tr = new TraversalRequest(request.FocusNavigationDirection); tr.Wrapped = true; traversed = inputSink.TabInto(tr); } // If we fail to navigate into IKeyboardInputSink then move to the next element if (!traversed && firstElement != nextTab) { // Navigate to next element in the tree traversed = Navigate(nextTab, request, modifierKeys, firstElement == null ? nextTab : firstElement); } return traversed; } } ////// Critical: This code accesses PresentationSource. /// TreatAsSafe: This code causes navigation to different elements within an app. /// It does not expose the PresentationSource /// Critical: Asserting UnmanagedCode permission to obtain HwndSource.IKeyboardInputSink.KeyboardInputSite /// TreatAsSafe: Not leaking the InputKeyboardSite obtained under elevation. /// [SecurityCritical, SecurityTreatAsSafe] private bool NavigateOutsidePresentationSource(DependencyObject currentElement, TraversalRequest request) { Visual visual = currentElement as Visual; if (visual == null) { visual = GetParentUIElementFromContentElement(currentElement as ContentElement); if (visual == null) return false; } IKeyboardInputSink inputSink = PresentationSource.CriticalFromVisual(visual) as IKeyboardInputSink; if (inputSink != null) { IKeyboardInputSite ikis = null; new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // BlessedAssert try { ikis = inputSink.KeyboardInputSite; } finally { CodeAccessPermission.RevertAssert(); } if (ikis != null) return ikis.OnNoMoreTabStops(request); } return false; } internal static KeyboardNavigation Current { get { return FrameworkElement.KeyboardNavigation; } } ///////////////////////////////////////////////////////////////////// ////// Critical: accesses e.StagingItem.Input and asserts to retrieve HwndSource /// [SecurityCritical] private void PostProcessInput(object sender, ProcessInputEventArgs e) { // Call Forwarded ProcessInput(e.StagingItem.Input); } ///////////////////////////////////////////////////////////////////// ////// Critical: asserts to retrieve HwndSource /// [SecurityCritical] private void TranslateAccelerator(object sender, KeyEventArgs e) { // Call Forwarded ProcessInput(e); } ///////////////////////////////////////////////////////////////////// ////// Critical: asserts to retrieve HwndSource /// [SecurityCritical] private void ProcessInput(InputEventArgs inputEventArgs) { ProcessForMenuMode(inputEventArgs); ProcessForUIState(inputEventArgs); // Process keyboard navigation for keydown event for Tab,Left,Right,Up,Down keys. if(inputEventArgs.RoutedEvent != Keyboard.KeyDownEvent) return; KeyEventArgs keyEventArgs = (KeyEventArgs)inputEventArgs; if (keyEventArgs.Handled) return; DependencyObject sourceElement = keyEventArgs.OriginalSource as DependencyObject; // For Keyboard Interop with Avalon-inside-Avalon via HwndHost. // In Keyboard interop, the target (called OriginalSource here) is "forced" // to point at the HwndHost containing the Hwnd with focus. This allows // us to tunnel/bubble the keystroke across the outer HwndSource to the // child hwnd that has focus. (see HwndSource.TranslateAccelerator) // But this "forced" target is wrong for Tab Navigation; eg. tabbing // across an inner avalon under the HwndHost. For that we need the // real original target element, which we happen to find in KeyboardDevice.Target. // // sourceElement and innerElement I don't expect will ever be different // except in this case. And I added a check that the "forced" target // is an HwndHost for good measure. DependencyObject innerElement = keyEventArgs.KeyboardDevice.Target as DependencyObject; if( innerElement != null && sourceElement != innerElement ) { if(sourceElement is HwndHost) sourceElement = innerElement; } // When nothing has focus - we should start from the root of the visual tree if (sourceElement == null) { HwndSource hwndSource = keyEventArgs.UnsafeInputSource as HwndSource; if (hwndSource == null) return; sourceElement = hwndSource.RootVisual; if (sourceElement == null) return; } // Focus visual support switch (GetRealKey(keyEventArgs)) { case Key.LeftAlt: case Key.RightAlt: ShowFocusVisual(); EnableKeyboardCues(sourceElement, true); break; case Key.Tab: case Key.Right: case Key.Left: case Key.Up: case Key.Down: ShowFocusVisual(); break; } keyEventArgs.Handled = Navigate(sourceElement, keyEventArgs.Key, keyEventArgs.KeyboardDevice.Modifiers); } internal static void EnableKeyboardCues(DependencyObject element, bool enable) { Visual visual = element as Visual; if (visual == null) { visual = GetParentUIElementFromContentElement(element as ContentElement); if (visual == null) return; } Visual rootVisual = GetVisualRoot(visual); if (rootVisual != null) { rootVisual.SetValue(ShowKeyboardCuesProperty, enable ? BooleanBoxes.TrueBox : BooleanBoxes.FalseBox); } } internal static FocusNavigationDirection KeyToTraversalDirection(Key key) { switch (key) { case Key.Left: return FocusNavigationDirection.Left; case Key.Right: return FocusNavigationDirection.Right; case Key.Up: return FocusNavigationDirection.Up; case Key.Down: return FocusNavigationDirection.Down; } throw new NotSupportedException(); } internal DependencyObject PredictFocusedElement(DependencyObject sourceElement, FocusNavigationDirection direction) { if (sourceElement == null) { return null; } _navigationProperty = DirectionalNavigationProperty; _verticalBaseline = BASELINE_DEFAULT; _horizontalBaseline = BASELINE_DEFAULT; return GetNextInDirection(sourceElement, direction); } internal bool Navigate(DependencyObject sourceElement, Key key, ModifierKeys modifiers) { bool success = false; switch (key) { // Logical (Tab) navigation case Key.Tab: success = Navigate(sourceElement, new TraversalRequest(((modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) ? FocusNavigationDirection.Previous : FocusNavigationDirection.Next), modifiers); break; case Key.Right: success = Navigate(sourceElement, new TraversalRequest(FocusNavigationDirection.Right), modifiers); break; case Key.Left: success = Navigate(sourceElement, new TraversalRequest(FocusNavigationDirection.Left), modifiers); break; case Key.Up: success = Navigate(sourceElement, new TraversalRequest(FocusNavigationDirection.Up), modifiers); break; case Key.Down: success = Navigate(sourceElement, new TraversalRequest(FocusNavigationDirection.Down), modifiers); break; } return success; } #endregion Navigate helpers #region Tree navigation // Filter the visual tree and return true if: // 1. visual is visible UIElement // 2. visual is visible UIElement3D // 3. visual is IContentHost but not UIElementIsland // Note: UIElementIsland is a special element that has only one child and should be excluded private bool IsInNavigationTree(DependencyObject visual) { UIElement uiElement = visual as UIElement; if (uiElement != null && uiElement.IsVisible) return true; if (visual is IContentHost && !(visual is MS.Internal.Documents.UIElementIsland)) return true; UIElement3D uiElement3D = visual as UIElement3D; if (uiElement3D != null && uiElement3D.IsVisible) return true; return false; } private DependencyObject GetPreviousSibling(DependencyObject e) { DependencyObject parent = GetParent(e); // If parent is IContentHost - get next from the enumerator IContentHost ich = parent as IContentHost; if (ich != null) { IInputElement previousElement = null; IEnumeratorenumerator = ich.HostedElements; while (enumerator.MoveNext()) { IInputElement current = enumerator.Current; if (current == e) return previousElement as DependencyObject; if (current is UIElement || current is UIElement3D) previousElement = current; else { ContentElement ce = current as ContentElement; if (ce != null && IsTabStop(ce)) previousElement = current; } } return null; } else { // If parent is UIElement(3D) - return visual sibling DependencyObject parentAsUIElement = parent as UIElement; if (parentAsUIElement == null) { parentAsUIElement = parent as UIElement3D; } DependencyObject elementAsVisual = e as Visual; if (elementAsVisual == null) { elementAsVisual = e as Visual3D; } if (parentAsUIElement != null && elementAsVisual != null) { int count = VisualTreeHelper.GetChildrenCount(parentAsUIElement); DependencyObject prev = null; for(int i = 0; i < count; i++) { DependencyObject vchild = VisualTreeHelper.GetChild(parentAsUIElement, i); if(vchild == elementAsVisual) break; if (IsInNavigationTree(vchild)) prev = vchild; } return prev; } } return null; } private DependencyObject GetNextSibling(DependencyObject e) { DependencyObject parent = GetParent(e); // If parent is IContentHost - get next from the enumerator IContentHost ich = parent as IContentHost; if (ich != null) { IEnumerator enumerator = ich.HostedElements; bool found = false; while (enumerator.MoveNext()) { IInputElement current = enumerator.Current; if (found) { if (current is UIElement || current is UIElement3D) return current as DependencyObject; else { ContentElement ce = current as ContentElement; if (ce != null && IsTabStop(ce)) return ce; } } else if (current == e) { found = true; } } } else { // If parent is UIElement(3D) - return visual sibling DependencyObject parentAsUIElement = parent as UIElement; if (parentAsUIElement == null) { parentAsUIElement = parent as UIElement3D; } DependencyObject elementAsVisual = e as Visual; if (elementAsVisual == null) { elementAsVisual = e as Visual3D; } if (parentAsUIElement != null && elementAsVisual != null) { int count = VisualTreeHelper.GetChildrenCount(parentAsUIElement); int i = 0; //go till itself for(; i < count; i++) { DependencyObject vchild = VisualTreeHelper.GetChild(parentAsUIElement, i); if(vchild == elementAsVisual) break; } i++; //search ahead for(; i < count; i++) { DependencyObject visual = VisualTreeHelper.GetChild(parentAsUIElement, i); if (IsInNavigationTree(visual)) return visual; } } } return null; } // For Control+Tab navigation or TabNavigation when fe is not a FocusScope: // Scenarios: // 1. UserControl can set its FocusedElement to delegate focus when Tab navigation happens // 2. ToolBar or Menu (which have IsFocusScope=true) both have FocusedElement but included only in Control+Tab navigation private DependencyObject FocusedElement(DependencyObject e) { IInputElement iie = e as IInputElement; // Focus delegation is enabled only if keyboard focus is outside the container if (iie != null && !iie.IsKeyboardFocusWithin) { DependencyObject focusedElement = FocusManager.GetFocusedElement(e) as DependencyObject; if (focusedElement != null) { if (_navigationProperty == ControlTabNavigationProperty || !IsFocusScope(e)) { // Verify if focusedElement is a visual descendant of e Visual visualFocusedElement = focusedElement as Visual; if (visualFocusedElement == null) { Visual3D visual3DFocusedElement = focusedElement as Visual3D; if (visual3DFocusedElement == null) { visualFocusedElement = GetParentUIElementFromContentElement(focusedElement as ContentElement); } else { if (visual3DFocusedElement.IsDescendantOf(e)) { return focusedElement; } } } if (visualFocusedElement.IsDescendantOf(e)) { return focusedElement; } } } } return null; } // We traverse only UIElement(3D) or ContentElement private DependencyObject GetFirstChild(DependencyObject e) { // If the element has a FocusedElement it should be its first child DependencyObject focusedElement = FocusedElement(e); if (focusedElement != null) return focusedElement; // If the element is IContentHost - return the first child IContentHost ich = e as IContentHost; if (ich != null) { IEnumerator enumerator = ich.HostedElements; while (enumerator.MoveNext()) { IInputElement current = enumerator.Current; if (current is UIElement || current is UIElement3D) { return current as DependencyObject; } else { ContentElement ce = current as ContentElement; if (ce != null && IsTabStop(ce)) return ce; } } return null; } // Return the first visible UIElement(3D) or IContentHost DependencyObject uiElement = e as UIElement; if (uiElement == null) { uiElement = e as UIElement3D; } if (uiElement == null || UIElementHelper.IsVisible(uiElement)) { DependencyObject elementAsVisual = e as Visual; if (elementAsVisual == null) { elementAsVisual = e as Visual3D; } if (elementAsVisual != null) { int count = VisualTreeHelper.GetChildrenCount(elementAsVisual); for (int i = 0; i < count; i++) { DependencyObject visual = VisualTreeHelper.GetChild(elementAsVisual, i); if (IsInNavigationTree(visual)) return visual; else { DependencyObject firstChild = GetFirstChild(visual); if (firstChild != null) return firstChild; } } } } // If element is ContentElement for example return null; } private DependencyObject GetLastChild(DependencyObject e) { // If the element has a FocusedElement it should be its last child DependencyObject focusedElement = FocusedElement(e); if (focusedElement != null) return focusedElement; // If the element is IContentHost - return the last child IContentHost ich = e as IContentHost; if (ich != null) { IEnumerator enumerator = ich.HostedElements; IInputElement last = null; while (enumerator.MoveNext()) { IInputElement current = enumerator.Current; if (current is UIElement || current is UIElement3D) last = current; else { ContentElement ce = current as ContentElement; if (ce != null && IsTabStop(ce)) last = current; } } return last as DependencyObject; } // Return the last visible UIElement(3D) or IContentHost DependencyObject uiElement = e as UIElement; if (uiElement == null) { uiElement = e as UIElement3D; } if (uiElement == null || UIElementHelper.IsVisible(uiElement)) { DependencyObject elementAsVisual = e as Visual; if (elementAsVisual == null) { elementAsVisual = e as Visual3D; } if (elementAsVisual != null) { int count = VisualTreeHelper.GetChildrenCount(elementAsVisual); for (int i = count - 1; i >= 0; i--) { DependencyObject visual = VisualTreeHelper.GetChild(elementAsVisual, i); if (IsInNavigationTree(visual)) return visual; else { DependencyObject lastChild = GetLastChild(visual); if (lastChild != null) return lastChild; } } } } return null; } private DependencyObject GetParent(DependencyObject e) { // For Visual - go up the visual parent chain until we find Visual, Visual3D or IContentHost if (e is Visual || e is Visual3D) { DependencyObject visual = e; while ((visual = VisualTreeHelper.GetParent(visual)) != null) { // if (IsInNavigationTree(visual)) return visual; } } else { // For ContentElement - return the host element (which is IContentHost) ContentElement contentElement = e as ContentElement; if (contentElement != null) { return MS.Internal.Documents.ContentHostHelper.FindContentHost(contentElement) as DependencyObject; } } return null; } /***************************************************************************\ * * GetNextInTree(DependencyObject e, DependencyObject container) * Search the subtree with container root; Don't go inside TabGroups * * Return the next Element in tree in depth order (self-child-sibling). * 1 * / \ * 2 5 * / \ * 3 4 * \***************************************************************************/ private DependencyObject GetNextInTree(DependencyObject e, DependencyObject container) { Debug.Assert(e != null, "e should not be null"); Debug.Assert(container != null, "container should not be null"); DependencyObject result = null; if (e == container || !IsGroup(e)) result = GetFirstChild(e); if (result != null || e == container) return result; DependencyObject parent = e; do { DependencyObject sibling = GetNextSibling(parent); if (sibling != null) return sibling; parent = GetParent(parent); } while (parent != null && parent != container); return null; } /***************************************************************************\ * * GetPreviousInTree(DependencyObject e, DependencyObject container) * Don't go inside TabGroups * Return the previous Element in tree in depth order (self-child-sibling). * 5 * / \ * 4 1 * / \ * 3 2 \***************************************************************************/ private DependencyObject GetPreviousInTree(DependencyObject e, DependencyObject container) { if (e == container) return null; DependencyObject result = GetPreviousSibling(e); if (result != null) { if (IsGroup(result)) return result; else return GetLastInTree(result); } else return GetParent(e); } // Find the last element in the subtree private DependencyObject GetLastInTree(DependencyObject container) { DependencyObject result; do { result = container; container = GetLastChild(container); } while (container != null && !IsGroup(container)); if (container != null) return container; return result; } private DependencyObject GetGroupParent(DependencyObject e) { return GetGroupParent(e, false /*includeCurrent*/); } // Go up thru the parent chain until we find TabNavigation != Continue // In case all parents are Continue then return the root private DependencyObject GetGroupParent(DependencyObject e, bool includeCurrent) { Debug.Assert(e != null, "e cannot be null"); DependencyObject result = e; // Keep the last non null element // If we don't want to include the current element, // start at the parent of the element. If the element // is the root, then just return it as the group parent. if (!includeCurrent) { result = e; e = GetParent(e); if (e == null) { return result; } } while (e != null) { if (IsGroup(e)) return e; result = e; e = GetParent(e); } return result; } #endregion Tree navigation #region Logical Navigation private bool IsTabStop(DependencyObject e) { FrameworkElement fe = e as FrameworkElement; if (fe != null) return (fe.Focusable && (bool)fe.GetValue(IsTabStopProperty)) && fe.IsEnabled && fe.IsVisible; FrameworkContentElement fce = e as FrameworkContentElement; return fce != null && fce.Focusable && (bool)fce.GetValue(IsTabStopProperty) && fce.IsEnabled; } private bool IsGroup(DependencyObject e) { return GetKeyNavigationMode(e) != KeyboardNavigationMode.Continue; } private KeyboardNavigationMode GetKeyNavigationMode(DependencyObject e) { return (KeyboardNavigationMode)e.GetValue(_navigationProperty); } private bool IsTabStopOrGroup(DependencyObject e) { return IsTabStop(e) || IsGroup(e); } private static int GetTabIndexHelper(DependencyObject d) { return (int)d.GetValue(TabIndexProperty); } #region Tab Navigation // Find the element with highest priority (lowest index) inside the group private DependencyObject GetFirstTabInGroup(DependencyObject container) { DependencyObject firstTabElement = null; int minIndexFirstTab = Int32.MinValue; DependencyObject currElement = container; while ((currElement = GetNextInTree(currElement, container)) != null) { if (IsTabStopOrGroup(currElement)) { int currPriority = GetTabIndexHelper(currElement); if (currPriority < minIndexFirstTab || firstTabElement == null) { minIndexFirstTab = currPriority; firstTabElement = currElement; } } } return firstTabElement; } // Find the element with the same TabIndex after the current element private DependencyObject GetNextTabWithSameIndex(DependencyObject e, DependencyObject container) { int elementTabPriority = GetTabIndexHelper(e); DependencyObject currElement = e; while ((currElement = GetNextInTree(currElement, container)) != null) { if (IsTabStopOrGroup(currElement) && GetTabIndexHelper(currElement) == elementTabPriority) { return currElement; } } return null; } // Find the element with the next TabIndex after the current element private DependencyObject GetNextTabWithNextIndex(DependencyObject e, DependencyObject container, KeyboardNavigationMode tabbingType) { // Find the next min index in the tree // min (index>currentTabIndex) DependencyObject nextTabElement = null; DependencyObject firstTabElement = null; int minIndexFirstTab = Int32.MinValue; int minIndex = Int32.MinValue; int elementTabPriority = GetTabIndexHelper(e); DependencyObject currElement = container; while ((currElement = GetNextInTree(currElement, container)) != null) { if (IsTabStopOrGroup(currElement)) { int currPriority = GetTabIndexHelper(currElement); if (currPriority > elementTabPriority) { if (currPriority < minIndex || nextTabElement == null) { minIndex = currPriority; nextTabElement = currElement; } } if (currPriority < minIndexFirstTab || firstTabElement == null) { minIndexFirstTab = currPriority; firstTabElement = currElement; } } } // Cycle groups: if not found - return first element if (tabbingType == KeyboardNavigationMode.Cycle && nextTabElement == null) nextTabElement = firstTabElement; return nextTabElement; } private DependencyObject GetNextTabInGroup(DependencyObject e, DependencyObject container, KeyboardNavigationMode tabbingType) { // None groups: Tab navigation is not supported if (tabbingType == KeyboardNavigationMode.None) return null; // e == null or e == container -> return the first TabStopOrGroup if (e == null || e == container) { return GetFirstTabInGroup(container); } if (tabbingType == KeyboardNavigationMode.Once) return null; DependencyObject nextTabElement = GetNextTabWithSameIndex(e, container); if (nextTabElement != null) return nextTabElement; return GetNextTabWithNextIndex(e, container, tabbingType); } private DependencyObject GetNextTab(DependencyObject e, DependencyObject container, bool goDownOnly) { Debug.Assert(container != null, "container should not be null"); KeyboardNavigationMode tabbingType = GetKeyNavigationMode(container); if (e == null) { if (IsTabStop(container)) return container; // Using ActiveElement if set DependencyObject activeElement = GetActiveElement(container); if (activeElement != null) return GetNextTab(null, activeElement, true); } else { if (tabbingType == KeyboardNavigationMode.Once || tabbingType == KeyboardNavigationMode.None) { if (container != e) { if (goDownOnly) return null; DependencyObject parentContainer = GetGroupParent(container); return GetNextTab(container, parentContainer, goDownOnly); } } } // All groups DependencyObject loopStartElement = null; DependencyObject nextTabElement = e; KeyboardNavigationMode currentTabbingType = tabbingType; // Search down inside the container while ((nextTabElement = GetNextTabInGroup(nextTabElement, container, currentTabbingType)) != null) { Debug.Assert(IsTabStopOrGroup(nextTabElement), "nextTabElement should be IsTabStop or group"); // Avoid the endless loop here for Cycle groups if (loopStartElement == nextTabElement) break; if (loopStartElement == null) loopStartElement = nextTabElement; DependencyObject firstTabElementInside = GetNextTab(null, nextTabElement, true); if (firstTabElementInside != null) return firstTabElementInside; // If we want to continue searching inside the Once groups, we should change the navigation mode if (currentTabbingType == KeyboardNavigationMode.Once) currentTabbingType = KeyboardNavigationMode.Contained; } // If there is no next element in the group (nextTabElement == null) // Search up in the tree if allowed // if (!goDownOnly && currentTabbingType != KeyboardNavigationMode.Contained && GetParent(container) != null) { return GetNextTab(container, GetGroupParent(container), false); } return null; } #endregion Tab Navigation #region Shift+Tab Navigation private DependencyObject GetLastTabInGroup(DependencyObject container) { DependencyObject lastTabElement = null; int maxIndexFirstTab = Int32.MaxValue; DependencyObject currElement = GetLastInTree(container); while (currElement != null && currElement != container) { if (IsTabStopOrGroup(currElement)) { int currPriority = GetTabIndexHelper(currElement); if (currPriority > maxIndexFirstTab || lastTabElement == null) { maxIndexFirstTab = currPriority; lastTabElement = currElement; } } currElement = GetPreviousInTree(currElement, container); } return lastTabElement; } // Look for element with the same TabIndex before the current element private DependencyObject GetPrevTabWithSameIndex(DependencyObject e, DependencyObject container) { int elementTabPriority = GetTabIndexHelper(e); DependencyObject currElement = GetPreviousInTree(e, container); while (currElement != null) { if (IsTabStopOrGroup(currElement) && GetTabIndexHelper(currElement) == elementTabPriority && currElement != container) { return currElement; } currElement = GetPreviousInTree(currElement, container); } return null; } private DependencyObject GetPrevTabWithPrevIndex(DependencyObject e, DependencyObject container, KeyboardNavigationMode tabbingType) { // Find the next max index in the tree // max (index maxIndex || nextTabElement == null) { maxIndex = currPriority; nextTabElement = currElement; } } if (currPriority > maxIndexFirstTab || lastTabElement == null) { maxIndexFirstTab = currPriority; lastTabElement = currElement; } } currElement = GetPreviousInTree(currElement, container); } // Cycle groups: if not found - return first element if (tabbingType == KeyboardNavigationMode.Cycle && nextTabElement == null) nextTabElement = lastTabElement; return nextTabElement; } private DependencyObject GetPrevTabInGroup(DependencyObject e, DependencyObject container, KeyboardNavigationMode tabbingType) { // None groups: Tab navigation is not supported if (tabbingType == KeyboardNavigationMode.None) return null; // Search the last index inside the group if (e==null) { return GetLastTabInGroup(container); } if (tabbingType == KeyboardNavigationMode.Once) return null; if (e == container) return null; DependencyObject nextTabElement = GetPrevTabWithSameIndex(e, container); if (nextTabElement != null) return nextTabElement; return GetPrevTabWithPrevIndex(e, container, tabbingType); } private DependencyObject GetPrevTab(DependencyObject e, DependencyObject container, bool goDownOnly) { Debug.Assert(e != null || container != null, "e or container should not be null"); if (container == null) container = GetGroupParent(e); KeyboardNavigationMode tabbingType = GetKeyNavigationMode(container); if (e == null) { // Using ActiveElement if set DependencyObject activeElement = GetActiveElement(container); if (activeElement != null) return GetPrevTab(null, activeElement, true); else { // If we Shift+Tab on a container with KeyboardNavigationMode=Once, and ActiveElement is null // then we want to go to the fist item (not last) within the container if (tabbingType == KeyboardNavigationMode.Once) { DependencyObject firstTabElement = GetNextTabInGroup(null, container, tabbingType); if (firstTabElement == null) { if (IsTabStop(container)) return container; if (goDownOnly) return null; return GetPrevTab(container, null, false); } else { return GetPrevTab(null, firstTabElement, true); } } } } else { if (tabbingType == KeyboardNavigationMode.Once || tabbingType == KeyboardNavigationMode.None) { if (goDownOnly || container==e) return null; // FocusedElement should not be e otherwise we will delegate focus to the same element if (IsTabStop(container)) return container; return GetPrevTab(container, null, false); } } // All groups (except Once) - continue DependencyObject loopStartElement = null; DependencyObject nextTabElement = e; // Look for element with the same TabIndex before the current element while ((nextTabElement = GetPrevTabInGroup(nextTabElement, container, tabbingType)) != null) { if (nextTabElement == container && tabbingType == KeyboardNavigationMode.Local) break; // At this point nextTabElement is TabStop or TabGroup // In case it is a TabStop only return the element if (IsTabStop(nextTabElement) && !IsGroup(nextTabElement)) return nextTabElement; // Avoid the endless loop here if (loopStartElement == nextTabElement) break; if (loopStartElement == null) loopStartElement = nextTabElement; // At this point nextTabElement is TabGroup DependencyObject lastTabElementInside = GetPrevTab(null, nextTabElement, true); if (lastTabElementInside != null) return lastTabElementInside; } if (tabbingType == KeyboardNavigationMode.Contained) return null; if (e != container && IsTabStop(container)) return container; // If end of the subtree is reached or there no other elements above if (!goDownOnly && GetParent(container) != null) { return GetPrevTab(container, null, false); } return null; } #endregion Shift+Tab Navigation #endregion Logical Navigation #region Directional Navigation // return the element rectange relative to the root internal static Rect GetRectangle(DependencyObject element) { UIElement uiElement = element as UIElement; if (uiElement != null && uiElement.IsArrangeValid) { Visual rootVisual = GetVisualRoot(uiElement); if (rootVisual != null) { GeneralTransform transform = uiElement.TransformToAncestor(rootVisual); Thickness deflateThickness = (Thickness)uiElement.GetValue(DirectionalNavigationMarginProperty); double x = -deflateThickness.Left; double y = -deflateThickness.Top; double width = uiElement.RenderSize.Width + deflateThickness.Left + deflateThickness.Right; double height = uiElement.RenderSize.Height + deflateThickness.Top + deflateThickness.Bottom; if (width < 0) { x = uiElement.RenderSize.Width * 0.5; width = 0d; } if (height < 0) { y = uiElement.RenderSize.Height * 0.5; height = 0d; } return transform.TransformBounds(new Rect(x, y, width, height)); } } else { ContentElement ce = element as ContentElement; if (ce != null) { IContentHost parentICH = null; UIElement parentUIElement = GetParentUIElementFromContentElement(ce, ref parentICH); Visual parent = parentICH as Visual; if (parentICH != null && parent != null && parentUIElement != null) { Visual rootVisual = GetVisualRoot(parent); if (rootVisual != null && parentUIElement.IsMeasureValid) { // Note: Here we consider only the fist rectangle // Do we need to consider all of them as one combined rectangle? ReadOnlyCollection rects = parentICH.GetRectangles(ce); IEnumerator enumerator = rects.GetEnumerator(); if (enumerator.MoveNext()) { GeneralTransform transform = parent.TransformToAncestor(rootVisual); Rect rect = enumerator.Current; return transform.TransformBounds(rect); } } } } else { UIElement3D uiElement3D = element as UIElement3D; if (uiElement3D != null) { Visual rootVisual = GetVisualRoot(uiElement3D); Visual containingVisual2D = VisualTreeHelper.GetContainingVisual2D(uiElement3D); if (rootVisual != null && containingVisual2D != null) { Rect rectElement = uiElement3D.Visual2DContentBounds; GeneralTransform transform = containingVisual2D.TransformToAncestor(rootVisual); return transform.TransformBounds(rectElement); } } } } return Rect.Empty; } // distance between two points private double GetDistance(Point p1, Point p2) { double deltaX = p1.X - p2.X; double deltaY = p1.Y - p2.Y; return Math.Sqrt(deltaX * deltaX + deltaY * deltaY); } private double GetPerpDistance(Rect sourceRect, Rect targetRect, FocusNavigationDirection direction) { switch (direction) { case FocusNavigationDirection.Right : return targetRect.Left - sourceRect.Left; case FocusNavigationDirection.Left : return sourceRect.Right - targetRect.Right; case FocusNavigationDirection.Up : return sourceRect.Bottom - targetRect.Bottom; case FocusNavigationDirection.Down : return targetRect.Top - sourceRect.Top; default : throw new System.ComponentModel.InvalidEnumArgumentException("direction", (int)direction, typeof(FocusNavigationDirection)); } } // Example when moving down: // distance between sourceRect.TopLeft (or Y=vertical baseline) // and targetRect.TopLeft private double GetDistance(Rect sourceRect, Rect targetRect, FocusNavigationDirection direction) { Point startPoint; Point endPoint; switch (direction) { case FocusNavigationDirection.Right : startPoint = sourceRect.TopLeft; if (_horizontalBaseline != BASELINE_DEFAULT) startPoint.Y = _horizontalBaseline; endPoint = targetRect.TopLeft; break; case FocusNavigationDirection.Left : startPoint = sourceRect.TopRight; if (_horizontalBaseline != BASELINE_DEFAULT) startPoint.Y = _horizontalBaseline; endPoint = targetRect.TopRight; break; case FocusNavigationDirection.Up : startPoint = sourceRect.BottomLeft; if (_verticalBaseline != BASELINE_DEFAULT) startPoint.X = _verticalBaseline; endPoint = targetRect.BottomLeft; break; case FocusNavigationDirection.Down : startPoint = sourceRect.TopLeft; if (_verticalBaseline != BASELINE_DEFAULT) startPoint.X = _verticalBaseline; endPoint = targetRect.TopLeft; break; default : throw new System.ComponentModel.InvalidEnumArgumentException("direction", (int)direction, typeof(FocusNavigationDirection)); } return GetDistance(startPoint, endPoint); } // Example when moving down: // true if the top of the toRect is below the bottom of fromRect private bool IsInDirection(Rect fromRect, Rect toRect, FocusNavigationDirection direction) { switch (direction) { case FocusNavigationDirection.Right: return DoubleUtil.LessThanOrClose(fromRect.Right, toRect.Left); case FocusNavigationDirection.Left: return DoubleUtil.GreaterThanOrClose(fromRect.Left, toRect.Right); case FocusNavigationDirection.Up : return DoubleUtil.GreaterThanOrClose(fromRect.Top, toRect.Bottom); case FocusNavigationDirection.Down : return DoubleUtil.LessThanOrClose(fromRect.Bottom, toRect.Top); default: throw new System.ComponentModel.InvalidEnumArgumentException("direction", (int)direction, typeof(FocusNavigationDirection)); } } // The element is focus scope if IsFocusScope is true or it is the visual tree root private bool IsFocusScope(DependencyObject e) { return FocusManager.GetIsFocusScope(e) || GetParent(e) == null; } private bool IsAncestorOf(DependencyObject sourceElement, DependencyObject targetElement) { Visual sourceVisual = sourceElement as Visual; Visual targetVisual = targetElement as Visual; if (sourceVisual == null || targetVisual == null) return false; return sourceVisual.IsAncestorOf(targetVisual); } // Example: When moving down: // Range is the sourceRect width extended to the vertical baseline // targetRect.Top > sourceRect.Top (target is below the source) // targetRect.Right > sourceRect.Left || targetRect.Left < sourceRect.Right private bool IsInRange(DependencyObject sourceElement, DependencyObject targetElement, Rect sourceRect, Rect targetRect, FocusNavigationDirection direction, double startRange, double endRange) { switch (direction) { case FocusNavigationDirection.Right : case FocusNavigationDirection.Left : if (_horizontalBaseline != BASELINE_DEFAULT) { startRange = Math.Min(startRange, _horizontalBaseline); endRange = Math.Max(endRange, _horizontalBaseline); } if (DoubleUtil.GreaterThan(targetRect.Bottom, startRange) && DoubleUtil.LessThan(targetRect.Top, endRange)) { // If there is no sourceElement - checking the range is enough if (sourceElement == null) return true; if (direction == FocusNavigationDirection.Right) return DoubleUtil.GreaterThan(targetRect.Left, sourceRect.Left) || (DoubleUtil.AreClose(targetRect.Left, sourceRect.Left) && IsAncestorOf(sourceElement, targetElement)); else return DoubleUtil.LessThan(targetRect.Right, sourceRect.Right) || (DoubleUtil.AreClose(targetRect.Right, sourceRect.Right) && IsAncestorOf(sourceElement, targetElement)); } break; case FocusNavigationDirection.Up : case FocusNavigationDirection.Down : if (_verticalBaseline != BASELINE_DEFAULT) { startRange = Math.Min(startRange, _verticalBaseline); endRange = Math.Max(endRange, _verticalBaseline); } if (DoubleUtil.GreaterThan(targetRect.Right, startRange) && DoubleUtil.LessThan(targetRect.Left, endRange)) { // If there is no sourceElement - checking the range is enough if (sourceElement == null) return true; if (direction == FocusNavigationDirection.Down) return DoubleUtil.GreaterThan(targetRect.Top, sourceRect.Top) || (DoubleUtil.AreClose (targetRect.Top, sourceRect.Top) && IsAncestorOf(sourceElement, targetElement)); else return DoubleUtil.LessThan(targetRect.Bottom, sourceRect.Bottom) || (DoubleUtil.AreClose(targetRect.Bottom, sourceRect.Bottom) && IsAncestorOf(sourceElement, targetElement)); } break; default : throw new System.ComponentModel.InvalidEnumArgumentException("direction", (int)direction, typeof(FocusNavigationDirection)); } return false; } private DependencyObject GetNextInDirection(DependencyObject sourceElement, FocusNavigationDirection direction) { _containerHashtable.Clear(); DependencyObject targetElement = MoveNext(sourceElement, null, direction, BASELINE_DEFAULT, BASELINE_DEFAULT); if (targetElement != null) { UIElement sourceUIElement = sourceElement as UIElement; if (sourceUIElement != null) sourceUIElement.RemoveHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus)); else { ContentElement sourceContentElement = sourceElement as ContentElement; if (sourceContentElement != null) sourceContentElement.RemoveHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus)); } UIElement targetUIElement = targetElement as UIElement; if (targetUIElement == null) targetUIElement = GetParentUIElementFromContentElement(targetElement as ContentElement); else { ContentElement targetContentElement = targetElement as ContentElement; if (targetContentElement != null) { // When Focus is changed we need to reset the base line targetContentElement.AddHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus), true); } } if (targetUIElement != null) { // When layout is changed we need to reset the base line // Set up a layout invalidation listener. targetUIElement.LayoutUpdated += new EventHandler(OnLayoutUpdated); // When Focus is changed we need to reset the base line if (targetElement == targetUIElement) targetUIElement.AddHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus), true); } } _containerHashtable.Clear(); return targetElement; } // LayoutUpdated handler. private void OnLayoutUpdated(object sender, EventArgs e) { UIElement uiElement = sender as UIElement; // Disconnect the layout listener. if (uiElement != null) { uiElement.LayoutUpdated -= new EventHandler(OnLayoutUpdated); } _verticalBaseline = BASELINE_DEFAULT; _horizontalBaseline = BASELINE_DEFAULT; } private void _LostFocus(object sender, KeyboardFocusChangedEventArgs e) { _verticalBaseline = BASELINE_DEFAULT; _horizontalBaseline = BASELINE_DEFAULT; if (sender is UIElement) ((UIElement)sender).RemoveHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus)); else if (sender is ContentElement) ((ContentElement)sender).RemoveHandler(Keyboard.PreviewLostKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(_LostFocus)); } private bool IsEndlessLoop(DependencyObject element, DependencyObject container) { object elementObject = element != null ? (object)element : _fakeNull; // If entry exists then we have endless loop Hashtable elementTable = _containerHashtable[container] as Hashtable; if (elementTable != null) { if (elementTable[elementObject] != null) return true; } else { // Adding the entry to the collection elementTable = new Hashtable(10); _containerHashtable[container] = elementTable; } elementTable[elementObject] = BooleanBoxes.TrueBox; return false; } private void ResetBaseLines(double value, bool horizontalDirection) { if (horizontalDirection) { _verticalBaseline = BASELINE_DEFAULT; if (_horizontalBaseline == BASELINE_DEFAULT) _horizontalBaseline = value; } else // vertical direction { _horizontalBaseline = BASELINE_DEFAULT; if (_verticalBaseline == BASELINE_DEFAULT) _verticalBaseline = value; } } private DependencyObject FindNextInDirection(DependencyObject sourceElement, Rect sourceRect, DependencyObject container, FocusNavigationDirection direction, double startRange, double endRange) { DependencyObject result = null; Rect resultRect = Rect.Empty; double resultScore = 0d; bool searchInsideContainer = sourceElement == null; DependencyObject currElement = container; while ((currElement = GetNextInTree(currElement, container)) != null) { if (currElement != sourceElement && IsTabStopOrGroup(currElement)) { Rect currentRect = GetRectangle(currElement); bool isInDirection = IsInDirection(sourceRect, currentRect, direction); bool isInRange = IsInRange(sourceElement, currElement, sourceRect, currentRect, direction, startRange, endRange); if (searchInsideContainer || isInDirection || isInRange) { double score = isInRange ? GetPerpDistance(sourceRect, currentRect, direction) : GetDistance(sourceRect, currentRect, direction); // Keep the first element in the result if (result == null) { result = currElement; resultRect = currentRect; resultScore = score; } else if (DoubleUtil.LessThan(score, resultScore) || (DoubleUtil.AreClose(score, resultScore) && GetDistance(sourceRect, resultRect, direction) > GetDistance(sourceRect, currentRect, direction))) { result = currElement; resultRect = currentRect; resultScore = score; } } } } return result; } private DependencyObject MoveNext(DependencyObject sourceElement, DependencyObject container, FocusNavigationDirection direction, double startRange, double endRange) { Debug.Assert(!(sourceElement == null && container == null), "Both sourceElement and container cannot be null"); if (container == null) { container = GetGroupParent(sourceElement); Debug.Assert(container != null, "container cannot be null"); } // If we get to the tree root, return null if (container == sourceElement) return null; if (IsEndlessLoop(sourceElement, container)) return null; KeyboardNavigationMode mode = GetKeyNavigationMode(container); bool searchInsideContainer = (sourceElement == null); // Don't navigate inside None containers if (mode == KeyboardNavigationMode.None && searchInsideContainer) return null; Rect sourceRect = GetRectangle(searchInsideContainer ? container : sourceElement ); bool horizontalDirection = direction == FocusNavigationDirection.Right || direction == FocusNavigationDirection.Left; // Reset the baseline when we change the direction ResetBaseLines(horizontalDirection ? sourceRect.Top : sourceRect.Left, horizontalDirection); // If range is not set - use source rect if (startRange == BASELINE_DEFAULT || endRange == BASELINE_DEFAULT) { startRange = horizontalDirection ? sourceRect.Top : sourceRect.Left; endRange = horizontalDirection ? sourceRect.Bottom : sourceRect.Right; } // Navigate outside the container if (mode == KeyboardNavigationMode.Once && !searchInsideContainer) return MoveNext(container, null, direction, startRange, endRange); DependencyObject result = FindNextInDirection(sourceElement, sourceRect, container, direction, startRange, endRange); // If there is no next element in current container if (result == null) { switch (mode) { case KeyboardNavigationMode.Cycle: return MoveNext(null, container, direction, startRange, endRange); case KeyboardNavigationMode.Contained: return null; default: // Continue, Once, None, Local - search outside the container return MoveNext(container, null, direction, startRange, endRange); } } // If the element is focusable and IsTabStop is true if (IsTabStop(result)) return result; // Using ActiveElement if set DependencyObject activeElement = GetActiveElementChain(result); if (activeElement != null) return activeElement; // Try to find focus inside the element // result is not TabStop, which means it is a group DependencyObject insideElement = MoveNext(null, result, direction, startRange, endRange); if (insideElement != null) return insideElement; return MoveNext(result, null, direction, startRange, endRange); } private DependencyObject GetActiveElementChain(DependencyObject element) { DependencyObject validActiveElement = null; DependencyObject activeElement = element; while ((activeElement = GetActiveElement(activeElement)) != null) { if (IsTabStop(activeElement)) validActiveElement = activeElement; } return validActiveElement; } #endregion Directional Navigation #region Global tracking for entering MenuMode // ISSUE: how do we deal with deactivate? ///////////////////////////////////////////////////////////////////// /// /// Critical: accesses e.StagingItem.Input /// [SecurityCritical] private void ProcessForMenuMode(InputEventArgs inputEventArgs) { // When ALT or F10 key up happens we should fire the EnterMenuMode event. // We should not fire if: // * there were any handled input events in between the key down and corresponding key up. // * another unmatched keydown or keyup happened // * an unhandled mouse down/up happens if (inputEventArgs.RoutedEvent == Keyboard.LostKeyboardFocusEvent) { KeyboardFocusChangedEventArgs args = inputEventArgs as KeyboardFocusChangedEventArgs; if (((args != null) && (args.NewFocus == null)) || inputEventArgs.Handled) { // Focus went to null, stop tracking the last key down _lastKeyPressed = Key.None; } } // If a key is pressed down, remember it until the corresponding // key up. Ignore repeated keydowns. else if (inputEventArgs.RoutedEvent == Keyboard.KeyDownEvent) { if (inputEventArgs.Handled) _lastKeyPressed = Key.None; else { KeyEventArgs keyEventArgs = inputEventArgs as KeyEventArgs; if (!keyEventArgs.IsRepeat) { if (_lastKeyPressed == Key.None) { if ((Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Shift | ModifierKeys.Windows)) == ModifierKeys.None) { _lastKeyPressed = GetRealKey(keyEventArgs); } } else { // Another key was pressed down in between the one that we're tracking, so reset. _lastKeyPressed = Key.None; } // Clear this bit, Win32 will see message and clear QF_FMENUSTATUS. _win32MenuModeWorkAround = false; } } } // If a key up is received and matches the last key down // and is a key that would cause us to enter menumode, // raise the (internal) EnterMenuMode event. else if (inputEventArgs.RoutedEvent == Keyboard.KeyUpEvent) { if (!inputEventArgs.Handled) { KeyEventArgs keyEventArgs = inputEventArgs as KeyEventArgs; Key realKey = GetRealKey(keyEventArgs); if (realKey == _lastKeyPressed && IsMenuKey(realKey)) { EnableKeyboardCues(keyEventArgs.Source as DependencyObject, true); keyEventArgs.Handled = OnEnterMenuMode(keyEventArgs.Source); } if (_win32MenuModeWorkAround) { if (IsMenuKey(realKey)) { _win32MenuModeWorkAround = false; // Mark the event args as handled so that Win32 never // sees this key up and doesn't enter menu-mode. keyEventArgs.Handled = true; } } // If someone was listening for MenuMode and did something, // we need to make sure we don't let Win32 enter menu mode. else if (keyEventArgs.Handled) { // Set this bit to true, this means that we will handle // the next ALT-up if no one else does. _win32MenuModeWorkAround = true; } } // No matter what we should reset and not track the last key anymore. _lastKeyPressed = Key.None; } // The following input events act to "cancel" the EnterMenuMode event else if (inputEventArgs.RoutedEvent == Mouse.MouseDownEvent || inputEventArgs.RoutedEvent == Mouse.MouseUpEvent) { _lastKeyPressed = Key.None; // Win32 will see this message and will set QF_FMENUSTATUS to false. _win32MenuModeWorkAround = false; } } private bool IsMenuKey(Key key) { return (key == Key.LeftAlt || key == Key.RightAlt || key == Key.F10); } private Key GetRealKey(KeyEventArgs e) { return (e.Key == Key.System) ? e.SystemKey : e.Key; } ////// SecurityCritical:This code gets PresentationSource and passes it to event handlers /// TreatAsSafe: This code is safe inspite of passing the object because of 3 reasons /// 1. We have a demand on adding the event handler so that no one external can attach /// 2. The one event handler that we are aware of does not expose the object /// 3. This code in the worst case will cause your app to go to menu mode /// [SecurityCritical,SecurityTreatAsSafe] private bool OnEnterMenuMode(object eventSource) { if (_weakEnterMenuModeHandlers == null) return false; lock (_weakEnterMenuModeHandlers) { if (_weakEnterMenuModeHandlers.Count == 0) { return false; } // Bug 940610: no way to get PresentationSource of event in PostProcessInput // WORKAROUND: For now I will try to get the source of the event with // PresentationSource.FromVisual. If that fails, try to get the // source of the active window. PresentationSource source = null; if (eventSource != null) { Visual eventSourceVisual = eventSource as Visual; source = (eventSourceVisual != null) ? PresentationSource.CriticalFromVisual(eventSourceVisual) : null; } else { // If Keyboard.FocusedElement is null we'll have to fall back here. IntPtr activeWindow = MS.Win32.UnsafeNativeMethods.GetActiveWindow(); if (activeWindow != IntPtr.Zero) { source = HwndSource.CriticalFromHwnd(activeWindow); } } // Can't fire the event if the event didn't happen in any source if (source == null) { return false; } EventArgs e = EventArgs.Empty; bool handled = false; for (int i = 0; i < _weakEnterMenuModeHandlers.Count; i++) { EnterMenuModeEventHandler currentHandler = _weakEnterMenuModeHandlers[i].Target as EnterMenuModeEventHandler; if (currentHandler != null) { if (currentHandler(source, e)) { handled = true; break; } } else { _weakEnterMenuModeHandlers.RemoveAt(i); i--; } } return handled; } } ////// Called when ALT or F10 is pressed anywhere in the global scope /// ////// Critical: This code causes the handler attached to get an object of type presentationsource /// The add is critical, the remove is ok /// TreatAsSafe: There is a demand on this /// internal event EnterMenuModeEventHandler EnterMenuMode { [SecurityCritical,SecurityTreatAsSafe] add { SecurityHelper.DemandUIWindowPermission(); if (_weakEnterMenuModeHandlers == null) _weakEnterMenuModeHandlers = new List(1); lock (_weakEnterMenuModeHandlers) { // Cleanup the list in case some of the weakEnterMenuModeHandlers is disposed for (int i = 0; i < _weakEnterMenuModeHandlers.Count; i++) { if (!(_weakEnterMenuModeHandlers[i].Target is EnterMenuModeEventHandler)) { _weakEnterMenuModeHandlers.RemoveAt(i); i--; } } _weakEnterMenuModeHandlers.Add(new WeakReference(value)); } } remove { if (_weakEnterMenuModeHandlers != null) { lock (_weakEnterMenuModeHandlers) { for (int i = 0; i < _weakEnterMenuModeHandlers.Count; i++) { EnterMenuModeEventHandler current = _weakEnterMenuModeHandlers[i].Target as EnterMenuModeEventHandler; if (current == null || current == value) { _weakEnterMenuModeHandlers.RemoveAt(i); i--; } } } } } } internal delegate bool EnterMenuModeEventHandler(object sender, EventArgs e); // Used to track what the last key was pressed so that // we can fire the EnterMenuMode event. // Will be reset to Key.None when an unmatched KeyUp or other input event happens private Key _lastKeyPressed = Key.None; // List of WeakReferences to delegates to be invoked when EnterMenuMode happens private List _weakEnterMenuModeHandlers; // Fix for bug 936302: (JevanSa) // The DefaultWindowProcWorker (windows/core/ntuser/kernel/dwp.c) // listens for ALT down followed by ALT up with nothing in between. // When ALT goes down they set QF_FMENUSTATUS. When ALT up happens, // if QF_FMENUSTATUS is still set, they open the system menu (or // menu for the window if there is one). If any keystrokes happen // in between, they clear QF_FMENUSTATUS. // // Consider the following sequence: // 1) KeyDown(Alt) - neither Win32 nor Avalon respond // 2) KeyUp(Alt) - Avalon handles the event, Win32 is skipped // 3) KeyDown(Alt) - Avalon handles the event, Win32 is skipped // 4) KeyUp(Alt) - Avalon does not respond, Win32 handles the message // (and enters "Invisible" MenuMode) // // Here, from the point of view of the DWP, there was just ALT down // followed by ALT up. We must fool the DWP somehow so that they // clear clear the QF_FMENUSTATUS bit before #4. // // Currently the best way DwayneN and I have come up with is to // mark the event has handled in case #4 so that the DWP // never sees the ALT up in #4. We set this bit when #2 happens. // If we see an unhandled ALT-up and this bit is set, we mark the // event as handled. If we see any unhandled key down or mouse up/down // we can clear this bit. private bool _win32MenuModeWorkAround; #endregion #region UIState /// /// Critical: accesses the RawUIStateInputReport /// [SecurityCritical] private void ProcessForUIState(InputEventArgs inputEventArgs) { PresentationSource source; RawUIStateInputReport report = ExtractRawUIStateInputReport(inputEventArgs, InputManager.InputReportEvent); if (report != null && (source = report.InputSource) != null) { // handle accelerator cue display if ((report.Targets & RawUIStateTargets.HideAccelerators) != 0) { Visual root = source.RootVisual; bool enable = (report.Action == RawUIStateActions.Clear); EnableKeyboardCues(root, enable); } } } ////// Critical: accesses the RawUIStateInputReport /// [SecurityCritical] private RawUIStateInputReport ExtractRawUIStateInputReport(InputEventArgs e, RoutedEvent Event) { RawUIStateInputReport uiStateInputReport = null; InputReportEventArgs input = e as InputReportEventArgs; if (input != null) { if (input.Report.Type == InputType.Keyboard && input.RoutedEvent == Event) { uiStateInputReport = input.Report as RawUIStateInputReport; } } return uiStateInputReport; } #endregion UIState #region FocusEnterMainFocusScope weak event // The event is raised when KeyboardFocus enters the main focus scope (visual tree root) // Selector and TreeView listen for this event to update their ActiveSelection property internal event EventHandler FocusEnterMainFocusScope { add { lock (_weakFocusEnterMainFocusScopeHandlers) { _weakFocusEnterMainFocusScopeHandlers.Add(new WeakReference(value)); } } remove { lock (_weakFocusEnterMainFocusScopeHandlers) { for (int i = 0; i < _weakFocusEnterMainFocusScopeHandlers.Count; i++) { object handler = _weakFocusEnterMainFocusScopeHandlers[i].Target; if (handler == null || (EventHandler)handler == value) { _weakFocusEnterMainFocusScopeHandlers.RemoveAt(i); i--; } } } } } private void NotifyFocusEnterMainFocusScope(object sender, EventArgs e) { if (_weakFocusEnterMainFocusScopeHandlers != null) { for (int i = 0; i < _weakFocusEnterMainFocusScopeHandlers.Count; i++) { EventHandler handler = _weakFocusEnterMainFocusScopeHandlers[i].Target as EventHandler; if (handler != null) { handler(sender, e); } else { _weakFocusEnterMainFocusScopeHandlers.RemoveAt(i); i--; } } } } private List_weakFocusEnterMainFocusScopeHandlers = new List (1); #endregion #region Data private const double BASELINE_DEFAULT = Double.MinValue; private double _verticalBaseline = BASELINE_DEFAULT; private double _horizontalBaseline = BASELINE_DEFAULT; private DependencyProperty _navigationProperty = null; private Hashtable _containerHashtable = new Hashtable(10); private static object _fakeNull = new object(); #endregion Data } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CounterCreationData.cs
- FontEmbeddingManager.cs
- PopOutPanel.cs
- WebRequestModulesSection.cs
- SiteMapNode.cs
- WindowsListViewGroupHelper.cs
- PersonalizationStateQuery.cs
- TextDecoration.cs
- ContainerParagraph.cs
- DataGridViewCheckBoxColumn.cs
- LinqDataSourceUpdateEventArgs.cs
- HiddenFieldDesigner.cs
- Symbol.cs
- KeyConstraint.cs
- BaseProcessor.cs
- CompilerResults.cs
- AnonymousIdentificationSection.cs
- SizeKeyFrameCollection.cs
- FunctionImportMapping.cs
- ControlCachePolicy.cs
- ConfigDefinitionUpdates.cs
- ControlCodeDomSerializer.cs
- RoutedEvent.cs
- FontFaceLayoutInfo.cs
- DecoderExceptionFallback.cs
- ZipPackagePart.cs
- EntityDataSourceColumn.cs
- SamlAuthorizationDecisionStatement.cs
- MediaElementAutomationPeer.cs
- AutoSizeComboBox.cs
- RtfControls.cs
- SpellerHighlightLayer.cs
- AttributeTableBuilder.cs
- AutoSizeComboBox.cs
- DictionaryEntry.cs
- TreePrinter.cs
- Int64AnimationBase.cs
- Mouse.cs
- TraceContextEventArgs.cs
- SortQuery.cs
- GridViewRowEventArgs.cs
- OracleInternalConnection.cs
- RoleGroup.cs
- DocumentPaginator.cs
- SqlExpander.cs
- webclient.cs
- DataGridViewSortCompareEventArgs.cs
- RuntimeHelpers.cs
- ObjectDataSourceStatusEventArgs.cs
- FontWeight.cs
- InputScopeConverter.cs
- EmptyEnumerable.cs
- GroupLabel.cs
- ViewBase.cs
- HtmlGenericControl.cs
- Composition.cs
- DecoratedNameAttribute.cs
- CssStyleCollection.cs
- MergablePropertyAttribute.cs
- RectAnimationBase.cs
- MetadataItemSerializer.cs
- DetailsViewRow.cs
- DetailsViewModeEventArgs.cs
- CompilerParameters.cs
- TaskDesigner.cs
- BaseCollection.cs
- ButtonChrome.cs
- XmlCodeExporter.cs
- Image.cs
- SmtpReplyReader.cs
- ComponentManagerBroker.cs
- FixedSOMLineCollection.cs
- InputReferenceExpression.cs
- KeyValuePairs.cs
- InputDevice.cs
- SimplePropertyEntry.cs
- NullableFloatSumAggregationOperator.cs
- Shape.cs
- ErrorsHelper.cs
- TagMapCollection.cs
- SystemWebCachingSectionGroup.cs
- DataComponentGenerator.cs
- AutomationElementCollection.cs
- WebPartDeleteVerb.cs
- SchemaInfo.cs
- ScopedKnownTypes.cs
- XPathSelfQuery.cs
- HijriCalendar.cs
- CustomAttributeSerializer.cs
- AssemblyAttributesGoHere.cs
- CodeExpressionStatement.cs
- RoutedEvent.cs
- GraphicsContainer.cs
- TextSelectionHelper.cs
- RtType.cs
- AutomationPeer.cs
- EventLogEntryCollection.cs
- AttributeUsageAttribute.cs
- WindowsFormsDesignerOptionService.cs
- CheckBox.cs