命令绑定在分层数据模板中

时间:2009-12-18 12:54:23

标签: .net binding command hierarchicaldatatemplate

我的应用中有菜单。我正在使用分层数据模板将其可视化:

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

菜单看起来应该如此,但每个菜单项的Command都不会被触发!更多 - 它没有限制,可以在调试器中看到:ICommand属性的访问器从未被执行过。 为什么会这样?

照例做得很完美:

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

3 个答案:

答案 0 :(得分:7)

您问题中第一个和第二个示例之间的区别在于,在第二个代码段中,您将MenuItem.Command绑定到父级的数据上下文,其中定义了RunOperationCommand。而在第一个带有HierarchicalDataTemplate的示例中,您绑定到“本地”DataContext,这是一个菜单项。它没有适当的属性,因此绑定失败。

您有几种选择:

  • 一个是使用命令属性扩展菜单项,就像你在答案中所做的那样;
  • 绑定到可视树中的相对源,该可视树具有带有命令的数据上下文,例如,假设命令在窗口的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>

答案 1 :(得分:1)

继续挖掘这个问题。我尝试过使用ItemsContainer Style的其他方式,在那里描述link text,因为DataTemplate在另一个MenuItem中创建了MenuItem,这不是很好,它还为点击行为添加了一些假象。

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

我忘了提到ApplicationMenu是我的自定义RMenuItem类的可观察集合。所以这种方式也有效,但命令也不起作用!但是我注意到有趣的功能 - 如果我们通过ItemsSource设置Menu的源代码,命令绑定不起作用,如果我们静态添加MenuItems(只是取消注释最后一行) - ItemContainerStyle中定义的命令绑定工作!!! - ((为什么会发生这样???? 但这不是我的目标 - 我想基于一些具有分配RoutedCommand的能力的集合来构建菜单的机制(为了具有menuitem的热键)。使用MVVM方法使情况变得复杂:我的menuitems集合驻留在ViewModel层中,而RoutedCommands是View的功能,而我在ViewModel中使用简单的ICommands。所以有一种值得思考的东西...... - ))

答案 2 :(得分:1)

似乎我找到了部分问题的解决方案。命令没有绑定,因为我们似乎需要为每个菜单项创建特定的命令实例。核心问题是,我的所有菜单项都执行相同的命令,差异仅在于命令参数的值。所以我应该这样做:

示例menuitem类:

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

    public string InputGesture { get; set; }

    public ICommand ItemCommand
    { get; set; }

    public List<RMyMenuItem> ChildrenItems { get; set; }
}
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))
        }
                }
            }
    };
    }

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