2015-07-03 15 views
5

Sto utilizzando MVVM e desidero associare i dati alla mia lista di MenuViewModels nel mio menu maim. Che consiste in un insieme di voci di menu e separatori.Come associare correttamente un ViewModel (che include i separatori) al menu di WPF?

Ecco il mio codice MenuItemViewModel:

public interface IMenuItemViewModel 
{ 
} 

[DebuggerDisplay("---")] 
public class SeparatorViewModel : IMenuItemViewModel 
{ 
} 

[DebuggerDisplay("{Header}, Children={Children.Count}")] 
public class MenuItemViewModel : IMenuItemViewModel, INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    public MenuItemViewModel(string header, ICommand command, ImageSource imageSource) 
    { 
     Header = header; 
     Command = command; 
     ImageSource = imageSource; 

     Children = new List<IMenuItemViewModel>(); 
    } 

    public string Header { get; private set; } 
    public ICommand Command { get; private set; } 

    public ImageSource ImageSource { get; private set; } 

    public IList<IMenuItemViewModel> Children { get; private set; } 
} 

e la mia finestra principale appare così:

<Window.Resources> 
    <HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}" 
     ItemsSource="{Binding Children}"> 
     <MenuItem Header="{Binding Header}" 
        Command="{Binding Command}"/> 
    </HierarchicalDataTemplate> 

    <DataTemplate DataType="{x:Type ViewModel:SeparatorViewModel}"> 
     <Separator /> 
    </DataTemplate> 
</Window.Resources> 

<DockPanel> 
    <Menu DockPanel.Dock="Top" 
      ItemsSource="{Binding MenuItems}"> 
    </Menu> 
</DockPanel> 

Dovrebbe essere roba molto semplice. Sfortunatamente, la voce di menu sembra errata o il separatore è vuoto menuItem (a seconda di quello che ho provato).

Quindi, come posso ottenere il mio Menu per trovare i miei due DataTemplates?

risposta

10

risolto il proprio problema

Dopo aver trascorso diverse ore la ricerca sul web, ho trovato un sacco di esempi che funzionano contro intenzioni naturali del WPF, ma nessuno che ha lavorato con esso.

Ecco come lavorare con il controllo Menu e non contro di essa ...

Un po 'di storia

il controllo di WPF Menu sarà normalmente auto create MenuItem oggetti per voi quando è vincolato a una raccolta POCO, utilizzando la proprietà ItemsSource.

Tuttavia, questo comportamento predefinito può essere sostituito da! Ecco come ...

La soluzione

In primo luogo, è necessario creare una classe che deriva da ItemContainerTemplateSelector. Oppure utilizzare la semplice classe che ho creato:

public class MenuItemContainerTemplateSelector : ItemContainerTemplateSelector 
{ 
    public override DataTemplate SelectTemplate(object item, ItemsControl parentItemsControl) 
    { 
     var key = new DataTemplateKey(item.GetType()); 
     return (DataTemplate) parentItemsControl.FindResource(key); 
    } 
} 

In secondo luogo, è necessario aggiungere un riferimento alla classe MenuItemContainerTemplateSelector al vostro oggetto di Windows resources, in questo modo:

<Window.Resources> 
    <Selectors:MenuItemContainerTemplateSelector x:Key="_menuItemContainerTemplateSelector" /> 

In terzo luogo, è necessario impostare due proprietà (UsesItemContainerTemplate e ItemContainerTemplateSelector) sia su Menu sia su MenuItem (definito in HierarchicalDataTemplate).

Come così:

<HierarchicalDataTemplate DataType="{x:Type ViewModel:MenuItemViewModel}" 
     ItemsSource="{Binding Children}"> 
     <MenuItem Header="{Binding Header}" 
        Command="{Binding Command}" 
        UsesItemContainerTemplate ="true" 
        ItemContainerTemplateSelector= 
        "{StaticResource _menuItemContainerTemplateSelector}"/> 
    </HierarchicalDataTemplate> 

    <Menu DockPanel.Dock="Top" 
      ItemsSource="{Binding MenuItems}" 
      UsesItemContainerTemplate="True" 
      ItemContainerTemplateSelector= 
      "{StaticResource _menuItemContainerTemplateSelector}"> 
    </Menu> 

perché funziona

a scopo di ottimizzazione, il Menu usa la bandiera UsesItemContainerTemplate (che ha un valore predefinito di false) per saltare il DataTemplate ricerca e restituisce solo un normale oggetto MenuItem. Pertanto, era necessario impostare questo valore su true e quindi il nostro ItemContainerTemplateSelector funziona come previsto.

Happy Coding!

1

una soluzione senza l'TemplateSelector:

forniscono ItemContainerTemplates al posto dei DataTemplates:

<ContextMenu ItemsSource="{Binding Path=MenuItems}" UsesItemContainerTemplate="True"> 
       <ContextMenu.Resources> 
       <ResourceDictionary> 
        <ItemContainerTemplate DataType="{x:Type ViewModel:MenuItemViewModel }"> 
        <MenuItem Header="{Binding Path=Header}" Command="{Binding Path=Command}" UsesItemContainerTemplate="True"> 
         <MenuItem.Icon> 
         <Image Source="{Binding Path=ImageSource}"/> 
         </MenuItem.Icon> 
        </MenuItem> 
        </ItemContainerTemplate> 
        <ItemContainerTemplate DataType="{x:Type ViewModel:SeparatorViewModel}"> 
        <Separator > 
         <Separator.Style> 
         <Style TargetType="{x:Type Separator}" BasedOn="{StaticResource ResourceKey={x:Static MenuItem.SeparatorStyleKey}}"/> 
         </Separator.Style> 
        </Separator> 
        </ItemContainerTemplate> 
       </ResourceDictionary> 
       </ContextMenu.Resources> 
      </ContextMenu> 

Note:

  • Non ho provato bambini
  • il separatore styled wrong: I dovuto riapplicare manualmente lo stile