IsDefault 和 FocusScope 恢复

IsDefault and FocusScope recovery

我的问题是我有两个按钮:"Backward" 和 "Forward"。

  <StackPanel>
            <Button Content="Backward"/>
            <Button Content="Forward" IsDefault="True"/>
        </StackPanel>

现在单击 "Enter" 按钮时一切正常。有一个可能的流程,用户点击后退按钮,然后想使用 "Enter" 来使用 "Forward" 按钮。问题是 "Backward" 按钮被聚焦,所以 "Backward" 按钮处理所有 "Enters"。因此,我尝试在单击后取消 "Backward" 按钮的焦点,但它仍然无法正常工作,可能是因为 StackPanel 失去了它的 Focus Scope。 Focus Scope恢复到StackPanel的方法是什么?

总之: 单击后退按钮后,我想将 "Forward" 按钮 "IsDefaulted" 属性 设置为 true。而且我不想让整个 StackPanel 成为焦点,我只想将焦点范围更改为放置转发按钮的焦点范围。

如果您想在后退按钮具有焦点并按下 Enter 时停止按下该按钮,您可以拦截 Enter 按键并将事件标记为已处理。

例如:

<StackPanel PreviewKeyDown="HandleKeyPress">
    <Button x:Name="BackwardButton" Content="Backward" Click="GoBack" />
    <Button x:Name="ForwardButton" Content="Forward" Click="GoForward" />
</StackPanel>

后面加上以下代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void GoForward(object sender, RoutedEventArgs e)
    {
        Debug.WriteLine("Forward");
    }

    private void GoBack(object sender, RoutedEventArgs e)
    {
        Debug.WriteLine("Backward");
    }

    private void HandleKeyPress(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            this.ForwardButton.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent));
            e.Handled = true;
        }

        if (e.Key == Key.Back)
        {
            this.BackwardButton.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent));
            e.Handled = true;
        }
    }
}

这意味着只要 StackPanel 的子元素获得焦点并且按下某个键,您就会获得一个更改以拦截该事件的流程。

在我的示例中,如果键是Enter,则将单击Forward按钮;如果按下后退(退格)键,则将单击后退按钮。

这意味着即使键盘焦点位于“后退”按钮上并且您按下回车;将调用 GoForward() 方法而不是 GoBack() 方法。

关键是要处理一个 Preview___ 事件,以便您的代码在按钮之前看到它;并确保您的代码将 .Handled 标记为 true。这样获得焦点的按钮将永远不会看到该事件。

您可以通过不同的方式来实现您正在寻找的目标。但是,这些都会以某种方式影响用户体验,这可能是令人满意的,也可能不是。

  • 您可以为 "Backward" 按钮单击编写处理程序,将焦点设置到 "Forward" 按钮。这样做的缺点是用户无法选择 "Backward" 按钮并按住 space 多次激活它。

    Xaml:

    <Button Content="Backward" Click="Button_Click" />
    <Button x:Name="ForwardButton" Content="Backward" />
    

    代码:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        ForwardButton.Focus();
    }
    
  • 另一种选择是将 "Backward" 设置为不可聚焦。这意味着用户根本无法使用键盘触及按钮,他们被迫使用鼠标来激活它(使应用程序更难访问,可能很糟糕)。

    <Button Content="Backward" Focusable="False" />
    
  • 或者,您可以将 "Backward" 按钮设置为不处理 return/enter 键。单击时它仍然会聚焦,并且在该点之后按 space 仍会激活它。但是,按下 enter 将被忽略,允许您的默认按钮将其拾取。这可能会让用户感到困惑,因为他们关注的按钮不是被激活的按钮。事实上,我对此进行了测试,在我单击 "Backward"(我使用的是 Aero 主题)后,两个按钮似乎同时在视觉上聚焦。

    <Button Content="Backward" KeyboardNavigation.AcceptsReturn="False" />
    
  • 另一种解决方案是使用 PreviewKeyDown 处理程序直接在您的代码中手动处理按键并执行所需的操作,如 Toby Crawford 的回答中所建议的那样。它需要更多的手动工作,但允许您完全控制发生的事情。使用此解决方案,您需要想出一些方法来向用户指示正在发生的事情。此外,还会有一些令人困惑的流程,例如用户跳转到 "Backward" 按钮,按回车键,然后激活 "Forward" 按钮。你需要考虑这样的事情。

基本上,它归结为所需的用户体验。在这种情况下,您必须决定哪些用户输入流更重要,但要以不太重要的输入流可能无法按预期工作为代价。

WPF 中的焦点管理很复杂 1 2. You can basically do whatever you want (whether it makes sense to the user or not), so it really helps to plan the overall focus flow of your application. You should consider reading up on how focus and keyboard focus work in WPF