【问题标题】:Keyboard shortcuts in WPFWPF 中的键盘快捷键
【发布时间】:2010-11-24 14:01:33
【问题描述】:

我知道使用 _ 而不是 &,但我正在查看所有 Ctrl + 类型快捷键。

Ctrl+Z 撤消,Ctrl+S 保存等

是否有在 WPF 应用程序中实现这些的“标准”方式?还是您自己动手并将它们连接到任何命令/控制?

【问题讨论】:

    标签: wpf keyboard-shortcuts


    【解决方案1】:

    这取决于你想在哪里使用它们。

    TextBoxBase 派生控件已经实现了这些快捷方式。如果您想使用自定义键盘快捷键,您应该查看命令和输入手势。这是开启代码的一个小教程:WPF Tutorial - Command Bindings and Custom Commands

    【讨论】:

    • 多么垃圾的教程——并没有解释绝对最重要的事情,即如何使用一个不属于他们预定义的 20 个“常用”命令之一的命令.
    【解决方案2】:

    一种方法是将您的快捷键添加到命令本身,即InputGestures。命令实现为RoutedCommands

    这使快捷键即使没有连接到任何控件也可以工作。由于菜单项可以理解键盘手势,因此如果您将该命令连接到菜单项,它们会自动在菜单项文本中显示您的快捷键。


    步骤

    1. 创建静态属性以保存命令(最好作为您为命令创建的静态类中的属性 - 但对于一个简单的示例,仅使用 window.cs 中的静态属性):

       public static RoutedCommand MyCommand = new RoutedCommand();
      
    2. 添加应该调用方法的快捷键:

       MyCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
      
    3. 创建一个命令绑定,指向您在执行时调用的方法。将这些放在它应该在其下工作的 UI 元素(例如,窗口)和方法的命令绑定中:

       <Window.CommandBindings>
           <CommandBinding Command="{x:Static local:MyWindow.MyCommand}" 
                           Executed="MyCommandExecuted"/>
       </Window.CommandBindings>
      
       private void MyCommandExecuted(object sender, ExecutedRoutedEventArgs e) 
       { ... }
      

    【讨论】:

    • 如何将命令与菜单项关联?当然,这将是包含在此答案中的最重要信息,但它缺失了。
    • @Timwi:我以这种方式使用上面的代码将键盘快捷键添加到现有事件:RoutedCommand cmndSettings = new RoutedCommand(); cmndSettings.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control)); CommandBindings.Add(new CommandBinding(cmndSettings, mnuSettings_Click));
    • itsho 的评论对我有用,无法使上面的 xml 代码工作。
    • 不幸的是,使用这种方法,命令的Executed 代码将最终出现在(窗口或用户控件的)代码隐藏而不是视图模型中,而不是使用一个常用命令(自定义 ICommand 实现)。
    【解决方案3】:

    我发现这正是我正在寻找的与 WPF 中的键绑定相关的内容:

    <Window.InputBindings>
            <KeyBinding Modifiers="Control"
                        Key="N"
                        Command="{Binding CreateCustomerCommand}" />
    </Window.InputBindings>
    

    见博文MVVM CommandReference and KeyBinding

    【讨论】:

    • 非常好用且简单!
    • 您介意详细说明什么是“CreateCustomerCommand”以及它是如何实现的吗?
    • 这仍然是一个仅链接的答案,因为复制和粘贴的代码 sn-p 在链接的博客文章中用“结果将是异常”来描述。 :P
    • 这里的作品令人惊叹。我首先尝试在按钮内容的键之前添加“_”,例如 OP,但它不起作用。最糟糕的是,当我没有专注于界面的可写对象时,当我按下键本身时它会激活.. 就像“s”用于保存,而不是 ctrl-s。
    • 对于多个修饰符,使用+,例如Modifiers="Control+Alt"。见stackoverflow.com/q/4050066
    【解决方案4】:

    VB.NET:

    Public Shared SaveCommand_AltS As New RoutedCommand
    

    loaded 事件内部:

    SaveCommand_AltS.InputGestures.Add(New KeyGesture(Key.S, ModifierKeys.Control))
    
    Me.CommandBindings.Add(New CommandBinding(SaveCommand_AltS, AddressOf Me.save))
    

    不需要 XAML。

    【讨论】:

      【解决方案5】:

      如何将命令与MenuItem 关联:

      <MenuItem Header="My command" Command="{x:Static local:MyWindow.MyCommand}"/>
      

      【讨论】:

        【解决方案6】:

        试试这个。

        首先创建一个RoutedCommand对象:

        RoutedCommand newCmd = new RoutedCommand();
        newCmd.InputGestures.Add(new KeyGesture(Key.N, ModifierKeys.Control));
        CommandBindings.Add(new CommandBinding(newCmd, btnNew_Click));
        

        【讨论】:

        • 这是要走的路!谢谢沙希德!绕过所有 wpf bs 并以编程方式进行。
        • 看起来 WPF 已经竭尽全力使这个简单的任务尽可能复杂。我只是让自己发疯地尝试其他建议并发现我无法让它们发挥作用。需要注意的一点:如果您正在为 MenuItem 执行此操作并且希望在菜单中显示快捷键,您仍然需要单独配置:&lt;MenuItem x:Name="loadSheetCommand" Header="Load From _Spreadsheet" Click="loadSheetCommand_Click" InputGestureText="Ctrl+O" /&gt; 但这是迄今为止我找到的最简单的解决方案它有效!
        【解决方案7】:

        为其他人记录这个答案,因为有一种更简单的方法可以做到这一点,很少被引用,并且根本不需要接触 XAML。

        要链接键盘快捷键,只需在 Window 构造函数中将新的 KeyBinding 添加到 InputBindings 集合即可。作为命令,传入实现 ICommand 的任意命令类。对于执行方法,只需实现您需要的任何逻辑。在下面的示例中,我的 WindowCommand 类接受了一个委托,该委托将在调用时执行。当我构造新的 WindowCommand 以通过我的绑定传递时,我只是在我的初始化程序中指出我希望 WindowCommand 执行的方法。

        您可以使用此模式来设计自己的快速键盘快捷键。

        public YourWindow() //inside any WPF Window constructor
        {
           ...
           //add this one statement to bind a new keyboard command shortcut
           InputBindings.Add(new KeyBinding( //add a new key-binding, and pass in your command object instance which contains the Execute method which WPF will execute
              new WindowCommand(this)
              {
                 ExecuteDelegate = TogglePause //REPLACE TogglePause with your method delegate
              }, new KeyGesture(Key.P, ModifierKeys.Control)));
           ...
        }
        

        创建一个简单的 WindowCommand 类,该类接受一个执行委托来触发其上设置的任何方法。

        public class WindowCommand : ICommand
        {
            private MainWindow _window;
        
            //Set this delegate when you initialize a new object. This is the method the command will execute. You can also change this delegate type if you need to.
            public Action ExecuteDelegate { get; set; }
        
            //You don't have to add a parameter that takes a constructor. I've just added one in case I need access to the window directly.
            public WindowCommand(MainWindow window)
            {
                _window = window;
            }
        
            //always called before executing the command, mine just always returns true
            public bool CanExecute(object parameter)
            {
                return true; //mine always returns true, yours can use a new CanExecute delegate, or add custom logic to this method instead.
            }
        
            public event EventHandler CanExecuteChanged; //i'm not using this, but it's required by the interface
        
            //the important method that executes the actual command logic
            public void Execute(object parameter)
            {
                if (ExecuteDelegate != null)
                {
                    ExecuteDelegate();
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
        }
        

        【讨论】:

          【解决方案8】:

          我遇到了类似的问题,发现@aliwa 的答案是最有用和最优雅的解决方案;但是,我需要一个特定的组合键,Ctrl + 1。不幸的是,我收到以下错误:

          “1”不能用作“键”的值。数字不是有效的枚举值。

          通过进一步搜索,我修改了@aliwa 对以下内容的回答:

          <Window.InputBindings>
              <KeyBinding Gesture="Ctrl+1" Command="{Binding MyCommand}"/>
          </Window.InputBindings>
          

          我发现这非常适合我需要的任何组合。

          【讨论】:

          • 这对我有用&lt;UserControl.InputBindings&gt; &lt;KeyBinding Gesture="Enter" Command="{Binding someCommand}"/&gt; &lt;/UserControl.InputBindings&gt;
          【解决方案9】:

          虽然最重要的答案是正确的,但我个人喜欢使用附加属性来使解决方案能够应用于任何UIElement,尤其是当Window 不知道应该关注的元素时。根据我的经验,我经常看到多个视图模型和用户控件的组合,其中窗口通常只是根容器。

          片段

          public sealed class AttachedProperties
          {
              // Define the key gesture type converter
              [System.ComponentModel.TypeConverter(typeof(System.Windows.Input.KeyGestureConverter))]
              public static KeyGesture GetFocusShortcut(DependencyObject dependencyObject)
              {
                  return (KeyGesture)dependencyObject?.GetValue(FocusShortcutProperty);
              }
          
              public static void SetFocusShortcut(DependencyObject dependencyObject, KeyGesture value)
              {
                  dependencyObject?.SetValue(FocusShortcutProperty, value);
              }
          
              /// <summary>
              /// Enables window-wide focus shortcut for an <see cref="UIElement"/>.
              /// </summary>
              // Using a DependencyProperty as the backing store for FocusShortcut.  This enables animation, styling, binding, etc...
              public static readonly DependencyProperty FocusShortcutProperty =
                  DependencyProperty.RegisterAttached("FocusShortcut", typeof(KeyGesture), typeof(AttachedProperties), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnFocusShortcutChanged)));
          
              private static void OnFocusShortcutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
              {
                  if (!(d is UIElement element) || e.NewValue == e.OldValue)
                      return;
          
                  var window = FindParentWindow(d);
                  if (window == null)
                      return;
          
                  var gesture = GetFocusShortcut(d);
                  if (gesture == null)
                  {
                      // Remove previous added input binding.
                      for (int i = 0; i < window.InputBindings.Count; i++)
                      {
                          if (window.InputBindings[i].Gesture == e.OldValue && window.InputBindings[i].Command is FocusElementCommand)
                              window.InputBindings.RemoveAt(i--);
                      }
                  }
                  else
                  {
                      // Add new input binding with the dedicated FocusElementCommand.
                      // see: https://gist.github.com/shuebner20/349d044ed5236a7f2568cb17f3ed713d
                      var command = new FocusElementCommand(element);
                      window.InputBindings.Add(new InputBinding(command, gesture));
                  }
              }
          }
          

          使用此附加属性,您可以为任何 UIElement 定义焦点快捷方式。它会自动在包含该元素的窗口中注册输入绑定。

          使用 (XAML)

          <TextBox x:Name="SearchTextBox"
                   Text={Binding Path=SearchText}
                   local:AttachedProperties.FocusShortcutKey="Ctrl+Q"/>
          

          源代码

          包含 FocusElementCommand 实现的完整示例可作为 gist 获得:https://gist.github.com/shuebner20/c6a5191be23da549d5004ee56bcc352d

          免责声明:您可以在任何地方免费使用此代码。请记住,这是一个不适合大量使用的样本。例如,没有对已移除元素进行垃圾回收,因为 Command 将持有对该元素的强引用。

          【讨论】:

            【解决方案10】:

            特殊情况:如果焦点位于“非原生”元素上,您的快捷方式不会触发。例如,在我的情况下,关注 WpfCurrencyTextbox 不会触发 XAML 中定义的快捷方式(定义类似于 oliwa 的答案)。

            我通过使用 NHotkey 包将快捷方式设为全局来解决此问题。

            简而言之,对于 XAML,你需要做的就是替换

            <KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}" />
            

            通过

            <KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}"
                        HotkeyManager.RegisterGlobalHotkey="True" />
            

            答案也已发布到:How can I register a global hot key to say CTRL+SHIFT+(LETTER) using WPF and .NET 3.5?

            【讨论】:

              【解决方案11】:

              我尝试了各种使用 XAML 的方法,但都没有奏效。我终于根据Shahid Neermunda证明的答案找到了解决方案

              一、菜单栏:

              <Menu x:Name="MainMenuBar" Grid.Row="0" HorizontalContentAlignment="Left">
                  <MenuItem Header="_File" HorizontalContentAlignment="Left">
                      <MenuItem x:Name="NewProjectMenuItem"
                                  Header="New Project"
                                  InputGestureText="Ctrl+N"
                                  Click="NewProject_Click"/>
                      <MenuItem x:Name="OpenProjectMenuItem"
                                  Header="Open Project"
                                  InputGestureText="Ctrl+O"
                                  Click="OpenProject_Click"/>
                      <MenuItem x:Name="CloseProjectMenuItem"
                                  Header="Close Project"
                                  Click="CloseProject_Click"/>
                      <Separator/>
                      <MenuItem x:Name="SaveProjectMenuItem"
                                  Header="Save Project"
                                  InputGestureText="Ctrl+S"
                                  Click="SaveProject_Click"/>
                      <MenuItem x:Name="SaveProjectAsMenuItem"
                                  Header="Save Project As ..."
                                  InputGestureText="Shift+Ctrl+S"
                                  Click="SaveProjectAs_Click"/>
                      <Separator/>
                      <MenuItem x:Name="ExitMenuItem"
                                  Header="Exit"
                                  InputGestureText="Alt+F4"
                                  Click="Exit_Click"/>
                  </MenuItem>
              </Menu>
              

              没什么特别的。每个菜单项都有一个“InputGestureText”属性(关闭除外)

              然后我修改了由 Click="[tab]" 命令自动生成的点击事件方法。我在这里只展示了两个——一个定义了快捷键,另一个没有定义(关闭):

              private void OpenProject_Executed(object sender, ExecutedRoutedEventArgs e) => OpenProject_Click(sender, e);
              private void OpenProject_Click(object sender, RoutedEventArgs e)
              {
                  OpenProject();
              }
              
              private void CloseProject_Click(object sender, RoutedEventArgs e)
              {
                  CloseProject();
              }
              

              XXX_Executed(...) 方法由快捷方式绑定调用(我将在接下来介绍),而 XXX_Click 方法由 Click 命令调用。

              我对 New Project、Open Project、Save Project As 和 Exit 自动生成的 XXX_Click 方法做了同样的事情。

              然后我用绑定创建了一个新文件(我将它分开以便在添加其他绑定时更容易找到):

              partial class MainWindow
              {
                  private void BindShortcuts()
                  {
                      BindShortcut(Key.N, ModifierKeys.Control, NewProject_Executed);
                      BindShortcut(Key.O, ModifierKeys.Control, OpenProject_Executed);
                      BindShortcut(Key.S, ModifierKeys.Control, SaveProject_Executed);
                      BindShortcut(Key.S, ModifierKeys.Control | ModifierKeys.Shift, SaveProjectAs_Executed);
                      BindShortcut(Key.F4, ModifierKeys.Alt, Exit_Executed);
                  }
              
                  private void BindShortcut(Key key, ModifierKeys modifiers, ExecutedRoutedEventHandler executed)
                  {
                      RoutedCommand cmd = new();
                      _ = cmd.InputGestures.Add(new KeyGesture(key, modifiers));
                      _ = CommandBindings.Add(new CommandBinding(cmd, executed));
                  }
              }
              

              这样,当我添加带有更多快捷方式的新菜单项时,我只需要添加适当的 标签,定义一个 XXX_Executed 方法来调用自动生成的 XXX_Click 方法,并更新BindShortcuts() 函数。

              最后,我在 MainWindow 类的构造函数中添加了以下内容:

              public MainWindow()
              {
                  InitializeComponent();
                  BindShortcuts();
              }
              

              像魅力一样工作。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-10-01
                • 1970-01-01
                • 2013-02-21
                • 1970-01-01
                • 2018-09-16
                相关资源
                最近更新 更多