简单WPF ItemsControl的奇怪焦点行为
当涉及到焦点和键盘导航时,我看到了奇怪的行为。在下面的示例中,我有一个简单的ItemsControl,它已模板化,因此它显示绑定到ItemsSource的复选框列表简单WPF ItemsControl的奇怪焦点行为,wpf,focus,itemscontrol,itemtemplate,keyboard-navigation,Wpf,Focus,Itemscontrol,Itemtemplate,Keyboard Navigation,当涉及到焦点和键盘导航时,我看到了奇怪的行为。在下面的示例中,我有一个简单的ItemsControl,它已模板化,因此它显示绑定到ItemsSource的复选框列表 <ItemsControl FocusManager.IsFocusScope="True" ItemsSource="{Binding ElementName=TheWindow, Path=ListOStrings}"> <ItemsControl.ItemTemplate&
<ItemsControl FocusManager.IsFocusScope="True"
ItemsSource="{Binding ElementName=TheWindow, Path=ListOStrings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
出于某种奇怪的原因,FocusManager.IsFocusScope=“True”分配会导致在通过鼠标单击选中复选框时无法设置键盘焦点,并且在使用键盘上的空格键选中复选框时,焦点会跳出ItemsControl。这两种症状似乎都表明,当选中复选框时,会出现一些奇怪的导航,但我很难弄清到底
如果使用此方法将可视化树中的任何父元素设置为焦点范围,则会出现此问题。如果我删除FocusManager.IsFocusScope=“True”,那么问题就会消失。不幸的是,我在一个更大的项目中看到了这个问题,在这个项目中,我不能只删除这些焦点范围而不担心其他与焦点相关的后果
有人能解释一下我看到的奇怪行为吗?这是一个bug还是我完全遗漏了什么?本文对此进行了很好的解释: 聚焦镜是为什么设计的? 微软在WPF中使用FocusScope来 创建临时辅助焦点。 WPF中的每个工具栏和菜单都有自己的 自己的焦点范围 有了这些知识,我们可以清楚地 看看我们为什么会有这些问题: 工具栏按钮不应执行 命令本身,但不管是什么 在打开工具栏之前具有焦点 点击。要实现这一点,我们需要 命令从焦点忽略焦点 作用域并使用“主”逻辑 而是专注。这就解释了原因 路由命令在内部不起作用 焦点范围 为什么屏幕上的大文本框 测试应用程序截图 显示插入符号?我不知道是什么原因 答案是——但为什么不应该呢? 当然,文本框没有 键盘焦点(中的小文本框) WPF的重点范围包括:;但是它 仍然是本文的主要逻辑焦点 活动窗口和是接收器 所有路由命令的 这部分涵盖了你看到的行为 为什么键盘焦点会移到 当您使用tab键切换到 WPF焦点范围中的复选框和 按空格键切换它 这正是你所期望的 单击菜单项或菜单项时 工具栏:键盘焦点应该是 回到主要焦点。全部的 ButtonBase派生控件就可以了 这个
@梅莱克很好地解释了这个问题。请阅读这篇文章,以充分了解问题是什么以及如何解决它。我将添加文章中提到的
IsEnhancedFocusScope
附加行为的完整实现:
public static class FocusExtensions
{
private static bool SettingKeyboardFocus { get; set; }
public static bool GetIsEnhancedFocusScope(DependencyObject element) {
return (bool)element.GetValue(IsEnhancedFocusScopeProperty);
}
public static void SetIsEnhancedFocusScope(DependencyObject element, bool value) {
element.SetValue(IsEnhancedFocusScopeProperty, value);
}
public static readonly DependencyProperty IsEnhancedFocusScopeProperty =
DependencyProperty.RegisterAttached(
"IsEnhancedFocusScope",
typeof(bool),
typeof(FocusExtensions),
new UIPropertyMetadata(false, OnIsEnhancedFocusScopeChanged));
private static void OnIsEnhancedFocusScopeChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) {
var item = depObj as UIElement;
if (item == null)
return;
if ((bool)e.NewValue) {
FocusManager.SetIsFocusScope(item, true);
item.GotKeyboardFocus += OnGotKeyboardFocus;
}
else {
FocusManager.SetIsFocusScope(item, false);
item.GotKeyboardFocus -= OnGotKeyboardFocus;
}
}
private static void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) {
if (SettingKeyboardFocus) {
return;
}
var focusedElement = e.NewFocus as Visual;
for (var d = focusedElement; d != null; d = VisualTreeHelper.GetParent(d) as Visual) {
if (FocusManager.GetIsFocusScope(d)) {
SettingKeyboardFocus = true;
try {
d.SetValue(FocusManager.FocusedElementProperty, focusedElement);
}
finally {
SettingKeyboardFocus = false;
}
if (!(bool)d.GetValue(IsEnhancedFocusScopeProperty)) {
break;
}
}
}
}
}
在XAML中,您只需设置此附加属性,而不是标准的IsFocusScope
属性:
<ItemsControl my:FocusExtensions.IsEnhancedFocusScope="True"
ItemsSource="{Binding ElementName=TheWindow, Path=ListOStrings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
它将按照您期望的焦点范围工作。+1了解问题的解释。我刚刚添加了IsEnhancedFocusScope附加行为的实现。给出这个解释后,我仍然不明白的唯一一件事是,如果我再举一个例子,将一个网格设置为焦点范围,其中包含一组子对象,这些子对象是按钮和复选框,当我点击其中一个复选框时,我没有看到我在ItemsControl中看到的奇怪的失焦行为。为什么这似乎只影响ItemsControl中的复选框,这是否与ScrollViewer、ItemsPresenter等有关。。。?