2
votes

Why doesn't keyboard input work for a ScrollViewer when the child control has input focus?

This is the scenario. A WPF window opens. It sets the focus to a control that is embedded in a ScrollViewer.

I hit the up and down and left and right keys. The ScrollViewer doesn't seem to handle the key events, anyone know why?

This is the simplest possible example:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300"
    FocusManager.FocusedElement="{Binding ElementName=control}"
    >
    <Grid>
        <ScrollViewer
            HorizontalScrollBarVisibility="Auto"
           >
            <ItemsControl
                x:Name="control"
                Width="1000"
                Height="1000"
                />
        </ScrollViewer>        
    </Grid>
</Window>

When you start the app that contains this window, "control" appears to have the focus as I intended. Pressing the key seems to result in bubbling key events reaching the ScrollViewer (I checked for this using WPF Snoop). I can't work out why it doesn't respond to the input.

3

3 Answers

8
votes

The problem

A ScrollViewer ignores all KeyDown events whose OriginalSource is not the ScrollViewer. The OriginalSource on a KeyDown is set to the focused control, therefore the ScrollViewer ignores it when a child has the focus.

The solution

Catch the KeyDown event and raise a copy of it directly on the ScrollViewer so it will have the correct OriginalSource, like this:

void ScrollViewer_KeyDown(object sender, KeyEventArgs e)
{
  if(e.Handled) return;
  var temporaryEventArgs =
    new KeyEventArgs(e.KeyboardDevice, e.InputSource, e.Timestamp, e.Key)
    {
      RoutedEvent = e.RoutedEvent
    };
  // This line avoids it from resulting in a stackoverflowexception
  if (sender is ScrollViewer) return;
  ((ScrollViewer)sender).RaiseEvent(temporaryEventArgs);
  e.Handled = temporaryEventArgs.Handled;
}

the event handler can be added in XAML:

<ScrollViewer KeyDown="ScrollViewer_KeyDown" />

or in code:

scrollViewer.AddHandler(Keyboard.KeyDownEvent, ScrollViewer_KeyDown);

The latter is more applicable if the ScrollViewer is inside a template somewhere and you have code to find it.

0
votes

In order for the ScrollViewer to react to your keyboard keys - it needs to have IsFocused="True" - right now it's child has the focus.

To prove it - try in your Loaded event to manually set focus to the ScrollViewer (you might have to set IsFocusable="True" for it to work on the ScrollViewer) - now the keys should work. If you want it to work otherwise, you need to set the appropriate EventHandlers on the ScrollViewer (KeyDown/KeyPress etc.)

0
votes

Similar issue with no keyboard navigation when using ScrollViewer inside an ItemsControl.Template. Adding Focusable and IsTabStop resolved the issue in my case.

<ScrollViewer
    Focusable="True"
    IsTabStop="True">
    <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
 </ScrollViewer>