为什么此 WPF RoutedCommand 绑定上下文菜单项被禁用?

发布于 2024-07-12 05:31:58 字数 1497 浏览 7 评论 0原文

我目前仍在摸索 WPF,并且无法弄清楚为什么此上下文菜单项被禁用:

<Window x:Class="DisabledMenuItemProblem.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DisabledMenuItemProblem"
        Title="Window1" Height="300" Width="300">
    <TextBlock Text="fooooobaaaaaar">
        <TextBlock.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Foo" Command="{x:Static local:MyCommands.FooBar}" />
            </ContextMenu>
        </TextBlock.ContextMenu>
    </TextBlock>
</Window>

代码隐藏:

using System.Windows;
using System.Windows.Input;

namespace DisabledMenuItemProblem
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            CommandBindings.Add(new CommandBinding(MyCommands.FooBar, FooExecuted, CanFooExecute));
        }

        public void FooExecuted(object sender, ExecutedRoutedEventArgs e)
        { MessageBox.Show("Foo!"); }

        public void CanFooExecute(object sender, CanExecuteRoutedEventArgs e)
        { e.CanExecute = true; }
    }

    public static class MyCommands
    { 
        public static RoutedCommand FooBar = new RoutedCommand(); 
    }
}

我缺少什么?

同样让我困惑的是,如果我在窗口中扔一个按钮并将其命令设置为 FooBar,它就会起作用,并且一旦执行,上下文菜单就会启用!

干杯,伙计们, 克里斯。

I'm still fumbling my way around WPF at the moment, and can not figure out why this context menu item is disabled:

<Window x:Class="DisabledMenuItemProblem.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DisabledMenuItemProblem"
        Title="Window1" Height="300" Width="300">
    <TextBlock Text="fooooobaaaaaar">
        <TextBlock.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Foo" Command="{x:Static local:MyCommands.FooBar}" />
            </ContextMenu>
        </TextBlock.ContextMenu>
    </TextBlock>
</Window>

Code Behind:

using System.Windows;
using System.Windows.Input;

namespace DisabledMenuItemProblem
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            CommandBindings.Add(new CommandBinding(MyCommands.FooBar, FooExecuted, CanFooExecute));
        }

        public void FooExecuted(object sender, ExecutedRoutedEventArgs e)
        { MessageBox.Show("Foo!"); }

        public void CanFooExecute(object sender, CanExecuteRoutedEventArgs e)
        { e.CanExecute = true; }
    }

    public static class MyCommands
    { 
        public static RoutedCommand FooBar = new RoutedCommand(); 
    }
}

What am i missing?

What's also baffling me is that if i throw a button in the window and set its command to FooBar it works, and once its been executed, then the context menu gets enabled!

Cheers guys,
Chris.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

平生欢 2024-07-19 05:31:58

这是我使用的一般模式....

首先,将命令保存在自己的静态类中,这可以促进重用等....

public static class MyCommands
{
    public static RoutedUICommand CmdFoo = new RoutedUICommand("CmdFoo", 
                                                               "CmdFoo", 
                                                               typeof(MyCommands));
}

其次,在控件/窗口/等中注册命令。 你想使用它,通常是在构造函数中

public MyControl
{
    public MyControl()
    {
        CommandBindings.Add( 
            new CommandBinding( MyCommands.CmdFoo,   // this is the command object
                                XCutFooCommand,      // execute
                                CanXCuteFooCommand));// can execute?
    }

,在控件/窗口/等中创建处理程序......

  public void CanExecuteRerollCommand(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;  // can this command be executed?
        e.Handled = true;     // has this event been handled?
    }
    public void ExecuteRerollCommand(object sender, ExecutedRoutedEventArgs e)
    {
    // do stuff
    }
}

最后,你的 xaml 应该如下所示:

    <ContextMenu>
        <ContextMenu.CommandBindings>
            <CommandBinding Command="foo:MyCommands.CmdFoo" 
                            CanExecute="CanExecuteRerollCommand" 
                            Executed="ExecuteRerollCommand" />
        </ContextMenu.CommandBindings>
        <MenuItem Header="Reroll"  Command="foo:MyCommands.CmdFoo"/>
    </ContextMenu>

注意没有绑定。 另外,请注意 中的
这是一个参考......
http://www.wiredprairie.us/journal/2007/04/commandtarget_menuitem_context.html

被禁用的命令位于此站点

here is the general pattern that I use....

first, keep your commands in thier own static class, this promotes reuse,etc....

public static class MyCommands
{
    public static RoutedUICommand CmdFoo = new RoutedUICommand("CmdFoo", 
                                                               "CmdFoo", 
                                                               typeof(MyCommands));
}

second, register the command in the control/window/etc. you want to use it in, normally in the constructor

public MyControl
{
    public MyControl()
    {
        CommandBindings.Add( 
            new CommandBinding( MyCommands.CmdFoo,   // this is the command object
                                XCutFooCommand,      // execute
                                CanXCuteFooCommand));// can execute?
    }

third, create your handlers in the control/window/etc.....

  public void CanExecuteRerollCommand(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;  // can this command be executed?
        e.Handled = true;     // has this event been handled?
    }
    public void ExecuteRerollCommand(object sender, ExecutedRoutedEventArgs e)
    {
    // do stuff
    }
}

lastly, your xaml ought to look like this:

    <ContextMenu>
        <ContextMenu.CommandBindings>
            <CommandBinding Command="foo:MyCommands.CmdFoo" 
                            CanExecute="CanExecuteRerollCommand" 
                            Executed="ExecuteRerollCommand" />
        </ContextMenu.CommandBindings>
        <MenuItem Header="Reroll"  Command="foo:MyCommands.CmdFoo"/>
    </ContextMenu>

