2010-05-17 4 views
0

J'ai essayé d'avoir un ContextMenu dynamique pour montrer la propriété de nom de chacun de l'objet dans son collection of objects.
voici un exemple concret, je me connecte à un webservice pour tirer contacts et groups d'un compte particulier. J'ai donc ceux comme global variables. i display the contacts in a listbox et i want to show on right click of a contact in the listbox the list of groups that it can be added to.
pour pouvoir ajouter un contact à un groupe j'ai besoin de l'identifiant du contact (que j'ai) et l'id du groupe que je cherche ici est mon code.comment passer des données en utilisant MenuItem.ItemContainerStyle

xmlns:serviceAdmin="clr-namespace:MyWpfApp.serviceAdmin" 
...... 
<ListBox.ContextMenu> 
         <ContextMenu> 
          <MenuItem Header="Refresh" Click="RefreshContact_Click"></MenuItem> 
          <MenuItem Header="Add New Contact" Click="ContactNew_Click"></MenuItem> 
          <MenuItem Header="Add to Group" Name="groupMenus"> 
           //<!--<MenuItem.Resources> 
            // <DataTemplate DataType="{x:Type serviceAdmin:groupInfo}" x:Key="groupMenuKey" > 
            // <MenuItem> 
            //  <TextBlock Text="{Binding name}" /> 
            // </MenuItem> 
            // </DataTemplate> 

           // </MenuItem.Resources>--> 
           <MenuItem.ItemContainerStyle> 
            <Style> 
             <Setter Property="MenuItem.Header" Value="{Binding name}"/> 
             <Setter Property="MenuItem.Tag" Value="{Binding id}" /> 

            </Style> 
           </MenuItem.ItemContainerStyle> 
          </MenuItem> 
          <MenuItem Header="Delete Selected" Click="ContactDelete_Click"></MenuItem> 
         </ContextMenu> 
        </ListBox.ContextMenu> 
        ...... 

et xaml.cs

//this code is in the method that loads the groups 
loadedgroup = service.getGroups(session.key, null); 
groupListBox.ItemsSource = loadedgroup; 
groupMenus.ItemsSource = loadedgroup.ToList(); 

ce code est indiquant le nom des groupes bien, mais je besoin de l'ID du groupe cliqué.
Si vous avez remarqué j'ai commenté une partie du code xaml. avec ce que je pouvais bind (avec facilité) l'identifiant du tag.But il ne fonctionnera pas et le MenuItem.ItemContainerStyle est celui qui travaille mais je suis perdu:

Question 1: comment puis-je créer une méthode gestionnaire pour un événement de clic d'un sous-menu qui a les noms des groupes?
Question 2: comment puis-je obtenir l'identifiant de groupe sur lequel je clique?

merci pour la lecture et de bien vouloir me aider dans cette

Répondre

2

Ci-dessous un échantillon en utilisant la liaison de données. Le contexte de données d'un sous-menu est une instance de Group.

XAML:

<Window x:Class="MenuTest.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Height="300" Width="300"> 
    <Grid Background="Red"> 
     <Grid.ContextMenu> 
      <ContextMenu> 
       <MenuItem Header="Menu Item 1"></MenuItem> 
       <MenuItem Header="Menu Item 2"></MenuItem> 
       <MenuItem Header="SubMenu" ItemsSource="{Binding Path=Groups}" 
          Click="OnGroupMenuItemClick"> 
        <MenuItem.ItemTemplate> 
         <DataTemplate> 
          <TextBlock Text="{Binding Path=Name}" /> 
         </DataTemplate> 
        </MenuItem.ItemTemplate> 
       </MenuItem> 
      </ContextMenu> 
     </Grid.ContextMenu> 
    </Grid> 
</Window> 

code derrière:

using System.Collections.Generic; 
using System.Windows; 
using System.Windows.Controls; 

namespace MenuTest 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 

      Groups = new List<Group>(); 
      Groups.Add(new Group() { Name = "Group1", Id = 1 }); 
      Groups.Add(new Group() { Name = "Group2", Id = 2 }); 
      Groups.Add(new Group() { Name = "Group3", Id = 3 }); 

      DataContext = this; 
     } 

     public List<Group> Groups { get; set; } 

     private void OnGroupMenuItemClick(object sender, RoutedEventArgs e) 
     { 
      MenuItem menuItem = e.OriginalSource as MenuItem; 
      Group group = menuItem.DataContext as Group; 
     } 
    } 

    public class Group 
    { 
     public string Name { get; set;} 
     public int Id { get; set;} 
    } 
} 
+0

qui fonctionne grâce –

0

Vous pouvez le faire sans utiliser l'événement Click, ou quoi que ce soit alambiquée du tout.

