Command Binding in hierarchical datatemplate

我的未来我决定 提交于 2019-12-20 14:10:44

问题


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>

回答1:


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>

  • Make a StaticResource from your command, similar to this blog post approach

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



回答2:


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




回答3:


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


来源:https://stackoverflow.com/questions/1928050/command-binding-in-hierarchical-datatemplate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!