Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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 anything to do with commands).

After clicking a button, commands start to be displayed correctly (when they are unavailable they get disabled and enabled if available).

Edit: it turns out that it is not the button click which makes command work correctly, but button or other controls in focus (e.g. if I tab into a control this also enables my commands).

Here is the code for commands:

<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>

Here is the commands CanExecuteMethod:

public static RoutedCommand Quit = new RoutedCommand();   

private void QuitCanExecute(object sender, CanExecuteRoutedEventArgs e)
     {
      e.CanExecute = true;
      e.Handled = true;
     }
like image 669
Vitalij Avatar asked Oct 13 '10 07:10

Vitalij


2 Answers

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!

like image 185
Steve Lillis Avatar answered Oct 22 '22 23:10

Steve Lillis


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}"
like image 40
Simon D. Avatar answered Oct 22 '22 23:10

Simon D.