【问题标题】:Hot keys ignored for custom commands自定义命令忽略热键
【发布时间】:2015-12-28 06:10:24
【问题描述】:

我正在编写我的第一个 WPF 应用程序,并试图让我的自定义命令正常工作。

public static RoutedUICommand Header1 { get; private set; }

.
.
.

gestures = new InputGestureCollection();
gestures.Add(new KeyGesture(Key.D1, ModifierKeys.Control, "Ctrl+1"));
Header1 = new RoutedUICommand("Header 1", "Header1", typeof(EditCommands), gestures);

然后我在窗口的 XAML 中添加了一个CommandBindings 部分。

<!-- local refers to my application's namespace -->
<Window.CommandBindings>
    <CommandBinding Command="local:EditCommands.Header1" Executed="CommandBinding_Executed" CanExecute="CommandBinding_CanExecute"></CommandBinding>
</Window.CommandBindings>

最后,向关联的 Ribbon 控件添加了一个命令条目。

<RibbonButton Label="Header 1" Command="local:EditCommands.Header1" SmallImageSource="Images\small.png" ToolTipTitle="Header 1" ToolTipDescription="" ToolTipImageSource="Images\small.png"></RibbonButton>

单击功能区按钮会按预期执行处理程序。但是,按Ctrl+1 似乎根本没有效果。如何识别我的热键?

【问题讨论】:

  • @AnjumSKhan:NumLock 不应该影响Key.D1 键的行为(这种情况下有Key.NumPad1 键)。
  • @AnjumSKhan:我的热键似乎都没有被识别。
  • @JonathanWood 嗨,我检查了包括 urs 在内的各种热键组合,它在这里工作正常。请检查您的Command 是否实际上首先被执行。还请尝试为您的Command 使用普通的Button 并验证。
  • @AnjumSKhan:如果我单击 RibbonButton,则执行命令。但是我的命令都没有使用热键执行。我尝试添加一个常规按钮——它现在只是被禁用了。我可以再玩一会儿。您是如何定义命令并将其绑定到您的窗口的?
  • 另外,你的窗口的CommandBinding_CanExecute 方法是什么样的?

标签: c# wpf xaml


【解决方案1】:

肯定有其他事情发生。是否有一些元素在键输入到达具有包含您的命令的命令绑定的元素之前处理键输入?

Snooop 是解决此类问题的一个有用工具。

只要按下 Ctrl+1,就会调用 HandleHeader1 下面的代码。

public static class MyCommands
{
    public static RoutedUICommand Header1 { get; } = new RoutedUICommand("Header 1", "Header1", typeof(MyCommands), new InputGestureCollection { new KeyGesture(Key.D1, ModifierKeys.Control, "Ctrl+1") });
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void HandleHeader1(object sender, ExecutedRoutedEventArgs e)
    {
    }
}

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApplication2"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
    <Window.CommandBindings>
        <CommandBinding Command="local:MyCommands.Header1" Executed="HandleHeader1"/>
    </Window.CommandBindings>
    <StackPanel>
        <TextBox/>
    </StackPanel>
</Window>

【讨论】:

  • 好吧,如果这行得通,我不明白为什么我的行不通。我确实注意到RoutedUICommand 构造函数的第三个参数是typeof(MainWindow),而不是保存命令的类的类型(在您的代码中为MyCommands)。我不知道那应该是什么,但似乎都不适合我。我没有添加任何其他东西来处理任何命令。
  • 我在代码中看到的唯一区别是我还引用了其他一些元素的命令(在我的例子中是RibbonButton)。
  • Type 参数只是我的一个错误。但正如您已经注意到的那样,在这种情况下它不应该有任何区别。
  • 我不认为从多个元素中引用命令应该有任何区别。我现在无法访问 Ribbon,但如果我在示例中添加常规菜单,它就可以正常工作。
  • 您是否尝试过 Snoop 来查看处理键输入的位置而不是您的根 Window 元素? github.com/cplotts/snoopwpf
【解决方案2】:

您好,我完全同意 JohanJane,您的代码适合我。但我只能在一种情况下重现这种奇怪的行为。如果您有多个具有相同手势的命令,则只会触发 Window.CommandBindings 部分中定义的第一个命令。这是我的代码,您可以查看。 1.Xaml:

