wpf - 使用 IsFocusScope 选项时如何阻止 FocusManager 将焦点移到打开的弹出窗口之外

标签 wpf

我有两个控件,一个 ToolBar 和一个 TextBox。 ToolBar 有一个 Button,它会打开一个 Popup,其中包含另一个 Button

当前行为:如果我在 TextBox 内单击,它会变成焦点,然后单击 ToolBar 中的按钮,这会打开一个弹出窗口 TextBox 仍然保持焦点并接收所有键盘输入。

现在我知道这是 ToolBar 所在的 FocusScope 内的项目的默认行为,但是当弹出窗口打开时我不需要这种行为。我该如何避免呢?

示例如下:

<Window x:Class="WpfApplication67.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
</Window.Resources>
<StackPanel HorizontalAlignment="Left" Width="400">
    <ToolBar>
        <ToggleButton Name="openButton">Open Popup</ToggleButton>
        <Popup Focusable="True"
            Placement="Right"        
            StaysOpen="False" 
            IsOpen="{Binding IsChecked, ElementName=openButton, Mode=TwoWay}"
            PlacementTarget="{Binding ElementName=openButton}">
            <StackPanel 
                Width="100" 
                Height="100" 
                Background="DarkGray" 
                Focusable="True">
                    <Button>More</Button>
                </StackPanel>
        </Popup>
    </ToolBar>

    <TextBox Text="Set focus on this..." />

</StackPanel>

编辑: 我正在努力寻找关于谁将焦点移动到嵌套 FocusScope 内的按钮单击以及如何阻止某些按钮(如弹出窗口内的按钮)这样做的解释。

最佳答案

你主要有三个要求(有错请指正):

  1. 如果弹出窗口打开,焦点应该在 popUp 内部,即 StackPanel 上。
  2. 在弹出窗口关闭时,焦点应保留回文本框。
  3. 当点击 popUp 内的按钮时,焦点不应离开 popUp。

让我们一一挑选这些要求。

If pop up is opened, focus should be inside popUp i.e. on StackPanel.

就像我在评论中提到的那样,在 PopUp 的 Opened 事件上关注 stackPanel:

private void Popup_Opened(object sender, EventArgs e)
{
   stackPanel.Focus();
}

On close of popUp, focus should retained back to textBox.

Hook Closed 事件并将焦点放回 TextBox:

private void Popup_Closed(object sender, EventArgs e)
{
   textBox.Focus();
}

When button inside popUp is clicked, focus should not leave the popUp.

现在,棘手的部分来了。正如评论中提到的,一旦您单击弹出窗口内的按钮,焦点就会移到弹出窗口之外。

您可以阻止的是将处理程序附加到 stackPanel 的 PreviewLostKeyboardFocus 事件。在处理程序检查条件是否键盘焦点在弹出窗口中,设置 e.Handled = true 以便仅在此处处理事件并且不会引发将强制键盘的气泡事件焦点在 stackPanel 之外。

也就是说,如果您在 stackPanel besies 按钮中有另一个 TextBox,则处理事件也不允许您在 popUp 中移动焦点。为了避免这种情况,您可以检查新的焦点元素是否不属于 stackPanel,然后只处理该事件。

这是实现该目标的代码(在 StackPanel 的 PreviewLostKeyboardFocus 事件上添加处理程序):

private void stackPanel_PreviewLostKeyboardFocus(object sender, 
                                             KeyboardFocusChangedEventArgs e)
{
   var newFocusedChild = e.NewFocus as FrameworkElement;
   bool isMovingWithinPanel = newFocusedChild != null
                              && newFocusedChild.Parent == stackPanel;

   // If keyboard focus is within stackPanel and new focused element is
   // outside of stackPanel boundaries then only handle the event.
   if (stackPanel.IsKeyboardFocusWithin && !isMovingWithinPanel)
      e.Handled = true;
}

关于wpf - 使用 IsFocusScope 选项时如何阻止 FocusManager 将焦点移到打开的弹出窗口之外,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22304926/

相关文章:

c# - 使用 C# 64 位进行安讯士相机视频流传输

wpf - 如何在WPF应用程序中获取登录的用户窗口的凭据

c# - 如何使用其 CanExecute 方法启用按钮

wpf - 将 WPF MediaElement 与 MKV 视频格式一起使用 - 无音频

c# - MVVM 违规

wpf - 如果我的应用程序未设置 StartupUri,则 App.xaml 文件不会被解析?

c# - 通过 MouseDown 创建一个圆并移动它

c# - 如何让 Microsoft Coded UI Test Builder 识别 ItemsControl?

wpf - 如何在主窗口中显示我的用户控件?

c# - DataTemplate 中的 VisualBrush 不会重绘