【问题标题】:Bind Command to KeyBinding in Code Behind在后面的代码中将命令绑定到 KeyBinding
【发布时间】:2016-10-12 07:58:38
【问题描述】:

我想为我的应用程序添加一些通用键盘快捷键。目前,在每个 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?

【问题讨论】:

    标签: c# wpf xaml mvvm data-binding


    【解决方案1】:

    我找到了一个简单的解决方案,它运行良好,并且似乎模仿了 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。

    【讨论】:

      【解决方案2】:

      您需要的是依赖属性

      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 更改,该值就会更改。

      所以步骤很简单,只要属性值发生变化:

      1. 为旧的ICommand 删除旧的InputCommand
      2. 为新的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

      【讨论】:

        猜你喜欢
        • 2022-08-19
        • 1970-01-01
        • 2018-05-08
        • 1970-01-01
        • 2013-05-09
        • 1970-01-01
        • 1970-01-01
        • 2017-02-19
        • 2011-08-14
        相关资源
        最近更新 更多