Oli
Oli

Reputation: 3136

Keyboard Navigation In ScrollViewer TextBlock

I have a Textblock within a scrollviewer around it. My application is completely controlled by remote so in this context navigation is performed with key up, down, left and right.

I can navigate into the Textblock but then I get trapped there. I've tried placing KeyboardNavigation.DirectionalNavigation="Continue" in everycontrol I can but no joy.

I then thought of making a customcontrol that extends the scrollbar or the scrollviewer. If extending the scrollbar I can override keydown as per the following.

protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        if (this.Orientation == Orientation.Vertical)
        {
            if (e.Key == Key.Up)
            {
                if (this.Value == this.Minimum)
                {
                    this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Up));
                    e.Handled = true;
                }
            }
            if (e.Key == Key.Down)
            {
                if (this.Value == this.Maximum)
                {
                    this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
                    e.Handled = true;
                }
            }
            if (e.Key == Key.Left)
            {
                this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Left));
                e.Handled = true;
            }
            if (e.Key == Key.Right)
            {
                this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
                e.Handled = true;
            }
        }

        if (this.Orientation == Orientation.Horizontal)
        {
            if (e.Key == Key.Left)
            {
                if (this.Value == this.Minimum)
                {
                    this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Left));
                    e.Handled = true;
                }
            }
            if (e.Key == Key.Right)
            {
                if (this.Value == this.Maximum)
                {
                    this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
                    e.Handled = true;
                }
            }
            if (e.Key == Key.Up)
            {
                this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Up));
                e.Handled = true;
            }
            if (e.Key == Key.Down)
            {
                this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
                e.Handled = true;
            }
        }
        base.OnPreviewKeyDown(e);
    }
}

The problem is I'm not sure how to change the ScrollViewer scrollbar to use a custom one or even if the code above will even fire when a key is pressed. I presume the text block and the scrollviewer will be the controls that see the event.

Is there a way to do something similar to the code above but in the Scrollviewers code behind?

Upvotes: 0

Views: 1455

Answers (1)

Oli
Oli

Reputation: 3136

I solved this in the end by creating a custom control. I work out if the scrollviewer can scroll in the direction the key has been pressed. If yes the event passes through to the underlying scrollviewer. If not the event is marked as handled and the focus is moved in the direction of the key press.

public class KNScrollViewer : ScrollViewer
{
    static KNScrollViewer()
    {

    }

    private bool canScrollUp
    {
        get
        {
            return this.ScrollableHeight > 0 && this.VerticalOffset > 0;
        }
    }

    private bool canScrollDown
    {
        get
        {
            return this.ScrollableHeight > 0 &&
              this.VerticalOffset + this.ViewportHeight < this.ExtentHeight;
        }
    }

    private bool canScrollLeft
    {
        get
        {
            return this.ScrollableWidth > 0 && this.HorizontalOffset > 0;
        }
    }

    private bool canScrollRight
    {
        get
        {
            return this.ScrollableWidth > 0 &&
            this.HorizontalOffset + this.ViewportWidth < this.ExtentWidth;
        }
    }

    public bool CanScroll
    {
        get
        {
            if (canScrollUp || canScrollDown || canScrollLeft || canScrollRight)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        if (e.Key == Key.Up)
        {
            if (!canScrollUp)
            {
                this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Up));
                e.Handled = true;
            }
        }
        if (e.Key == Key.Down)
        {
            if (!canScrollDown)
            {
                this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
                e.Handled = true;
            }
        }
        if (e.Key == Key.Left)
        {
            if (!canScrollLeft)
            {
                this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Left));
                e.Handled = true;
            }
        }
        if (e.Key == Key.Right)
        {
            if (!canScrollRight)
            {
                this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
                e.Handled = true;
            }
        }
    }
}

Upvotes: 1

Related Questions