notice that there is no binding. Also, notice the <CommandBinding> in the <ContextMenu>.
here is a reference....
http://www.wiredprairie.us/journal/2007/04/commandtarget_menuitem_context.html

the command being disabled is addressed at this site

人心善变 2024-07-19 05:31:58

对于任何寻找此问题答案的人 - 在搜索互联网后,我发现最有效的答案是在需要其“所有者”听到其命令的 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,因此 Popup 正文中某处的 {Binding Text} 将绑定到文本文本框的。

老实说,我希望这些信息可以帮助刚接触 WPF 的人解决我这个周末所经历的头痛……尽管它确实教会了我很多东西!

史蒂夫

For anyone looking for an answer to this issue - After trawling the internet I have found the most effective answer to be to include the following in any declaration of a MenuItem that needs its commands to be heard by it's "owner".

In layman's terms; if you want the commands of your context menu to be heard by the thing you're right clicking on. Add this code:

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

Example:

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

This will also work within Templates (something I found a lot of another solutions not to support). Here is an explanation of the meaning of the statement taken from elsewhere (I'm appalling at explaining things):

Every FrameworkElement has a DataContext that is an arbitrary object. The default source for a data binding is that DataContext. You can use RelativeSource.Self to change the source for a binding to the FrameworkElement itself instead of its DataContext. So the RelativeSource part just moves you "up one level" from the DataContext of the FrameworkElement to the FrameworkElement itself. Once you are at the FrameworkElement you can specify a path to any of its properties. If the FrameworkElement is a Popup, it will have a PlacementTarget property that is the other FrameworkElement that the Popup is positioned relative to.

In short, if you have a Popup placed relative to a TextBox for example, that expression sets the DataContext of the Popup to the TextBox and as a result {Binding Text} somewhere in the body of the Popup would bind to the text of the TextBox.

I honestly hope that this information saves someone who's new to WPF the headache I've gone through this weekend... though it did teach me a lot!

Steve

葬花如无物 2024-07-19 05:31:58

据我了解,这就是发生的事情。
当显示 ContextMenu 时,它会显示在弹出窗口中,该弹出窗口基本上是一个单独的窗口。 弹出窗口与窗口中的主要内容不属于同一视觉树,因此命令不会“冒泡”到主窗口中。 这就是为什么您的 CanExecute 方法永远不会被调用的原因。 例如,如果您将 CommandBindings 附加到 ContextMenu 本身,则 CanExecute 将被正确调用。

不过,我确实记得在某处读过,在某些情况下,弹出窗口不应表现得像普通窗口,某些东西应该“冒泡”。

我想一定有某种内在的魔力正在发生。 例如,如果您只是将 TextBlock 更改为 TextBox,那么它似乎可以工作。 我打赌 Reflector 会向您展示 TextEditorBase 中的一些额外逻辑或类似的东西。

如果您确实需要使用 TextBlock,我可能会手动将 CommandBinding 添加到 ContextMenu 本身而不是窗口上。

As far as I understand this is what happens.
When the ContextMenu is shown it is shown in a Popup which is basically a separate Window. The Popup does not belong to the same visual tree as the main content in your Window and therefore the Command doesn't 'bubble' up into your main window. This is why your CanExecute method is never called. If for example you attach the CommandBindings on the ContextMenu itself the CanExecute will be called correctly.

However I do recall reading somewhere that the Popup in certain cases should not behave like an ordinary Window and certain things should 'bubble' up.

I think there must be some internal magic going on. If you simply change the TextBlock to a TextBox for example it seems to work. I bet Reflector would show you some extra logic in the TextEditorBase or something like that.

If you really need to use a TextBlock I probably would manually add the CommandBinding to the ContextMenu itself rather than on the window.

那伤。 2024-07-19 05:31:58

我发现解决这个问题的最简单方法是将上下文菜单移动到窗口资源中并从那里引用它

<ContextMenu x:Key="ControlContextMenu">
    <ContextMenu.CommandBindings>
        <CommandBinding Command="{StaticResource CloseCommand}" Executed="CloseExecuted" CanExecute="CloseCanExecute" />
    </ContextMenu.CommandBindings>          
    <MenuItem Command="{StaticResource CloseCommand}" />
</ContextMenu>

,然后在 UIElement 上设置 ContextMenu 属性

<TextBlock ContextMenu="{StaticResource ControlContextMenu}"/>

I have found the easiest way to overcome this issue is move the context menu into a window resource and reference it from there

<ContextMenu x:Key="ControlContextMenu">
    <ContextMenu.CommandBindings>
        <CommandBinding Command="{StaticResource CloseCommand}" Executed="CloseExecuted" CanExecute="CloseCanExecute" />
    </ContextMenu.CommandBindings>          
    <MenuItem Command="{StaticResource CloseCommand}" />
</ContextMenu>

and then on the UIElement just set the ContextMenu property

<TextBlock ContextMenu="{StaticResource ControlContextMenu}"/>
浊酒尽余欢 2024-07-19 05:31:58

一个更简单的答案是在 Window 的构造函数中添加对 Focus() 的调用。 我昨天遇到了这个问题,花了相当多的时间弄清楚发生了什么。 我在这里写了博客:http://cebla5.spaces.live。 com/blog/cns!1B8262ED00250003!206.entry

这篇博文将解释为什么在构造函数中调用 Focus() 会起作用。

An even simpler answer would be to add a call to Focus() in the Window's constructor. I bumped into this issue yesterday and spent quite a bit of time figuring out what was going on. I blogged about it here: http://cebla5.spaces.live.com/blog/cns!1B8262ED00250003!206.entry

The blog post will explain why calling Focus() in the constructor works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文