tags:

views:

2309

answers:

3

Hi all,

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>

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.

+3  A: 

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

Muad'Dib
Actually if you have your <CommandBinding> declaration in the XAML, you can skip the CommandBindings.Add() in the ctor... if that make any sense.
blesh
The link to why the menu item is showing as disabled at first was the clincher for me. Thanks
TheZenker
A: 

Hi,

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.

Patrick Klug
A: 

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.

Caleb Vear