views:

1337

answers:

3

Hello! I have Menu in my app. I'm visualizing it using hierarchical data template:

    <MenuItem Header="Main menu" ItemsSource="{Binding ApplicationMenu}" >
        <MenuItem.ItemTemplate>                    
            <HierarchicalDataTemplate DataType="{x:Type tm:RMenuItem}" 
                                      ItemsSource="{Binding Path=ChildrenItems}">                        
                <MenuItem Header="{Binding Name}" Command="{Binding RunOperationCommand}" />
            </HierarchicalDataTemplate>
        </MenuItem.ItemTemplate>
    </MenuItem>

menu looks like as it should, but Command for each menu item is not fired! Even more - it is not bounded, which could be seen in debugger: get accessor of ICommand Property has been never executed. Why it happens so?

Doing as usual works perfect:

<Menu>
    <MenuItem Header="SomeHeader" Command="{Binding RunOperationCommand}"/>
<Menu>
A: 

Continue digging this problem. I've tried other way using ItemsContainer Style, described there link text, because DataTemplate creates MenuItem within another MenuItem, which is not pretty good and it also adds some artefacts to clicking behavior.

<Menu Height="23" DockPanel.Dock="Top" ItemsSource="{Binding ApplicationMenu}" >
                <Menu.ItemContainerStyle>
                    <Style TargetType="{x:Type MenuItem}">
                        <Setter Property="Header" Value="{Binding Name}" />
                        <Setter Property="Command" Value="{Binding RunOperationCommand}"/>   
                        <Setter Property="CommandParameter" Value="123"/>
                        <Setter Property="ItemsSource" Value="{Binding ChildrenItems}" />
                    </Style>
                </Menu.ItemContainerStyle>
               <!--<MenuItem  />-->
</Menu>

I've forgotten to mention that ApplicationMenu is an observable collection of my custom RMenuItem class. So this way also works, but commands don't work either!!! BUT I've noticed interesting feature - command binding don't works if we setting Menu's source via ItemsSource, if we add MenuItems statically (just uncommenting last line) - command binding defined in ItemContainerStyle works!!! -(( Why it happens so???? But it is not my ending aim - I'd like to make mechanism of building menu based on some collection with ability of assigning RoutedCommand (in order to have hot-key for menuitem). The situation is being complicated by using MVVM approach: my collection of menuitems resides in ViewModel layer, while RoutedCommands is a View's feature, while I'm using simple ICommands in my ViewModel. So there is a food for thought ... -))

Andrey Khataev
A: 

Seems like I've found solution for part of my problem. Command is not being binding because, it seems like we need to create particular instance of command for each menu item. The core problem is that, allmost all my menuitems execute the same command and differences are only in value of command parameter. So I should do so:

sample menuitem class:

public class RMyMenuItem { public string Name { get; set; }

public string InputGesture { get; set; }

public ICommand ItemCommand
{ get; set; }

public List<RMyMenuItem> ChildrenItems { get; set; }

}

property in ViewModel:

public ObservableCollection<RMyMenuItem> ApplicationMenu
{
    get
    {
        //RApplicationMainMenu menu = new RApplicationMainMenu(0);
        //return new ObservableCollection<RMenuItem>(menu.Items);
        return new ObservableCollection<RMyMenuItem>()
        {
        new RMyMenuItem()
            {
                Name = "item1",                    
                ItemCommand = new DelegateCommand((param) => RunOperationExecute(param)),
                ChildrenItems = new List<RMyMenuItem>()
                {
        new RMyMenuItem()
        {
            Name = "item2",
            ItemCommand = new DelegateCommand((param) => RunOperationExecute(param))
        }
                }
            }
    };
    }

And XAML:

    <Menu.ItemContainerStyle>
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Header" Value="{Binding Name}" />
            <Setter Property="MenuItem.Command" Value="{Binding ItemCommand}"/>   
            <Setter Property="MenuItem.CommandParameter" Value="123"/>
            <Setter Property="ItemsSource" Value="{Binding ChildrenItems}" />                    
        </Style>
    </Menu.ItemContainerStyle>
}
Andrey Khataev
+1  A: 

The difference between the first and the second example in your question is that in the second code snippet you are binding MenuItem.Command to the parent's data context, which has the RunOperationCommand defined. Whereas in the first example with the HierarchicalDataTemplate you are binding to the "local" DataContext, which is a menu item. It doesn't have the appropriate property, so the binding fails.

You have several options:

  • one is to extend your menu items with the command property, like you did in your answer already;
  • bind to the relative source up in the visual tree, which has the data context with the command, e.g. assuming that the command is in your window's DataContext:


    <MenuItem Header="Main menu" ItemsSource="{Binding ApplicationMenu}" >
        <MenuItem.ItemTemplate>                    
            <HierarchicalDataTemplate DataType="{x:Type tm:RMenuItem}" 
                                      ItemsSource="{Binding Path=ChildrenItems}">                        
                <MenuItem Header="{Binding Name}" 
                          Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.RunOperationCommand}" 
                />
            </HierarchicalDataTemplate>
        </MenuItem.ItemTemplate>
    </MenuItem>



<Window.Resources>
     <coreView:CommandReference x:Key="RunOperationCommand"
                                Command="{Binding RunOperationCommand}" />
</Window.Resources>

    <MenuItem Header="Main menu" ItemsSource="{Binding ApplicationMenu}" >
        <MenuItem.ItemTemplate>                    
            <HierarchicalDataTemplate DataType="{x:Type tm:RMenuItem}" 
                                      ItemsSource="{Binding Path=ChildrenItems}">                        
                <MenuItem Header="{Binding Name}" 
                          Command="{StaticResource RunOperationCommand}" 
                />
            </HierarchicalDataTemplate>
        </MenuItem.ItemTemplate>
    </MenuItem>
Yacoder
Thanks for reply. Concerning your thought about "parent's" and "local" datacontext. I don't understand why they differ. I supposed that menu and menuitems should derive parent datacontext. Isn't it the one of the dependency property's features, which the datacontext is?
Andrey Khataev
If the menu and the menu items would have had the same data context, then the {Binding Name} would always have bound to the same property Name in that common datacontext. But you want Name to bind to the actual menu item's entity. Hence {Binding RunOperationCommand} has the same effect, it's searching for the RunOperationCommand on the menu item. Does it answer your question?
Yacoder
Oh, thanks! I've got it!
Andrey Khataev