2009-12-18 2 views
7

У меня есть меню в моем приложении. Я визуализируя его с помощью иерархического шаблона данных:Командная привязка в иерархическом datatemplate

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

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

Делать как обычно работает отлично:

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

ответ

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

Спасибо за РЕПЛ у. Что касается вашей мысли о «родительском» и «локальном» datacontext. Я не понимаю, почему они отличаются. Я предположил, что меню и элементы меню должны выводить родительский datacontext. Разве это не одно из свойств свойства зависимостей, которое имеет datacontext? –

+0

Если в меню и в пунктах меню был одинаковый контекст данных, то {Binding Name} всегда привязывалось бы к тому же имени свойства в этом общем datacontext. Но вы хотите, чтобы имя связывалось с сущностью элемента меню. Следовательно, {Binding RunOperationCommand} имеет тот же эффект, он ищет RunOperationCommand в элементе меню. Отвечает ли он на ваш вопрос? –

+0

О, спасибо! Я понял! –

1

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

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> 
} 
Смежные вопросы