2009-05-26 10 views
5

Ho uno scenario in cui è necessario disporre di voci di menu statiche e dinamiche. Gli articoli statici saranno definiti in XAML e quelli dinamici forniti da un modello di vista. Ogni oggetto dinamico sarà rappresentato da un VieModel, chiamiamolo CommandViewModel. Un CommandViewModel ha tra le altre cose un nome visualizzato, può anche contenere altri CommandViewModels.Miscelazione di voci di menu XAML dinamiche e statiche

Il MainViewModel che viene utilizzato come DataContext per il menu è la seguente:

public class MainMenuViewModel : INotifyPropertyChanged 
{ 

    private ObservableCollection<CommandViewModel> m_CommandVMList; 


    public MainMenuViewModel() 
    { 
    m_ CommandVMList = new ObservableCollection<CommandViewModel>(); 

    CommandViewModel cmv = new CommandViewModel(); 
    cmv.DisplayName = "Dynamic Menu 1"; 
    m_CommandVMList.Add(cmv); 

    cmv = new CommandViewModel(); 
    cmv.DisplayName = "Dynamic Menu 2"; 
    m_CommandVMList.Add(cmv); 

    cmv = new CommandViewModel(); 
    cmv.DisplayName = "Dynamic Menu 3"; 
    m_CommandVMList.Add(cmv); 

    } 

    public ObservableCollection<CommandViewModel> CommandList 
    { 
    get { return m_CommandVMList; } 
    set 
    { 
     m_CommandVMList = value; 
     OnPropertyChanged("CommandList"); 
    } 
    } 

... ... ...

Il XAML Menu:

<Grid> 
    <Grid.Resources> 
    <HierarchicalDataTemplate DataType="{x:Type Fwf:CommandViewModel}" ItemsSource="{Binding Path=CommandViewModels}"> 
     <MenuItem Header="{Binding Path=DisplayName}"/> 
    </HierarchicalDataTemplate> 
    </Grid.Resources> 

    <Menu VerticalAlignment="Top" HorizontalAlignment="Stretch"> 
    <MenuItem Header="Static Top Menu Item 1"> 
     <MenuItem Header="Static Menu Item 1"/> 
     <MenuItem Header="Static Menu Item 2"/> 
     <MenuItem Header="Static Menu Item 3"/> 
     <ItemsControl ItemsSource="{Binding Path= CommandList}"/> 
     <MenuItem Header="Static Menu Item 4"/> 
     </MenuItem> 
    </Menu> 
</Grid> 

Tutto funziona benissimo tranne che qualsiasi cosa provi a rappresentare l'elenco di menu dinamici, in questo caso un ItemsControl viene mostrato nell'interfaccia utente come UNO Menu Ite m aggiungendo più voci di menu, quindi l'intera raccolta di voci di menu dinamici viene selezionata quando si fa clic sull'elemento. La raccolta viene rappresentata correttamente in quanto ogni voce di menu dinamica viene visualizzata come voce di menu stessa ma all'interno di questa voce di menu più grande. Penso di vedere perché, dal momento che il Menu sta semplicemente creando una voce di menu per ciascuno degli elementi contenuti, statici o dinamici a cui non importa. C'è un modo per avere ogni voce di menu dinamica creata sullo stesso livello e appartenente alla voce di menu genitore come quella statica dell'esempio?

risposta

5

Invece di codificare in modo rigido le voci di menu "statiche" sul lato XAML, farei un hard-code sul lato VM come oggetti CommandViewModel.

Dal momento che l'hard-coding in entrambi i casi, non perderete flessibilità e otterrete un ulteriore vantaggio di mantenere sincronizzati gli elementi di menu statici con HierarchicalDataTemplate nel caso in cui decideste di renderli in modo diverso in futuro .

Nota che potrebbe essere necessario modificare le associazioni in modo che il menu si leghi a una raccolta di voci di menu. È possibile trovare un esempio di questo here.

EDIT: Codice di esempio

sono stato in grado di incidere questo abbastanza rapidamente, e la maggior parte delle definizioni di classe sono incomplete (ad esempio INotifyPropertyChanged), ma dovrebbe dare un'idea di cosa si può fare. Ho aggiunto alcuni comandi di annidamento sul terzo comando per assicurarmi che il Gerarchico DataTemplate funzioni.

Ecco il codice XAML

<Window 
    x:Class="WPFDynamicMenuItems.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:WPFDynamicMenuItems" 
    Title="Window1" Height="300" Width="600"> 
    <Grid> 
     <Grid.Resources> 
      <HierarchicalDataTemplate DataType="{x:Type local:CommandViewModel}" ItemsSource="{Binding Path=CommandList}"> 
       <ContentPresenter 
        Content="{Binding Path=DisplayName}" 
        RecognizesAccessKey="True" /> 
      </HierarchicalDataTemplate> 
     </Grid.Resources> 
     <ToolBarTray> 
      <ToolBar> 
      <Menu> 
       <Menu.ItemsSource> 
        <CompositeCollection> 
         <MenuItem Header="A"></MenuItem> 
         <MenuItem Header="B"></MenuItem> 
         <MenuItem Header="C"></MenuItem> 