<ribbon:RibbonWindow x:Class="SoRibbonWpfApplivationHelpAttempt.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ribbon="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
    xmlns:soRibbonWpfApplivationHelpAttempt="clr-namespace:SoRibbonWpfApplivationHelpAttempt"
    Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
    <CommandBinding Command="soRibbonWpfApplivationHelpAttempt:EditCommands.PandaButton" Executed="CommandBinding_Executed_P" CanExecute="CommandBinding_CanExecute_P"></CommandBinding>
    <CommandBinding Command="soRibbonWpfApplivationHelpAttempt:EditCommands.Header1" Executed="CommandBinding_Executed" CanExecute="CommandBinding_CanExecute"></CommandBinding>

</Window.CommandBindings>
<Grid>
    <ribbon:Ribbon x:Name="RibbonWin"  SelectedIndex="0" IsEnabled="True">
        <ribbon:Ribbon.HelpPaneContent>
            <ribbon:RibbonButton SmallImageSource="Images/Penguins.jpg" 
                                 ToolTipTitle="Header 1" ToolTipDescription="" ToolTipImageSource="Images/Penguins.jpg"
                                 Command="soRibbonWpfApplivationHelpAttempt:EditCommands.Header1"/>
        </ribbon:Ribbon.HelpPaneContent>
        <ribbon:Ribbon.QuickAccessToolBar>
            <ribbon:RibbonQuickAccessToolBar>
                <ribbon:RibbonButton x:Name ="Save" SmallImageSource="Images\Koala.jpg" 
                                     ToolTipTitle="Header 1" ToolTipDescription="" ToolTipImageSource="Images/Koala.jpg"
                                     Command="soRibbonWpfApplivationHelpAttempt:EditCommands.PandaButton"/>
            </ribbon:RibbonQuickAccessToolBar>
        </ribbon:Ribbon.QuickAccessToolBar>
    </ribbon:Ribbon>
    <TextBox VerticalAlignment="Bottom" HorizontalAlignment="Stretch" BorderBrush="Red"/>
</Grid>

2. 后台代码:

    public partial class MainWindow : RibbonWindow
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        Debug.WriteLine("header 1");
    }

    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    private void CommandBinding_CanExecute_P(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    private void CommandBinding_Executed_P(object sender, ExecutedRoutedEventArgs e)
    {
        Debug.WriteLine("panda");
    }
}

public class EditCommands
{
    public static RoutedUICommand Header1 { get; private set; }

    public static RoutedUICommand PandaButton
    {
        get;
        private set;
    }

    static EditCommands()
    {
        var gestures = new InputGestureCollection {new KeyGesture(Key.D1, ModifierKeys.Control, "Ctrl+1")};
        Header1 = new RoutedUICommand("Header 1", "Header1", typeof(EditCommands), gestures);

        var pandaG = new InputGestureCollection { new KeyGesture(Key.D2, ModifierKeys.Control, "Ctrl+2") };
        PandaButton = new RoutedUICommand("Panda Button", "PandaButton", typeof(EditCommands), gestures);
    }
}

所以我只能建议你一件事;检查是否有其他命令(可能不是自定义的)已经在使用您尝试定义的手势。

问候,

