为什么当子控件获得输入焦点时,滚动视图中的键盘输入无法生效?

3

当子控件获得输入焦点时,为什么ScrollViewer无法接收键盘输入?

场景如下:打开一个WPF窗口,将焦点设置到嵌入在ScrollViewer中的控件上。

我按上下左右键,但是ScrollViewer似乎无法处理这些键盘事件,有人知道原因吗?

以下是最简单的示例:

<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>

当您启动包含此窗口的应用程序时,“control”似乎具有我预期的焦点。按键似乎会导致冒泡键事件到达 ScrollViewer (使用WPF Snoop进行检查)。我无法弄清楚为什么它不响应输入。
3个回答

8

问题

当一个子控件获得焦点时,ScrollViewer会忽略所有KeyDown事件,因为KeyDown事件的OriginalSource被设置为了获得焦点的控件。

解决方案

捕获KeyDown事件并在ScrollViewer上直接引发其副本,这样它将具有正确的OriginalSource,示例如下:

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;
}

事件处理程序可以在XAML中添加:

<ScrollViewer KeyDown="ScrollViewer_KeyDown" />

或者在代码中:

scrollViewer.AddHandler(Keyboard.KeyDownEvent, ScrollViewer_KeyDown);

如果ScrollViewer位于某个模板内并且您有代码来查找它,则后者更适用。

谢谢,听起来很有用,我会试一下。虽然我很想知道为什么它不能像我期望的那样正常工作的设计原因,如果有的话。 - Ashley Davis
我不知道,但我知道他们进行了大量的可用性测试。我的猜测是,当ScrollViewer没有焦点时滚动可能导致可用性受到影响。换句话说,我怀疑用户在按钮获得焦点时箭头引起滚动的行为是意料之外和/或令人惊讶的。 - Ray Burns

0
为了让ScrollViewer对键盘按键做出反应,它需要设置IsFocused="True",目前它的子元素拥有焦点。
为了证明这一点,在Loaded事件中尝试手动将焦点设置到ScrollViewer(可能需要将IsFocusable="True"设置为ScrollViewer才能正常工作)-现在按键应该可以正常使用。如果你希望以其他方式工作,你需要在ScrollViewer上设置适当的事件处理程序(KeyDown/KeyPress等)。

谢谢,但我觉得你误解了问题。我想让子元素获得焦点。在我的应用程序中,子元素处理输入,然后事件会冒泡到 ScrollViewer 上。问题是为什么 ScrollViewer 不处理这些事件? - Ashley Davis
如果ScrollViewer没有焦点,箭头键的“自动映射”将无法工作。您需要像我在答案中写的那样,附加处理程序到Keyevents上。在您的代码中,您没有处理这些事件。 - Goblin

0

在使用 ScrollViewerItemsControl.Template 内部时,遇到了类似的问题,无法进行键盘导航。在我的情况下,添加 FocusableIsTabStop 解决了这个问题。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接