当 IsFocusScope 设置为 true 时如何阻止控件在更改后失去键盘焦点?
How to stop controls from losing keyboard focus after changing when IsFocusScope is set to true?
下面是一些重现问题的示例代码:
<StackPanel Width="100">
<ToggleButton Content="Test 1"/>
<ToggleButton Content="Test 2"/>
<ToggleButton Content="Test 3"/>
<StackPanel FocusManager.IsFocusScope="True">
<ToggleButton Content="Test 4"/>
<ToggleButton Content="Test 5"/>
<ToggleButton Content="Test 6"/>
</StackPanel>
</StackPanel>
如果您通过 ToggleButtons 切换,前 3 个您可以 check/uncheck 与 space 栏没有问题,因为键盘焦点保留在您更改的项目上。但是,如果您 click/tab 通过焦点范围内的框 4-6 并尝试通过 space 栏更改它们,焦点将在该焦点范围之外重置,随后 space杠铃在焦点范围之外执行。
如何在 IsFocusScope=True
的部分更改数据时防止键盘焦点离开?
我发现此行为是 WPF 的“设计使然”。这个 post 最好地解释了它:
创建附加 属性 以实现您自己的焦点范围处理的建议解决方案也在 link。
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;
}
}
}
}
}
下面是一些重现问题的示例代码:
<StackPanel Width="100">
<ToggleButton Content="Test 1"/>
<ToggleButton Content="Test 2"/>
<ToggleButton Content="Test 3"/>
<StackPanel FocusManager.IsFocusScope="True">
<ToggleButton Content="Test 4"/>
<ToggleButton Content="Test 5"/>
<ToggleButton Content="Test 6"/>
</StackPanel>
</StackPanel>
如果您通过 ToggleButtons 切换,前 3 个您可以 check/uncheck 与 space 栏没有问题,因为键盘焦点保留在您更改的项目上。但是,如果您 click/tab 通过焦点范围内的框 4-6 并尝试通过 space 栏更改它们,焦点将在该焦点范围之外重置,随后 space杠铃在焦点范围之外执行。
如何在 IsFocusScope=True
的部分更改数据时防止键盘焦点离开?
我发现此行为是 WPF 的“设计使然”。这个 post 最好地解释了它:
创建附加 属性 以实现您自己的焦点范围处理的建议解决方案也在 link。
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;
}
}
}
}
}