我有两个控件,一个 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 内的按钮单击以及如何阻止某些按钮(如弹出窗口内的按钮)这样做的解释。
最佳答案
你主要有三个要求(有错请指正):
- 如果弹出窗口打开,焦点应该在 popUp 内部,即 StackPanel 上。
- 在弹出窗口关闭时,焦点应保留回文本框。
- 当点击 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/