WPF custom command in context menu are disabled until any button clicked

后端 未结 4 913
无人及你
无人及你 2020-12-31 03:48

I have a custom command and I try to execute them from the context menu, but they are always displayed as disabled unless I click any button on the UI (buttons do not have a

相关标签:
4条回答
  • 2020-12-31 04:00

    There is probably some change "behind the scenes" that would normally enable the commands, but the view is not aware of this change. One would need to see the Command-implementations to give more precise hints.

    You can either make anything that changes your command-enable-state notify the view or manually trigger a command-refresh via CommandManager.InvalidateRequerySuggested(), for example when the context-menu opens.

    WPF ICommands work that way; they requery their CanExecute function whenever something in the view changes (e.g. PropertyChanged-event is fired or a button is clicked), but they don't requery if they have no reason to.

    0 讨论(0)
  • 2020-12-31 04:07

    Completely different track, now: there is indeed something special about the ContextMenu as the carrier for commands: the menu is not regarded as part of the window and therefore does not behave like an element in its visual tree would.

    There are different solutions for your problems defined here: http://www.wpftutorial.net/RoutedCommandsInContextMenu.html

    The easiest approach seems to be adding this to your XAML (for the window):

    FocusManager.FocusedElement="{Binding RelativeSource={x:Static RelativeSource.Self}, Mode=OneTime}"
    
    0 讨论(0)
  • 2020-12-31 04:13

    This issue is due to the ContextMenu being on a separate Visual and Logical Tree to that of the Window and its Controls.

    For anyone still 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!

    0 讨论(0)
  • 2020-12-31 04:14

    I just ran into this while trying to implement a custom context menu for AvalonDock. None of the solutions suggested above worked for me.

    I got the context menu working by explicitly registering my command handlers on the ContextMenu class in addition to the main widow. The function below is a helper I used for command registration.

        void RegisterCmd(RoutedCommand command, ExecutedRoutedEventHandler handler, CanExecuteRoutedEventHandler canExecute)
        {
            var binding = new CommandBinding(command, handler, canExecute);
            this.CommandBindings.Add(binding);
            CommandManager.RegisterClassCommandBinding(typeof(ContextMenu), binding);
        }
    
    0 讨论(0)
提交回复
热议问题