0

I have some CommandBindings (in a Window) that work with MenuItems (by "work" I mean the executed/canexecute handlers are called),

I have others (in a UserControl) that work when assigned to the Command properties of Buttons (the handlers are called) -- but not when used with MenuItems (the handlers are never called).

I can make a MenuItem interact correctly with a CommandBinding in the UserControl by copying and pasting each binding into the appropriate MenuItem.CommandBindings, like so:

<MenuItem
    Header="Select All"
    Command="{StaticResource SelectAllCommand}"
    >
    <MenuItem.CommandBindings>
        <CommandBinding
            Command="{StaticResource SelectAllCommand}"
            Executed="SelectAll_Executed"
            CanExecute="SelectAll_CanExecute"
            />
    </MenuItem.CommandBindings>
</MenuItem>

But that's silly (and see below).

I can also make them work by copying the UserControl's command bindings up to the window in the UserControl's constructor:

C#

Application.Current.MainWindow.CommandBindings.AddRange(this.CommandBindings);

Again, that's pretty crazy, but it does seem to imply that there's a contextual factor at work here.

I copied the relevant bits of the control XAML into the following test XAML to reproduce the issue, but the issue didn't reproduce. Unlike the production code I extracted it from, it works as you'd expect: It's a binding, it's bound, it calls the methods. But the exact same method of binding event handler methods to commands to menuitems fails in a different UserControl in a different (and incompably vaster and more complicated) project.

XAML:

<UserControl
    x:Class="CommandTest.TestControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    mc:Ignorable="d" 
    d:DesignHeight="300"
    d:DesignWidth="300"
    >
    <UserControl.Resources>
        <ResourceDictionary>
            <RoutedUICommand x:Key="TestCommand" />

            <ContextMenu x:Key="TestMenu">
                <MenuItem 
                    Header="_Test"
                    Command="{StaticResource TestCommand}"
                    />
            </ContextMenu>
        </ResourceDictionary>
    </UserControl.Resources>

    <UserControl.CommandBindings>
        <CommandBinding
            Command="{StaticResource TestCommand}"
            Executed="TestCommand_Executed"
            CanExecute="TestCommand_CanExecute"
            />
    </UserControl.CommandBindings>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>

        <TextBox 
            Width="200"
            ContextMenu="{StaticResource TestMenu}"
            />
    </Grid>
</UserControl>

C#:

private void TestCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
    MessageBox.Show("Test Command", "Test", MessageBoxButton.OK, 
                    MessageBoxImage.Information);
}

private void TestCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
    e.Handled = true;
}

So the question is, what hidden factors can cause a CommandBinding to fail silently? How do you even debug this? Why would I see completely different behavior between a Button and a MenuItem for the same command binding in the same control? Is it because the ContextMenu is a resource? But in my test code, it's a resource and everything works.

UPDATE:

Another solution^Wworkaround: Explicitly set the MenuItem.PlacementTarget to the ContextMenu. Huh.

Community
  • 1
  • 1

1 Answers1

1

I suspect there is a subtle difference between the visual trees of your test project and your production project that is interfering with the command routing. Routed Commands search for command handlers along the visual tree, and up to two paths may be taken.

From this MSDN Magazine article:

Usually, a command invoker looks for a command binding between its own location in the visual tree and the root of the visual tree. If it finds one, the bound command handler will determine whether the command is enabled and will be called when the command is invoked. If the command is hooked up to a control inside a toolbar or menu (or, more generally, a container that sets FocusManager.IsFocusScope = true), then some additional logic runs that also looks along the visual tree path from the root to the focus element for a command binding.

Mike Strobel
  • 25,075
  • 57
  • 69