Le problème est que votre commande a un DataContext (le ViewModel) et l'élément en a un autre. Ainsi, deux options: soit placer la commande sur les éléments dans ItemsSource (plus flexible, car ils peuvent avoir des commandes différentes), soit relier une source RelativeSource à la vue, récupérer le ViewModel via view.DataContext, et obtenir la commande depuis cette. Cela vous évite des tracas triviaux en passant la commande autour de tous ces éléments de données. Notez que puisque nous le faisons via un style, nous lierons la même commande à chaque élément de menu si nous obtenons la commande à partir de DataContext qui est commun à tous les éléments de menu de style similaire. Mais puisque nous générons les éléments eux-mêmes à partir d'une liste d'éléments de données similaires, c'est probablement ce que vous voulez. Sinon, placez des commandes sur les éléments de données.

Si les éléments de votre ItemsSource ont une commande, il s'agit d'une propriété publique de l'élément de données qui peut facilement être liée à MenuItem.Command dans votre ItemContainerStyle. Plus de C#, moins de XAML. Ceci a l'avantage supplémentaire de fonctionner aussi bien sur un seul sous-menu localisé dans un système de menu plus grand, où d'autres éléments de menu sont définis de la manière conventionnelle. Voici une implémentation partielle de la liste MRU. Vous pourriez très facilement faire la même chose pour d'autres sous-menus similaires - ou avec très peu de travail supplémentaire, l'arborescence entière de votre menu principal.Pour des raisons de simplicité, je suppose que le projet a un espace de noms défini comme local en XAML et que ce XAML se trouve dans une vue appelée MainWindow.

Les deux ont été testés dans une implémentation complète, mais ce qui est ci-dessous n'est pas complet: Pour une réponse SO gérable, il est réduit au minimum pour permettre au XAML de donner un sens. J'ai fini par utiliser la version RelativeSource AncestorType parce que c'est un peu plus simple et je n'ai pas besoin de donner des commandes différentes à certains éléments de la liste, mais j'ai gardé l'autre version en commentaire.

<Window.Resources> 
    <!-- ... --> 
    <DataTemplate DataType="{x:Type local:MRUListItem}" > 
     <Label Content="{Binding HeaderText}" /> 
    </DataTemplate> 
    <!-- ... --> 
</Window.Resources> 

<!-- ... --> 

<Menu> 
    <MenuItem Header="_File"> 
     <MenuItem Header="_Save File" 
        Command="{Binding SaveFileCommand}" /> 

     <!-- ... --> 
     <!-- etc. --> 
     <!-- ... --> 

     <MenuItem Header="_Recent Files" 
        ItemsSource="{Binding MRUList}"> 
      <MenuItem.ItemContainerStyle> 
       <Style TargetType="MenuItem"> 
        <Setter Property="Command" 
          Value="{Binding FileOpenCommand}" /> 
        <Setter Property="CommandParameter" 
          Value="{Binding FileName}" /> 
       </Style> 
      </MenuItem.ItemContainerStyle> 
     </MenuItem> 

     <!-- etc. --> 
    </MenuItem> 

    <!-- ... --> 
</Menu> 

version RelativeSource Alternative, obtenir la commande directement à partir du ViewModel en XAML:

<!-- 
      <MenuItem.ItemContainerStyle> 
       <Style TargetType="MenuItem"> 
        <Setter Property="Command" 
         Value="{Binding RelativeSource={RelativeSource 
         AncestorType={x:Type local:MainWindow}}, 
         Path=DataContext.MRUFileOpenCommand}" /> 
        <Setter Property="CommandParameter" 
         Value="{Binding FileName}" /> 
       </Style> 
      </MenuItem.ItemContainerStyle> 
--> 

C#

public class MRUList : ObservableCollection<MRUListItem> 
{ 
    // The owning ViewModel provides us with his FileOpenCommand 
    // initially. 
    public MRUList(ICommand fileOpenCommand) 
    { 
     FileOpenCommand = fileOpenCommand; 
     CollectionChanged += CollectionChangedHandler; 
    } 

    public ICommand FileOpenCommand { get; protected set; } 

    // Methods to renumber and prune when items are added, 
    // remove duplicates when existing item is re-added, 
    // and to assign FileOpenCommand to each new MRUListItem. 

    // etc. etc. etc. 
} 

public class MRUListItem : INotifyPropertyChanged 
{ 
    public ICommand FileOpenCommand { get; set; } 

    private int _number; 
    public int Number { 
     get { return _number; } 
     set 
     { 
      _number = value; 
      OnPropertyChanged("Number"); 
      OnPropertyChanged("HeaderText"); 
     } 
    } 
    public String HeaderText { 
     get { 
      return String.Format("_{0} {1}", Number, FileName); 
     } 
    } 

    // etc. etc. etc. 
} 
Questions connexes