Связывание команд в иерархической табличке данных - PullRequest
7 голосов
/ 18 декабря 2009

У меня есть Меню в моем приложении. Я визуализирую это, используя иерархический шаблон данных:

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

меню выглядит так, как должно, но команда для каждого пункта меню не запускается! Более того - он не ограничен, что можно увидеть в отладчике: метод доступа get свойства ICommand никогда не выполнялся. Почему так происходит?

Как обычно, отлично работает:

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

Ответы [ 3 ]

7 голосов
/ 21 декабря 2009

Разница между первым и вторым примером в вашем вопросе заключается в том, что во втором фрагменте кода вы привязываете 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 голос
/ 21 декабря 2009

Похоже, я нашел решение для части моей проблемы. Команда не является обязательной, потому что, похоже, нам нужно создать конкретный экземпляр команды для каждого пункта меню. Основная проблема заключается в том, что почти все мои пункты меню выполняют одну и ту же команду, а различия заключаются только в значении параметра команды. Поэтому я должен сделать это:

образец класса меню:

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>
}
1 голос
/ 21 декабря 2009

Продолжайте копать эту проблему. Я попробовал другой способ, используя ItemsContainer Style, описанный там текст ссылки , потому что 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, если мы добавляем MenuItems статически (просто раскомментируем последнюю строку) - привязка команды, определенная в ItemContainerStyle, работает !!! - ((Почему так бывает ???? Но это не моя конечная цель - я хотел бы создать механизм построения меню на основе некоторой коллекции с возможностью назначения RoutedCommand (чтобы иметь горячую клавишу для элемента меню). Ситуация осложняется использованием подхода MVVM: моя коллекция пунктов меню находится в слое ViewModel, в то время как RoutedCommands является функцией View, а я использую простые ICommands в моей ViewModel. Так что есть пища для размышлений ... -))

...