在代码后面将Command绑定到KeyBinding
本文关键字:绑定 KeyBinding Command 代码 | 更新日期: 2023-09-27 18:12:45
我想在我的应用程序中添加一些通用的键盘快捷键。目前,在每个View XAML中,我添加了以下代码:
<Window.InputBindings>
<KeyBinding Command="{Binding ZoomInCommand}" Key="Add" Modifiers="Control" />
<KeyBinding Command="{Binding ZoomOutCommand}" Key="Subtract" Modifiers="Control" />
</Window.InputBindings>
为了推广这一点,我想子类化WPF窗口类并使用新创建的子类代替。现在我想知道如何在相应的代码中绑定这些键盘命令。目前看起来是这样的:
public class MyWindow : Window
{
public MyWindow()
{
DataContextChanged += OnDataContextChanged;
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
InputBindings.Clear();
var dataContext = DataContext as IZoomableViewModel;
if (dataContext != null)
{
InputBindings.Add(new KeyBinding(dataContext.ZoomInCommand, Key.Add, ModifierKeys.Control));
InputBindings.Add(new KeyBinding(dataContext.ZoomOutCommand, Key.Subtract, ModifierKeys.Control));
}
}
}
但是这看起来不对,因为我需要直接访问DataContext并强制转换它,而不是使用Binding()对象。如何更改代码使其看起来更像mvvm ?
我发现了一个简单的解决方案,它工作得很好,似乎模仿了XAML解析器的行为。基本上,对于放大功能,我将以下代码放入MyWindow构造函数中:
var zoomInKeyBinding = new KeyBinding { Key = Key.Add, Modifiers = ModifierKeys.Control };
BindingOperations.SetBinding(
zoomInKeyBinding,
InputBinding.CommandProperty,
new Binding { Path = new PropertyPath("ZoomInCommand") }
);
InputBindings.Add(zoomInKeyBinding);
当然,绑定的ViewModel需要适当地实现ZoomInCommand。
你需要的是依赖属性
在MyWindow
中,为和命令创建一个ICommand
依赖属性,当依赖属性值发生变化时,您还需要实现一个回调方法,这是ZoomInCommand
的一个:
public ICommand ZoomInCommand
{
get { return (ICommand)GetValue(ZoomInCommandProperty); }
set { SetValue(ZoomInCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for ZoomInCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ZoomInCommandProperty =
DependencyProperty.Register("ZoomInCommand", typeof(ICommand), typeof(MyWindow), new PropertyMetadata(null, new PropertyChangedCallback(OnZoomInCommandChanged)));
...
private static void OnZoomInCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyWindow wnd = (MyWindow)d;
//Remove the old key binding if there is one.
wnd.RemoveInputBinding(e.OldValue as ICommand);
//Add the new input binding.
if (e.NewValue != null)
wnd.InputBindings.Add(new KeyBinding((ICommand)e.NewValue, Key.Add, ModifierKeys.Control));
}
private void RemoveInputBinding(ICommand command)
{
if (command == null)
return;
//Find the old binding if there is one.
InputBinding oldBinding = null;
foreach (InputBinding binding in InputBindings)
{
if (binding.Command == command)
{
oldBinding = binding;
break;
}
}
//Remove the old input binding.
if (oldBinding != null)
InputBindings.Remove(oldBinding);
}
那么上面的代码到底是做什么的呢?
在依赖属性上,有一个PropertyChangedCallback
方法是可选的,它将在属性值改变时触发,这很好,因为我们可以使用它来删除旧的InputBinding
,并在值改变时创建一个新的InputBinding
。在您的示例中,当DataContext
更改时,该值将更改。
所以步骤很简单,当属性值改变时:
- 为旧
ICommand
删除旧InputCommand
。 - 为新的
ICommand
添加新的InputCommand
。
我已经创建了一个方便的RemoveInputBinding
方法,它应该可以更容易地为您的其他依赖属性重用代码,这取决于您的实现。
为了适应这些,在您的DataContextChanged
事件处理程序中,您只需要编写一个手动绑定:
private void MyWindow_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
//Bind this.ZoomInCommand to DataContext.ZoomInCommand
Binding zoomInCommandBinding = new Binding("ZoomInCommand");
zoomInCommandBinding.Source = DataContext;
this.SetBinding(MyWindow.ZoomInCommandProperty, zoomInCommandBinding);
...
}
这将确保您不再需要担心将DataContext
转换为IZoomableViewModel
,您只需尝试绑定到ZoomInCommand
。如果DataContext
中没有这样的命令,那么它将默默地失败。但是,如果成功,则会触发PropertyChangedCallback
,并为该命令创建一个InputBinding
。