Why is this WPF RoutedCommand bound Context MenuItem disabled?

前端 未结 5 2063
北恋
北恋 2020-12-19 06:46

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



        
相关标签:
5条回答
  • 2020-12-19 07:19

    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

    0 讨论(0)
  • 2020-12-19 07:22

    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}"/>
    
    0 讨论(0)
  • 2020-12-19 07:34

    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.

    0 讨论(0)
  • 2020-12-19 07:40

    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.

    0 讨论(0)
  • 2020-12-19 07:42

    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

    0 讨论(0)
提交回复
热议问题