WPF KeyBinding 禁用并让键盘快捷键冒泡
WPF KeyBinding disable and let bubble up keyboard shortcut
我正在开发一个使用 MVVM、KeyBinding 和 ICommand 的项目。
我在同一个 window 上有多个嵌套视图 (UserControl),其中许多使用相同的 KeyBinding "Ctrl+S" 到 运行 a SaveCommand
。
与视图关联的 ViewModel 有一个 IsSaveCommandAvailable
属性,可以判断 SaveCommand
在该 ViewModel 中是否可用。
在我的例子中,只有 "root" 视图必须能够通过按 Ctrl+S 来启动 SaveCommand
,嵌套视图必须忽略按键命中并让它冒泡到根视图,执行所有保存操作。
我用谷歌搜索找到解决方案,只发现我可以使用 ICommand.CanExecute
来 return false 并避免 KeyBinding 到 运行.
但是这个解决方案不符合我的需要,因为如果我在子视图上按 Ctrl+S,它的 SaveCommand
CanExecute returns 为 false,并且按键命中丢失。
有没有办法在 KeyBinding 可以 运行 之前增加按键命中?
我找到的解决方案是在KeyBinding的Key
属性上使用一个IValueConverter
,将布尔值转换为作为CommandParameter传递的键,并且,如果值为false
, return Key.None
:
public class BooleanToKeyConverter : IValueConverter
{
/// <summary>
/// Key to use when the value is false
/// </summary>
public Key FalseKey { get; set; } = Key.None;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool flag && flag && parameter != null && parameter != DependencyProperty.UnsetValue)
{
if (parameter is Key key)
{
return key;
}
else if (Enum.TryParse<Key>(parameter.ToString(), out var parsedKey))
{
return parsedKey;
}
}
return this.FalseKey;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
在资源文件中(例如:App.xaml):
<conv:BooleanToKeyConverter x:Key="boolToKey"/>
其中 "conv" 是您的本地命名空间。
然后,在 KeyBindings 中:
<KeyBinding Command="{Binding Path=SaveCommand}"
Key="{Binding Path=IsSaveCommandAvailable, Converter={StaticResource boolToKey}, ConverterParameter=S}"
Modifiers="Ctrl"/>
如果您希望保留 Key
属性 原样(并且可绑定),您可以派生 KeyBinding
并添加 IsEnabled
依赖项 属性 而也覆盖 Gesture
:
public override InputGesture Gesture
{
get
{
return IsEnabled ? base.Gesture as KeyGesture : new KeyGesture(Key.None);
}
set
{
base.Gesture = value;
}
}
// IsEnabled dependency property changed callback
private static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as MyKeyBinding)?.WritePostscript(); // raise Gesture changed
}
不要忘记通知 UI Gesture
在 IsEnabled
中更改了使用 WritePostscript()
更改回调以及覆盖 CreateInstanceCore
.
使用示例:
<utils:MyKeyBinding
Command="{Binding SaveCommand}"
Key="{Binding KeyBindings.SaveKey}"
Modifiers="{Binding KeyBindings.SaveModifiers}"
IsEnabled="{Binding IsSaveCommandAvailable}"/>
我正在开发一个使用 MVVM、KeyBinding 和 ICommand 的项目。
我在同一个 window 上有多个嵌套视图 (UserControl),其中许多使用相同的 KeyBinding "Ctrl+S" 到 运行 a SaveCommand
。
与视图关联的 ViewModel 有一个 IsSaveCommandAvailable
属性,可以判断 SaveCommand
在该 ViewModel 中是否可用。
在我的例子中,只有 "root" 视图必须能够通过按 Ctrl+S 来启动 SaveCommand
,嵌套视图必须忽略按键命中并让它冒泡到根视图,执行所有保存操作。
我用谷歌搜索找到解决方案,只发现我可以使用 ICommand.CanExecute
来 return false 并避免 KeyBinding 到 运行.
但是这个解决方案不符合我的需要,因为如果我在子视图上按 Ctrl+S,它的 SaveCommand
CanExecute returns 为 false,并且按键命中丢失。
有没有办法在 KeyBinding 可以 运行 之前增加按键命中?
我找到的解决方案是在KeyBinding的Key
属性上使用一个IValueConverter
,将布尔值转换为作为CommandParameter传递的键,并且,如果值为false
, return Key.None
:
public class BooleanToKeyConverter : IValueConverter
{
/// <summary>
/// Key to use when the value is false
/// </summary>
public Key FalseKey { get; set; } = Key.None;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool flag && flag && parameter != null && parameter != DependencyProperty.UnsetValue)
{
if (parameter is Key key)
{
return key;
}
else if (Enum.TryParse<Key>(parameter.ToString(), out var parsedKey))
{
return parsedKey;
}
}
return this.FalseKey;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
在资源文件中(例如:App.xaml):
<conv:BooleanToKeyConverter x:Key="boolToKey"/>
其中 "conv" 是您的本地命名空间。
然后,在 KeyBindings 中:
<KeyBinding Command="{Binding Path=SaveCommand}"
Key="{Binding Path=IsSaveCommandAvailable, Converter={StaticResource boolToKey}, ConverterParameter=S}"
Modifiers="Ctrl"/>
如果您希望保留 Key
属性 原样(并且可绑定),您可以派生 KeyBinding
并添加 IsEnabled
依赖项 属性 而也覆盖 Gesture
:
public override InputGesture Gesture
{
get
{
return IsEnabled ? base.Gesture as KeyGesture : new KeyGesture(Key.None);
}
set
{
base.Gesture = value;
}
}
// IsEnabled dependency property changed callback
private static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as MyKeyBinding)?.WritePostscript(); // raise Gesture changed
}
不要忘记通知 UI Gesture
在 IsEnabled
中更改了使用 WritePostscript()
更改回调以及覆盖 CreateInstanceCore
.
使用示例:
<utils:MyKeyBinding
Command="{Binding SaveCommand}"
Key="{Binding KeyBindings.SaveKey}"
Modifiers="{Binding KeyBindings.SaveModifiers}"
IsEnabled="{Binding IsSaveCommandAvailable}"/>