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 GestureIsEnabled 中更改了使用 WritePostscript() 更改回调以及覆盖 CreateInstanceCore.
使用示例:

<utils:MyKeyBinding 
    Command="{Binding SaveCommand}" 
    Key="{Binding KeyBindings.SaveKey}" 
    Modifiers="{Binding KeyBindings.SaveModifiers}" 
    IsEnabled="{Binding IsSaveCommandAvailable}"/>