【问题标题】:WPF custom command in context menu are disabled until any button clicked上下文菜单中的 WPF 自定义命令被禁用,直到单击任何按钮
【发布时间】:2011-04-24 17:12:19
【问题描述】:

我有一个自定义命令,我尝试从上下文菜单中执行它们,但它们始终显示为禁用,除非我单击 UI 上的任何按钮(按钮与命令没有任何关系)。

单击按钮后,命令开始正确显示(当它们不可用时,它们会被禁用并在可用时启用)。

编辑:事实证明,使命令正常工作的不是按钮单击,而是按钮或其他焦点控件(例如,如果我进入控件,这也会启用我的命令)。

这里是命令代码:

<Window.InputBindings>
    <KeyBinding Command="{x:Static local:MainWindow.Quit}" Key="Q" Modifiers="Ctrl"/>
    <KeyBinding Command="{x:Static local:MainWindow.Disconnect}" Key="D" Modifiers="Ctrl"/>
</Window.InputBindings>

<Window.ContextMenu>
    <ContextMenu Opacity="95">
        <MenuItem Header="Quit Application                  Ctrl + Q"   Command="{x:Static local:MainWindow.Quit}"/>
        <MenuItem Header="Disconnect from the pump   Ctrl + D" Command="{x:Static local:MainWindow.Disconnect}"/>
    </ContextMenu>
</Window.ContextMenu>

这里是 CanExecuteMethod 的命令:

public static RoutedCommand Quit = new RoutedCommand();   

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

【问题讨论】:

  • 对于其他人(比如我)在尝试确定为什么在没有键盘焦点时(可能使用Keyboard.ClearFocus())时他们的菜单项被禁用的原因,您可以通过设置您的窗口具有键盘焦点。我用Keyboard.Focus(this)

标签: wpf command


【解决方案1】:

这是一个已知的错误。如果窗口的主焦点范围内没有焦点元素,CanExecute 路由将停止在 ContextMenu,因此不会到达 Window 上的 CommandBinding,一种解决方法是将 MenuItem 的 CommandTarget 绑定到主窗口,如下代码所示:

<Window.ContextMenu>
  <ContextMenu >
    <ContextMenu.Items>
     <MenuItem Command="ApplicationCommands.Open"
               CommandTarget="{Binding Path=PlacementTarget,RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
    </ContextMenu.Items>
  </ContextMenu>
</Window.ContextMenu>

【讨论】:

    【解决方案2】:

    我在尝试为 AvalonDock 实现自定义上下文菜单时遇到了这个问题。上面建议的解决方案都不适合我。

    除了主寡妇之外,我还通过在 ContextMenu 类上显式注册我的命令处理程序来使上下文菜单正常工作。下面这个函数是我用来注册命令的助手。

        void RegisterCmd(RoutedCommand command, ExecutedRoutedEventHandler handler, CanExecuteRoutedEventHandler canExecute)
        {
            var binding = new CommandBinding(command, handler, canExecute);
            this.CommandBindings.Add(binding);
            CommandManager.RegisterClassCommandBinding(typeof(ContextMenu), binding);
        }
    

    【讨论】:

    • 你在哪里/如何实现/调用上面的函数?
    • 这是我的Window控件上的一个实例方法
    【解决方案3】:

    此问题是由于 ContextMenu 位于与 Window 及其控件不同的可视化和逻辑树上。

    对于仍在寻找此问题答案的任何人 - 在搜索互联网后,我发现最有效的答案是将以下内容包含在需要其“所有者”听到其命令的 MenuItem 的任何声明中。

    通俗地说;如果您希望右键单击的内容可以听到上下文菜单的命令。添加此代码:

    CommandTarget="{Binding Path=PlacementTarget,
                            RelativeSource={RelativeSource AncestorType=ContextMenu}
                   }"
    

    例子:

        <ContextMenu>
            <MenuItem Header="Close" Command="Application.Close"
                      CommandTarget="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
        </ContextMenu>
    

    这也适用于模板(我发现很多其他解决方案不支持)。这是对从其他地方获取的语句含义的解释(我对解释事情感到震惊):

    每个 FrameworkElement 都有一个 DataContext,它是一个任意对象。数据绑定的默认源是 DataContext。您可以使用 RelativeSource.Self 来更改绑定到 FrameworkElement 本身而不是其 DataContext 的源。因此,RelativeSource 部分只是将您从 FrameworkElement 的 DataContext 移到 FrameworkElement 本身“上一级”。进入 FrameworkElement 后,您可以指定其任何属性的路径。如果 FrameworkElement 是 Popup,它将具有 PlacementTarget 属性,该属性是 Popup 相对定位的另一个 FrameworkElement。

    简而言之,例如,如果您有一个相对于 TextBox 放置的 Popup,则该表达式会将 Popup 的 DataContext 设置为 TextBox,结果 {Binding Text} 在 Popup 正文中的某处将绑定到文本的文本框。

    我真诚地希望这些信息可以让 WPF 新手免去我这个周末所经历的头痛……尽管它确实教会了我很多东西!

    【讨论】:

    • 绝对避免了头痛,并且非常适合RoutedUICommand
    【解决方案4】:

    “幕后”可能有一些通常会启用命令的更改,但视图不知道此更改。 需要查看命令实现以提供更精确的提示。

    您可以通过CommandManager.InvalidateRequerySuggested() 手动触发命令刷新,例如当上下文菜单打开时。

    WPF ICommands 就是这样工作的;每当视图中的某些内容发生更改(例如触发 PropertyChanged 事件或单击按钮)时,他们都会重新查询其 CanExecute 函数,但如果没有理由,他们不会重新查询。

    【讨论】:

    • 问题是一个命令的 CanExecute 方法总是返回 true,但如果没有视觉元素处于焦点,该命令仍然被禁用。
    • 我认为这是一些时间问题,也许你的窗口(似乎定义了命令)在第一次初始化命令时还没有准备好。
    • 但是当你打开上下文菜单时它应该运行 CanExecute 方法是正确的吗?但是,如果我调试我的应用程序,则第一次调用 CanExecute 方法是在我已制表符或单击控件然后打开上下文菜单之后。从那时起,每次我右键单击时它都会开始调用 CanExecute 方法,但在没有调用 CanExecute 方法之前。
    • 您的命令是静态属性,但它们是否也是静态初始化的,它们是否仅使用静态依赖项?它需要您的代码来回答这些问题,请发布定义您的命令的代码。
    【解决方案5】:

    完全不同的轨道,现在: ContextMenu 作为命令的载体确实有一些特别之处: 菜单不被视为窗口的一部分,因此其行为不像其可视树中的元素。

    对于此处定义的问题,有不同的解决方案: http://www.wpftutorial.net/RoutedCommandsInContextMenu.html

    最简单的方法似乎是将其添加到您的 XAML(用于窗口):

    FocusManager.FocusedElement="{Binding RelativeSource={x:Static RelativeSource.Self}, Mode=OneTime}"
    

    【讨论】:

    • 对,我完全忘记了上下文菜单有一个单独的可视化树。后来我找到了类似的解决方案,但我不明白为什么它会起作用。我什至前段时间读过这篇文章。不过还是谢谢,这很有帮助!
    • 您是否尝试过博文中描述的所有替代方案? (点击链接)
    • 对于仍在苦苦挣扎的其他人,请参阅下面的答案
    • 这个 XAML hack 的 C# 版本是:FocusManager.SetFocusedElement(this, this);
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-12
    • 1970-01-01
    • 2011-10-10
    相关资源
    最近更新 更多