【讨论】:

    【解决方案3】:

    找到this example。也许您缺少KeyBindingInputBindings.Add 部分。

    KeyGesture keyg = new KeyGesture(Key.V, ModifierKeys.Control);  
    KeyBinding kb = new KeyBinding(Window1.CtrlVCommand,keyg);  
    InputBindings.Add(kb); 
    

    您的代码看起来非常相似,但在您显示的内容中缺少这些。

    我在搜索 wpf ribbon button shortcut key 后发现了大约 5 个链接。

    为了使上述示例能够像你想要的那样使用 Ctrl+1,我更新了这一行,如下所示:

    KeyGesture keyg = new KeyGesture(Key.D1, ModifierKeys.Control);
    

    如果这不起作用,也许您可​​以采用上述工作示例,开始将项目中的部分添加到其中,然后查看它何时/何​​处中断。如果您可以发布一个完整的、最小的解决方案来显示此问题,也许我/我们可以调试。

    更多信息:

    在对此进行更多研究时,我遇到了reference on input bindings#1084 A KeyBinding Binds a Command to a Key

    用户界面元素有一个 CommandBindings 集合,其中包含命令绑定对象,这些对象指示该元素支持哪些命令以及该命令绑定到的代码。

    用户界面元素还有一个 InputBindings 集合,其中包含 KeyBinding 和 MouseBinding 实例,每个实例都将键盘或鼠标输入映射到同样存在于 CommandBindings 集合中的命令。

    最后是InputBinding上的MSDN

    【讨论】:

    • 如果我还必须在其他地方指定它们,我真的不明白在 KeyGesture 中指定键的意义何在。此外,您创建KeyGesture 的代码看起来与我的相同,只是您没有指定击键描述。这有什么帮助?
    • 您是否尝试过我在尝试研究您的问题的答案并添加到其中以模仿您的项目时找到的示例代码?我将代码复制到我的机器上,如上图所示更改了 1 行,它就可以工作了。我查看了我拥有的其他一些代码,它们都以这种方式工作,所以我从未质疑过。如果我写的没有帮助,也许其他人会提供一些东西。
    • 我一直在看。我不明白InputBindings 是什么。我现在明白您所说的关于修改该行的内容。我将继续研究该代码。
    【解决方案4】:

    下面的代码对我有用,如果我单击“标题 1”功能区项目或使用 Ctrl+1,“已执行”将打印到控制台中的输出。因此,我必须同意 Johan 的观点,即您的应用程序主窗口中肯定发生了其他事情,而不是您向我们展示的内容。

    命令

    public class EditCommands
    {
        public static RoutedUICommand Header1 { get; private set; }
    
        static EditCommands()
        {
            var gestures = new InputGestureCollection();
            gestures.Add(new KeyGesture(Key.D1, ModifierKeys.Control, "Ctrl+1"));
            Header1 = new RoutedUICommand("Header 1", "Header1", typeof(EditCommands), gestures);
        }
    }
    

    XAML

    <Window.CommandBindings>
        <CommandBinding Command="local:EditCommands.Header1" Executed="CommandBinding_OnExecuted" CanExecute="CommandBinding_OnCanExecute"></CommandBinding>
    </Window.CommandBindings>
    
    <Grid>
        <Ribbon>
            <RibbonButton Label="Header 1" Command="local:EditCommands.Header1" ToolTipTitle="Header 1" ToolTipDescription=""/>
        </Ribbon>
    </Grid>
    

    后面的代码

    private void CommandBinding_OnExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        Console.WriteLine("Executed");
    }
    
    private void CommandBinding_OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }
    

    【讨论】:

      【解决方案5】:

      您在此处尝试实现的内容可以通过直接 InputBindings 到 Windows 或您想要的容器控件来解决!

      <Window.InputBindings>
              <KeyBinding Command="{x:Static local:EditCommands.Header1}" Gesture="CTRL+1" />
       </Window.InputBindings>
      

      我会使用这种方法来将表示逻辑与业务逻辑分开(并且由于您已经绑定到命令,因此遵循 MVVM 模式可能是一个好主意)。 除此之外,您还可以将相同的快捷方式绑定到不同的容器控件!

      强制焦点到用户控件:

      <Window FocusManager.FocusedElement="{Binding ElementName=ribbon}">
         <RibbonButton x:Name="ribbon" Label="Header 1" Command="local:EditCommands.Header1" Focusable="True"/>
      </Window>
      

      【讨论】:

      • 显然,还需要做更多的工作,因为这不会激活命令。更麻烦的是我不明白为什么要在这么多不同的地方指定热键。回到过去,我们必须使用直观的拖放和双击来设计我们的 VB6 应用程序。到目前为止,我根本不喜欢 WPF。
      • 除了 InputBindins 之外,无需指定任何其他内容。您的问题可能是组合键被另一个容器控件捕获。尝试先关注功能区,然后使用快捷方式!您可以通过将初始焦点强制到功能区或禁止将焦点放在捕获快捷方式的容器上来解决此问题。
      • 如果你的快捷方式被文本框捕获,那么看看这篇关于如何禁用默认文本框快捷方式的文章:stackoverflow.com/questions/12941707/…
      猜你喜欢
      • 1970-01-01
      • 2013-09-26
      • 2022-08-04
      • 1970-01-01
      • 2018-12-01
      • 1970-01-01
      • 2021-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多