Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Input / KeyboardNavigation.cs / 3 / 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();
}
bool applyTransform = contentHost != AdornedElement;
Rect desiredRect = Rect.Empty;
IEnumerator enumerator = _contentRects.GetEnumerator();
GeneralTransform transform = null;
if (applyTransform)
transform = ((Visual)contentHost).TransformToAncestor(AdornedElement);
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;
if (applyTransform)
rect = transform.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;
if (applyTransform)
rect = transform.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)
{
ReadOnlyCollection oldRects = _contentRects;
_contentRects = null;
IContentHost contentHost = ContentHost;
if (contentHost != null)
{
_contentRects = contentHost.GetRectangles(_adornedContentElement);
}
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;
IEnumerator enumerator = 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
//
/////////////////////////////////////////////////////////////////////
///
/// 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: ([....])
// 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 [....] 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
- NativeActivityMetadata.cs
- ImpersonateTokenRef.cs
- XMLSyntaxException.cs
- LinkButton.cs
- AttachedPropertyDescriptor.cs
- DiagnosticsElement.cs
- TextRangeEdit.cs
- TransferRequestHandler.cs
- SynchronizationLockException.cs
- XmlSchemaException.cs
- RenderingBiasValidation.cs
- SID.cs
- GetRecipientRequest.cs
- OptimalTextSource.cs
- Filter.cs
- SimpleTypeResolver.cs
- GenericWebPart.cs
- QueryStringParameter.cs
- ScrollContentPresenter.cs
- StringResourceManager.cs
- DeclarativeCatalogPart.cs
- ExpressionBuilder.cs
- PasswordRecovery.cs
- CustomUserNameSecurityTokenAuthenticator.cs
- ChannelManagerService.cs
- ProxyGenerator.cs
- AppLevelCompilationSectionCache.cs
- LongValidator.cs
- XmlAutoDetectWriter.cs
- DayRenderEvent.cs
- TextRenderer.cs
- UnsafeNativeMethods.cs
- TabControlToolboxItem.cs
- SmtpClient.cs
- COM2ColorConverter.cs
- StylusPointPropertyInfoDefaults.cs
- StringFunctions.cs
- HttpConfigurationContext.cs
- WindowsTreeView.cs
- DataGridLength.cs
- DefaultAsyncDataDispatcher.cs
- DataSetUtil.cs
- SuppressIldasmAttribute.cs
- CollaborationHelperFunctions.cs
- TypeGenericEnumerableViewSchema.cs
- HttpConfigurationSystem.cs
- WorkflowHostingEndpoint.cs
- xmlformatgeneratorstatics.cs
- SystemNetworkInterface.cs
- SettingsSection.cs
- ModuleElement.cs
- Help.cs
- RequestSecurityTokenResponse.cs
- PropertyChangedEventArgs.cs
- GuidelineCollection.cs
- exports.cs
- CodeMemberField.cs
- COM2TypeInfoProcessor.cs
- Size3D.cs
- AttachedPropertyBrowsableAttribute.cs
- EnumCodeDomSerializer.cs
- LinkArea.cs
- DataListItem.cs
- AutomationAttributeInfo.cs
- Calendar.cs
- _NetworkingPerfCounters.cs
- AttachmentService.cs
- LiteralDesigner.cs
- Comparer.cs
- DesignerWebPartChrome.cs
- VoiceChangeEventArgs.cs
- InputScope.cs
- UrlAuthorizationModule.cs
- PolyBezierSegment.cs
- InternalConfigEventArgs.cs
- TagPrefixInfo.cs
- UICuesEvent.cs
- QilName.cs
- XmlNodeChangedEventArgs.cs
- SecureEnvironment.cs
- SmtpSpecifiedPickupDirectoryElement.cs
- TrackingStringDictionary.cs
- IsolationInterop.cs
- HtmlWindow.cs
- ClientScriptItem.cs
- HelpHtmlBuilder.cs
- oledbconnectionstring.cs
- _PooledStream.cs
- _HTTPDateParse.cs
- Adorner.cs
- NullableIntMinMaxAggregationOperator.cs
- DataSourceBooleanViewSchemaConverter.cs
- TypeConverterHelper.cs
- ClassValidator.cs
- OperationAbortedException.cs
- Axis.cs
- Wrapper.cs
- WeakEventTable.cs
- HtmlContainerControl.cs
- PartialCachingControl.cs