views:

299

answers:

1

I'm getting weird behavior with command propagation from MenuItems of ContextMenu.

I have the following kind of layout: ContextMenu is set for each DataGridRow of DataGrid inside UserControl, which in its turn is inside DockableContent of AvalonDock. If I get rid of either docking or UserControl around my grid there are no problems. ListBox instead of DataGrid doesn't have this issue either.

<Window x:Class="DockAndMenuTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ad="clr-namespace:AvalonDock;assembly=AvalonDock"
        Title="MainWindow" Height="350" Width="525">
    <ad:DockingManager>
        <ad:DocumentPane>
            <ad:DockableContent Title="Doh!">
                <UserControl>
                    <UserControl.CommandBindings>
                        <CommandBinding Command="Zoom" 
                                        Executed="ExecuteZoom" 
                                        CanExecute="CanZoom"/>
                    </UserControl.CommandBindings>
                    <DataGrid Name="_evilGrid">
                        <DataGrid.Resources>
                            <Style TargetType="DataGridRow">
                                <Setter Property="ContextMenu">
                                    <Setter.Value>
                                        <ContextMenu>
                                            <MenuItem Command="Zoom"/>
                                        </ContextMenu>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </DataGrid.Resources>
                    </DataGrid>
                </UserControl>
            </ad:DockableContent>
        </ad:DocumentPane>
    </ad:DockingManager>
</Window>

Code-behind is trivial as well:

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();

        _evilGrid.ItemsSource =
            new[]
                {
                    Tuple.Create(1, 2, 3),
                    Tuple.Create(4, 4, 3),
                    Tuple.Create(6, 7, 1),
                };
    }

    private void ExecuteZoom(object sender, ExecutedRoutedEventArgs e)
    {
        MessageBox.Show("zoom !");
    }

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

So here's the problem: right-clicking on the selected row (if it it was selected before the right click) my command comes out disabled. The command is "Zoom" in this case, but can be any other, including a custom one.

I don't know what's at fault here. SNOOP shows that in cases when this propagation fails, instead of UserControl, CanExecute is handled by "PART_ShowContextMenuButton" (Button), which is part of docking header.

I've had other issues with UI command propagation within UserControls hosted inside AvalonDock, but this one is the easiest to reproduce.

+2  A: 

ContextMenu is a popup and as such it has the FocusScope attached property set to true:

From MSDN

A focus scope is a container element that keeps track of the FocusManager..::.FocusedElement within the its scope. By default, the Window class is a focus scope as are the Menu, ContextMenu, and ToolBar classes. An element which is a focus scope has IsFocusScope set to false.

Basicly it also tells commands to stop looking any further in the visual tree.

So you have two options

  1. Set the FocusManager.IsFocusScope="True" on your context menu object

  2. Or move your Command bindings so that your are bound to the ContextMenu in stead of the UserControl like so:

Code sample:

<ContextMenu FocusManager.IsFocusScope="False">
    <ContextMenu.CommandBindings>
        <CommandBinding Command="Zoom" 
                Executed="ExecuteZoom" 
                CanExecute="CanZoom"/>
    </ContextMenu.CommandBindings>
         <MenuItem Command="Zoom"/>
</ContextMenu>

Hope this helps! :)

Some FocusScope nightmare tales from the field on the interwebs:

Arcturus
Doing exactly that didn't solve the issue, but after few more hours of digging I found a solution for my case: I made a focus scope on the UserControl level (<UserCotrol FocusManager.IsFocusScope="True">...) and now command events are being routed properly.Thanks a lot for pointing me in the right direction!
repka
Ow sorry.. I will change the answer to True ;)
Arcturus