/// <summary> /// Gets the next element in tab order. /// </summary> /// <param name="element">The element.</param> /// <returns>The next element in tab order.</returns> public static IInputElement GetNextInTabOrder(IInputElement element) { Contract.Requires<ArgumentNullException>(element != null); var container = element.GetVisualParent<IInputElement>(); if (container != null) { var mode = KeyboardNavigation.GetTabNavigation((InputElement)container); switch (mode) { case KeyboardNavigationMode.Continue: return GetNextInContainer(element, container) ?? GetFirstInNextContainer(container); case KeyboardNavigationMode.Cycle: return GetNextInContainer(element, container) ?? GetDescendents(container).FirstOrDefault(); default: return GetFirstInNextContainer(container); } } else { return GetDescendents(element).FirstOrDefault(); } }
/// <summary> /// Gets the next control in the specified navigation direction. /// </summary> /// <param name="element">The element.</param> /// <param name="direction">The navigation direction.</param> /// <returns> /// The next element in the specified direction, or null if <paramref name="element"/> /// was the last in the requested direction. /// </returns> public static IInputElement GetNext( IInputElement element, FocusNavigationDirection direction) { Contract.Requires<ArgumentNullException>(element != null); Contract.Requires<ArgumentException>( direction != FocusNavigationDirection.Next && direction != FocusNavigationDirection.Previous); var container = element.GetVisualParent<IInputElement>(); if (container != null) { var isForward = IsForward(direction); var mode = KeyboardNavigation.GetDirectionalNavigation((InputElement)container); switch (mode) { case KeyboardNavigationMode.Continue: return GetNextInContainer(element, container, direction) ?? GetFirstInNextContainer(element, direction); case KeyboardNavigationMode.Cycle: return GetNextInContainer(element, container, direction) ?? GetFocusableDescendent(container, direction); case KeyboardNavigationMode.Contained: return GetNextInContainer(element, container, direction); default: return null; } } else { return GetFocusableDescendents(element).FirstOrDefault(); } }
/// <summary> /// Gets the first item that should be focused in the next container. /// </summary> /// <param name="element">The element being navigated away from.</param> /// <param name="container">The container.</param> /// <param name="direction">The direction of the search.</param> /// <returns>The first element, or null if there are no more elements.</returns> private static IInputElement GetFirstInNextContainer( IInputElement element, IInputElement container, NavigationDirection direction) { var parent = container.GetVisualParent <IInputElement>(); IInputElement next = null; if (parent != null) { if (direction == NavigationDirection.Previous && parent.CanFocus()) { return(parent); } var allSiblings = parent.GetVisualChildren() .OfType <IInputElement>() .Where(FocusExtensions.CanFocusDescendants); var siblings = direction == NavigationDirection.Next ? allSiblings.SkipWhile(x => x != container).Skip(1) : allSiblings.TakeWhile(x => x != container).Reverse(); foreach (var sibling in siblings) { var customNext = GetCustomNext(sibling, direction); if (customNext.handled) { return(customNext.next); } if (sibling.CanFocus()) { return(sibling); } else { next = direction == NavigationDirection.Next ? GetFocusableDescendants(sibling, direction).FirstOrDefault() : GetFocusableDescendants(sibling, direction).LastOrDefault(); if (next != null) { return(next); } } } if (next == null) { next = GetFirstInNextContainer(element, parent, direction); } } else { next = direction == NavigationDirection.Next ? GetFocusableDescendants(container, direction).FirstOrDefault() : GetFocusableDescendants(container, direction).LastOrDefault(); } return(next); }
/// <summary> /// Gets the first item that should be focused in the next container. /// </summary> /// <param name="container">The container.</param> /// <param name="direction">The direction of the search.</param> /// <returns>The first element, or null if there are no more elements.</returns> private static IInputElement GetFirstInNextContainer( IInputElement container, FocusNavigationDirection direction) { var parent = container.GetVisualParent <IInputElement>(); var isForward = IsForward(direction); IInputElement next = null; if (parent != null) { if (!isForward && parent.CanFocus()) { return(parent); } var siblings = parent.GetVisualChildren() .OfType <IInputElement>() .Where(FocusExtensions.CanFocusDescendents); IInputElement sibling; if (isForward) { sibling = siblings.SkipWhile(x => x != container).Skip(1).FirstOrDefault(); } else { sibling = siblings.TakeWhile(x => x != container).LastOrDefault(); } if (sibling != null) { if (sibling.CanFocus()) { next = sibling; } else { next = isForward ? GetFocusableDescendents(sibling).FirstOrDefault() : GetFocusableDescendents(sibling).LastOrDefault(); } } if (next == null) { next = GetFirstInNextContainer(parent, direction); } } else { next = isForward ? GetFocusableDescendents(container).FirstOrDefault() : GetFocusableDescendents(container).LastOrDefault(); } return(next); }
/// <summary> /// Gets the first item that should be focused in the next container. /// </summary> /// <param name="container">The container.</param> /// <param name="direction">The direction of the search.</param> /// <returns>The first element, or null if there are no more elements.</returns> private static IInputElement GetFirstInNextContainer( IInputElement container, FocusNavigationDirection direction) { var parent = container.GetVisualParent<IInputElement>(); var isForward = IsForward(direction); IInputElement next = null; if (parent != null) { if (!isForward && parent.CanFocus()) { return parent; } var siblings = parent.GetVisualChildren() .OfType<IInputElement>() .Where(FocusExtensions.CanFocusDescendents); IInputElement sibling; if (isForward) { sibling = siblings.SkipWhile(x => x != container).Skip(1).FirstOrDefault(); } else { sibling = siblings.TakeWhile(x => x != container).LastOrDefault(); } if (sibling != null) { if (sibling.CanFocus()) { next = sibling; } else { next = isForward ? GetFocusableDescendents(sibling).FirstOrDefault() : GetFocusableDescendents(sibling).LastOrDefault(); } } if (next == null) { next = GetFirstInNextContainer(parent, direction); } } else { next = isForward ? GetFocusableDescendents(container).FirstOrDefault() : GetFocusableDescendents(container).LastOrDefault(); } return next; }
/// <summary> /// Gets the focus scope ancestors of the specified control, traversing popups. /// </summary> /// <param name="control">The control.</param> /// <returns>The focus scopes.</returns> private static IEnumerable <IFocusScope> GetFocusScopeAncestors(IInputElement control) { while (control != null) { var scope = control as IFocusScope; if (scope != null && control.VisualRoot?.IsVisible == true) { yield return(scope); } control = control.GetVisualParent <IInputElement>() ?? ((control as IHostedVisualTreeRoot)?.Host as IInputElement); } }
/// <summary> /// Gets the next control in the specified tab direction. /// </summary> /// <param name="element">The element.</param> /// <param name="direction">The tab direction. Must be Next or Previous.</param> /// <param name="outsideElement"> /// If true will not descend into <paramref name="element"/> to find next control. /// </param> /// <returns> /// The next element in the specified direction, or null if <paramref name="element"/> /// was the last in the requested direction. /// </returns> public static IInputElement?GetNextInTabOrder( IInputElement element, NavigationDirection direction, bool outsideElement = false) { element = element ?? throw new ArgumentNullException(nameof(element)); if (direction != NavigationDirection.Next && direction != NavigationDirection.Previous) { throw new ArgumentException("Invalid direction: must be Next or Previous."); } var container = element.GetVisualParent <IInputElement>(); if (container != null) { var mode = KeyboardNavigation.GetTabNavigation((InputElement)container); switch (mode) { case KeyboardNavigationMode.Continue: return(GetNextInContainer(element, container, direction, outsideElement) ?? GetFirstInNextContainer(element, element, direction)); case KeyboardNavigationMode.Cycle: return(GetNextInContainer(element, container, direction, outsideElement) ?? GetFocusableDescendant(container, direction)); case KeyboardNavigationMode.Contained: return(GetNextInContainer(element, container, direction, outsideElement)); default: return(GetFirstInNextContainer(element, container, direction)); } } else { return(GetFocusableDescendants(element, direction).FirstOrDefault()); } }
/// <summary> /// Gets the next control in the specified navigation direction. /// </summary> /// <param name="element">The element.</param> /// <param name="direction">The navigation direction.</param> /// <returns> /// The next element in the specified direction, or null if <paramref name="element"/> /// was the last in the requested direction. /// </returns> public static IInputElement GetNext( IInputElement element, FocusNavigationDirection direction) { Contract.Requires <ArgumentNullException>(element != null); Contract.Requires <ArgumentException>( direction != FocusNavigationDirection.Next && direction != FocusNavigationDirection.Previous); var container = element.GetVisualParent <IInputElement>(); if (container != null) { var isForward = IsForward(direction); var mode = KeyboardNavigation.GetDirectionalNavigation((InputElement)container); switch (mode) { case KeyboardNavigationMode.Continue: return(GetNextInContainer(element, container, direction) ?? GetFirstInNextContainer(element, direction)); case KeyboardNavigationMode.Cycle: return(GetNextInContainer(element, container, direction) ?? GetFocusableDescendent(container, direction)); case KeyboardNavigationMode.Contained: return(GetNextInContainer(element, container, direction)); default: return(null); } } else { return(GetFocusableDescendents(element).FirstOrDefault()); } }
/// <summary> /// Gets the next control in the specified tab direction. /// </summary> /// <param name="element">The element.</param> /// <param name="direction">The tab direction. Must be Next or Previous.</param> /// <param name="outsideElement"> /// If true will not descend into <paramref name="element"/> to find next control. /// </param> /// <returns> /// The next element in the specified direction, or null if <paramref name="element"/> /// was the last in the requested direction. /// </returns> public static IInputElement GetNextInTabOrder( IInputElement element, NavigationDirection direction, bool outsideElement = false) { Contract.Requires <ArgumentNullException>(element != null); Contract.Requires <ArgumentException>( direction == NavigationDirection.Next || direction == NavigationDirection.Previous); var container = element.GetVisualParent <IInputElement>(); if (container != null) { var mode = KeyboardNavigation.GetTabNavigation((InputElement)container); switch (mode) { case KeyboardNavigationMode.Continue: return(GetNextInContainer(element, container, direction, outsideElement) ?? GetFirstInNextContainer(element, element, direction)); case KeyboardNavigationMode.Cycle: return(GetNextInContainer(element, container, direction, outsideElement) ?? GetFocusableDescendant(container, direction)); case KeyboardNavigationMode.Contained: return(GetNextInContainer(element, container, direction, outsideElement)); default: return(GetFirstInNextContainer(element, container, direction)); } } else { return(GetFocusableDescendants(element, direction).FirstOrDefault()); } }
/// <summary> /// Gets the focus scope ancestors of the specified control, traversing popups. /// </summary> /// <param name="control">The control.</param> /// <returns>The focus scopes.</returns> private static IEnumerable<IFocusScope> GetFocusScopeAncestors(IInputElement control) { while (control != null) { var scope = control as IFocusScope; if (scope != null) { yield return scope; } control = control.GetVisualParent<IInputElement>() ?? ((control as IHostedVisualTreeRoot)?.Host as IInputElement); } }
/// <summary> /// Gets the last item that should be focused in the previous container. /// </summary> /// <param name="container">The container.</param> /// <returns>The next element, or null if there are no more elements.</returns> private static IInputElement GetLastInPreviousContainer(IInputElement container) { var parent = container.GetVisualParent<IInputElement>(); IInputElement next = null; if (parent != null) { var sibling = parent.GetVisualChildren() .OfType<IInputElement>() .Where(CanFocusDescendent) .TakeWhile(x => x != container) .LastOrDefault(); if (sibling != null) { if (CanFocus(sibling)) { next = sibling; } else { next = GetDescendents(sibling).LastOrDefault(); } } if (next == null) { next = GetLastInPreviousContainer(parent); } } else { next = GetDescendents(container).LastOrDefault(); } return next; }