         <CollectionContainer x:Name="dynamicMenuItems"> 
         </CollectionContainer> 

         <MenuItem Header="D"></MenuItem> 

        </CompositeCollection> 
       </Menu.ItemsSource> 

      </Menu> 
       </ToolBar> 
     </ToolBarTray> 
    </Grid> 
</Window> 

Ed ecco il code-behind:

using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Windows; 

namespace WPFDynamicMenuItems 
{ 
    /// <summary> 
    /// Interaction logic for Window1.xaml 
    /// </summary> 
    public partial class Window1 : Window 
    { 
     private MainMenuViewModel _mainMenuVM = new MainMenuViewModel(); 

     public Window1() 
     { 
      InitializeComponent(); 

      this.dynamicMenuItems.Collection = this._mainMenuVM.CommandList; 
     } 
    } 


    public class MainMenuViewModel : INotifyPropertyChanged 
    { 
     private ObservableCollection<CommandViewModel> m_CommandVMList; 

     public MainMenuViewModel() 
     { 
      m_CommandVMList = new ObservableCollection<CommandViewModel>(); 
      CommandViewModel cmv = new CommandViewModel(); 
      cmv.DisplayName = "Dynamic Menu 1"; 
      m_CommandVMList.Add(cmv); 
      cmv = new CommandViewModel(); 
      cmv.DisplayName = "Dynamic Menu 2"; 
      m_CommandVMList.Add(cmv); 
      cmv = new CommandViewModel(); 
      cmv.DisplayName = "Dynamic Menu 3"; 
      m_CommandVMList.Add(cmv); 

      CommandViewModel nestedCMV = new CommandViewModel(); 
      nestedCMV.DisplayName = "Nested Menu 1"; 
      cmv.CommandList.Add(nestedCMV); 

      nestedCMV = new CommandViewModel(); 
      nestedCMV.DisplayName = "Nested Menu 2"; 
      cmv.CommandList.Add(nestedCMV); 
     } 
     public ObservableCollection<CommandViewModel> CommandList 
     { 
      get { return m_CommandVMList; } 
      set { m_CommandVMList = value; OnPropertyChanged("CommandList"); } 
     } 

     protected void OnPropertyChanged(string propertyName) 
     { 
      // Hook up event... 
     } 

     #region INotifyPropertyChanged Members 

     public event PropertyChangedEventHandler PropertyChanged; 

     #endregion 
    } 

    public class CommandViewModel : INotifyPropertyChanged 
    { 
     private ObservableCollection<CommandViewModel> m_CommandVMList; 

     public CommandViewModel() 
     { 
      this.m_CommandVMList = new ObservableCollection<CommandViewModel>(); 
     } 

     public string DisplayName { get; set; } 

     public ObservableCollection<CommandViewModel> CommandList 
     { 
      get { return m_CommandVMList; } 
      set { m_CommandVMList = value; OnPropertyChanged("CommandList"); } 
     } 

     protected void OnPropertyChanged(string propertyName) 
     { 
      // Hook up event... 
     } 

     #region INotifyPropertyChanged Members 

     public event PropertyChangedEventHandler PropertyChanged; 

     #endregion 
    } 
} 
+0

Sì Vorrei poter fare questo, ma purtroppo non posso, la ragione in primo luogo è che il codice XAML è praticamente dalle mie mani. È creato/gestito dagli sviluppatori di applicazioni, semplicemente usano il mio modello per aumentare i loro menu con elementi che devono essere guidati dal codice. –

+0

Questo è un peccato. Esiste un modo per modificare XAML per trarre vantaggio da CompositeCollection (http://msdn.microsoft.com/en-us/library/system.windows.data.compositecollection.aspx)? Non è l'ideale, ma potrebbe consentire di iniettare gli elementi dinamici. L'unico inconveniente che riesco a vedere è come vengono ordinati i MenuItem risultanti, ma potresti riuscire a farlo trattando i MenuItem che arrivano prima come un'unica raccolta e i MenuItem che vengono in seguito come un'altra. – micahtan

+0

Micahtan - forse dovresti scrivere la soluzione con la classe CompositeCollection fino al tuo post? Perché sembra essere davvero un modo giusto per farlo. Grazie, ho avuto lo stesso problema. – arconaut

Problemi correlati