2009-12-18 5 views
7

J'ai le menu dans mon application. Je visualise à l'aide de modèle de données hiérarchique:Commande Binding dans le datatemplate hiérarchique

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

ressemble comme il se doit, mais commande pour chaque élément de menu est pas tiré! Encore plus - il n'est pas borné, ce qui pourrait être vu dans le débogueur: get accessor de ICommand Property n'a jamais été exécuté. Pourquoi cela arrive-t-il?

Faire comme d'habitude fonctionne parfaitement:

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

Répondre

7

La différence entre la première et la seconde exemple dans votre question est que dans le deuxième extrait de code vous liez MenuItem.Command à le contexte de données du parent, qui a le RunOperationCommand défini. Alors que dans le premier exemple avec le HierarchicalDataTemplate vous liez le "DataContext" local, qui est un élément de menu. Il n'a pas la propriété appropriée, donc la liaison échoue.

Vous avez plusieurs options:

  • un est d'étendre vos éléments de menu avec la propriété de commande, comme vous l'avez fait dans votre réponse déjà;
  • se lier à la source relative dans l'arborescence visuelle, qui a le contexte de données avec la commande, par ex.en supposant que la commande est en DataContext de votre fenêtre:

<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

Merci pour repl y. Concernant votre réflexion sur le contexte de données «parent» et «local». Je ne comprends pas pourquoi ils diffèrent. J'ai supposé que menu et menuitems devraient dériver datacontext parent. N'est-ce pas l'une des fonctionnalités de la propriété de dépendance, qui est le datacontext? –

+0

Si le menu et les éléments de menu avaient le même contexte de données, le {Nom de la liaison} serait toujours lié au même nom de propriété dans ce contexte de données commun. Mais vous voulez que Name se lie à l'entité de l'élément de menu actuel. Par conséquent {Binding RunOperationCommand} a le même effet, il recherche le RunOperationCommand sur l'élément de menu. Est-ce que cela répond à votre question? –

+0

Oh, merci! J'ai compris! –

1

Continuer à creuser ce problème. J'ai essayé d'une autre façon en utilisant ItemsContainer Style, décrit ici link text, parce que DataTemplate crée MenuItem dans un autre MenuItem, ce qui n'est pas très bon et ajoute également quelques artefacts au comportement de cliquer. J'ai oublié de mentionner que ApplicationMenu est une collection observable de ma classe RMenuItem personnalisée. Donc, cette façon fonctionne aussi, mais les commandes ne fonctionnent pas non plus !!! MAIS j'ai remarqué une fonctionnalité intéressante - la commande de liaison ne fonctionne pas si nous configurons la source de Menu via ItemsSource, si nous ajoutons StaticMenu (juste une dernière ligne de commentaire) - la liaison de commande définie dans ItemContainerStyle fonctionne !!! - ((Pourquoi ça arrive donc? Mais ce n'est pas mon but final - Je voudrais faire un mécanisme de construction de menu basé sur une collection avec la possibilité d'assigner RoutedCommand (afin d'avoir un raccourci clavier pour menuitem) La situation est compliquée en utilisant l'approche MVVM: ma collection de menuitems réside dans la couche ViewModel, alors que RoutedCommands est une fonctionnalité de View, alors que j'utilise des ICommands simples dans mon ViewModel.

1

On dirait que j'ai trouvé une solution pour une partie de mon problème. La commande n'est pas liée car il semble que nous ayons besoin de créer une instance particulière de commande pour chaque élément de menu. Le problème principal est que, presque tous mes menuitems exécutent la même commande et les différences sont seulement dans la valeur du paramètre de commande. Alors je le faire:

échantillon classe 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; } 
} 

propriété 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)) 
     } 
       } 
      } 
    }; 
    } 

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