2009-05-26 13 views
5

Tengo un escenario en el que necesito tener elementos de menú estáticos y dinámicos. Los elementos estáticos se definirán en XAML y los dinámicos suministrados por un modelo de vista. Cada elemento dinámico se representará con VieModel, vamos a llamarlo CommandViewModel. Un CommandViewModel tiene, entre otras cosas, un nombre para mostrar, también puede contener otros CommandViewModels.Mezcla de elementos de menú XAML dinámicos y estáticos

El MainViewModel que se usa como el DataContext para el menú es el siguiente:

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"); 
    } 
    } 

... ... ...

El XAML Menú:

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

Todo funciona bien, excepto que sea lo que sea lo que intento representar la lista de menús dinámicos, en este caso un ItemsControl se muestra en la interfaz de usuario como UNO menú Ite Concinando más elementos de menú, para que la colección completa de elementos dinámicos del menú se seleccione al hacer clic en el elemento. La colección se representa correctamente, ya que cada elemento de menú dinámico se muestra como un elemento de menú, pero dentro de este elemento de menú más grande. Creo que veo por qué, como el Menú simplemente crea un elemento de menú para cada uno de los elementos contenidos, estático o dinámico, no le importa. ¿Hay alguna manera de tener cada elemento de menú dinámico creado en el mismo nivel y perteneciendo al elemento del menú principal como lo hacen los estáticos en el ejemplo?

Respuesta

5

En lugar de codificar los elementos del menú "estáticos" en el lado XAML, los codificaría en el lado de VM como objetos CommandViewModel.

Como está codificando de cualquier manera, no perderá flexibilidad, y obtendrá el beneficio adicional de tener sus elementos de menú estáticos sincronizados con su HierarchicalDataTemplate si elige renderizarlos de manera diferente en el futuro .

Tenga en cuenta que es posible que tenga que cambiar sus enlaces para que su Menú se una a una colección de elementos de menú. Puede encontrar un ejemplo de este here.

EDIT: Código de ejemplo

yo era capaz de cortar este con bastante rapidez, y la mayoría de las definiciones de clase están incompletos (por ejemplo INotifyPropertyChanged) pero debe darle una idea de lo que puede hacer. Agregué algunos comandos de anidamiento en el tercer comando para asegurarme de que Hierarchical DataTemplate funciona.

Aquí está el 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> 

Y aquí está el código subyacente:

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í me gustaría poder hacer eso, pero por desgracia no puedo, la razón es principalmente que el XAML es más o menos fuera de mis manos. Lo crean/administran los desarrolladores de aplicaciones, simplemente usan mi modelo para aumentar sus menús con elementos que necesitan ser impulsados ​​desde el código. –

+0

Eso es desafortunado. ¿Hay alguna forma de que pueda cambiar el XAML para aprovechar CompositeCollection (http://msdn.microsoft.com/en-us/library/system.windows.data.compositecollection.aspx)? No es ideal, pero puede permitirle inyectar sus elementos dinámicos. El único inconveniente que puedo ver es cómo se ordenan los elementos de menú resultantes, pero puede hacer eso tratando los artículos de menú que vienen antes como una colección, y los artículos de menú que vienen después como otro. – micahtan

+0

Micahtan: ¿quizás debería escribir la solución con la clase CompositeCollection hasta su publicación? Porque parece ser realmente una forma correcta de hacerlo. Gracias, he estado teniendo el mismo problema. – arconaut

Cuestiones relacionadas