/// <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();
            }
        }
예제 #2
0
        /// <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);
        }
예제 #4
0
        /// <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);
        }
예제 #5
0
        /// <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;
        }
예제 #6
0
        /// <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);
            }
        }
예제 #7
0
        /// <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());
            }
        }
예제 #8
0
        /// <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());
            }
        }
예제 #9
0
        /// <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());
            }
        }
예제 #10
0
        /// <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;
        }