2012-10-23 13 views
6

Buduję aplikację WPF, starając się ściśle przestrzegać zasad MVVM. Mam problem z poprawnym renderowaniem menu. Próbowałem kilku podejść i utknąłem. Wygląda na to, że moje powiązanie jest poprawne, ale nie jestem pewien co do manipulacji moimi stylami.Powiązanie menu WPF przy użyciu HierarchicalDataTemplate nie renderuje poprawnie elementów menu.

Oto kod, z którym mam problem. Tak jak powiedziałem, wygląda na to, że powiązanie jest dobre, i mogę nawet zobaczyć poprawne wartości pozycji menu nagłówka za pomocą Snoop, ale wszystko, co widzę renderowane, to puste kontenery dla pozycji menu.

<DockPanel> 
     <DockPanel.Resources> 
      <HierarchicalDataTemplate x:Key="TopMenuHDT" ItemsSource="{Binding Children}"> 
       <HierarchicalDataTemplate.ItemContainerStyle> 
        <Style TargetType="{x:Type MenuItem}"> 
         <Setter Property="Command" Value="{Binding Command}" /> 
         <Setter Property="Header" Value="{Binding MenuText}" /> 
         <Setter Property="Icon"> 
          <Setter.Value> 
           <Image Source="{Binding MenuIcon}" Height="16px" Width="16px" /> 
          </Setter.Value> 
         </Setter> 
        </Style> 
       </HierarchicalDataTemplate.ItemContainerStyle> 
      </HierarchicalDataTemplate> 

     </DockPanel.Resources> 
     <Menu DockPanel.Dock="Top" Height="auto" 
       ItemsSource="{Binding TopMenuItems}" 
       ItemTemplate="{StaticResource TopMenuHDT}"/> 

W moim głównym ViewModel:

private ObservableCollection<MenuViewModel> _topMenuItems; 
    public ObservableCollection<MenuViewModel> TopMenuItems 
    { 
     get { return _topMenuItems; } 
     set 
     { 
      if (_topMenuItems == value) 
       return; 

      _topMenuItems = value; base.RaisePropertyChanged("TopMenuItems"); 
     } 
    } 
... 
    public void LoadMainMenu() 
    { 
     IList<ViewModels.MenuViewModel> fileMenuItems = PopulateFileMenuEntries(); 
     IList<ViewModels.MenuViewModel> editMenuItems = PopulateEditMenuEntries(); 

     _topMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "_File", Children = new ObservableCollection<ViewModels.MenuViewModel>(fileMenuItems) }); 
     _topMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "_Edit", Children = new ObservableCollection<ViewModels.MenuViewModel>(editMenuItems) }); 

    private IList<ViewModels.MenuViewModel> PopulateFileMenuEntries() 
    { 
     List<ViewModels.MenuViewModel> fileMenuItems = new List<ViewModels.MenuViewModel>(); 

     fileMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "_Open", MenuIcon = new BitmapImage(new Uri("pack://application:,,,/Resources/OpenDocument16.png")) , Command = _mainWindowViewModel.OpenCommand }); 
     fileMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "Open _Recent" }); 

     return fileMenuItems; 
    } 

MenuViewModel:

public class MenuViewModel : ObservableObject 
{ 
    internal MenuViewModel() 
    { 
     IsEnabled = true; 
    } 

    private string _menuText; 
    public string MenuText 
    { 
     get { return _menuText; } 
     set 
     { 
      if (_menuText == value) 
       return; 

      _menuText = value; base.RaisePropertyChanged("MenuText"); 
     } 
    } 

    private ICommand _command; 
    public ICommand Command 
    { 
     get { return _command; } 
     set 
     { 
      if (_command == value) 
       return; 

      _command = value; base.RaisePropertyChanged("Command"); 
     } 
    } 

    private BitmapImage _menuIcon; 
    public BitmapImage MenuIcon 
    { 
     get { return _menuIcon; } 
     set 
     { 
      if (_menuIcon == value) 
       return; 

      _menuIcon = value; base.RaisePropertyChanged("MenuIcon"); 
     } 
    } 


    private ObservableCollection<MenuViewModel> _children; 
    public ObservableCollection<MenuViewModel> Children 
    { 
     get { return _children; } 
     set 
     { 
      _children = value; base.RaisePropertyChanged("Children"); 
     } 
    } 
} 

Każda pomoc w uzyskaniu tego renderowane poprawnie byłoby bardzo mile widziane.

EDIT:

Oto ostateczne rozwiązanie w przypadku gdy ktoś natknie tym podobnego problemu:

<DockPanel> 
    <Menu DockPanel.Dock="Top" Height="auto" ItemsSource="{Binding TopMenuItems}" > 
     <Menu.Resources> 
      <Image x:Key="MenuIconResource" Height="16" Width="16" Source="{Binding MenuIcon}" x:Shared="False" /> 
      <Style TargetType="{x:Type MenuItem}"> 
       <Setter Property="Command" Value="{Binding Command}" /> 
       <Setter Property="Header" Value="{Binding MenuText}" /> 
       <Setter Property="InputGestureText" Value="{Binding ShortcutText}" /> 
       <Setter Property="IsEnabled" Value="{Binding IsEnabled}" /> 
       <Setter Property="Icon" Value="{StaticResource MenuIconResource}" /> 
       <Setter Property="ItemsSource" Value="{Binding Children}"/> 

       <Style.Triggers> 
        <DataTrigger Binding="{Binding }" Value="{x:Null}"> 
         <Setter Property="Template" > 
          <Setter.Value> 
           <ControlTemplate> 
            <Separator Style="{StaticResource {x:Static MenuItem.SeparatorStyleKey}}" /> 
           </ControlTemplate> 
          </Setter.Value> 
         </Setter> 
        </DataTrigger> 
       </Style.Triggers> 
      </Style> 
     </Menu.Resources> 
    </Menu> 
+1

Gdzie jest szablon w twoim HierarchicalDataTemplate? Widzę tylko ItemContainerStyle zdefiniowany – Dtex

+0

Widzę twój punkt. Zbliżałem się do tego z niewłaściwego kierunku, wydaje się, że aby zachować standardowy wygląd menu przy minimalnym wysiłku, niestandardowy styl opisany poniżej jest lepszym podejściem? – genus

+0

@genus Czy masz pomysł, jak to edytować, aby obsługiwać Separatory? –

Odpowiedz

10

Spróbuj to zamiast swojego DataTemplate

<DockPanel> 
    <Menu DockPanel.Dock="Top" Height="auto" 
      ItemsSource="{Binding TopMenuItems}"> 

     <Menu.Resources> 
      <Style TargetType="{x:Type MenuItem}"> 
       <Setter Property="Command" Value="{Binding Command}" /> 
       <Setter Property="Header" Value="{Binding MenuText}" /> 
       <Setter Property="Icon"> 
        <Setter.Value> 
         <Image Source="{Binding MenuIcon}" Height="16px" Width="16px" /> 
        </Setter.Value> 
       </Setter> 
       <Setter Property="ItemsSource" Value="{Binding Children}"/> 
      </Style> 
     </Menu.Resources> 

    </Menu> 
</DockPanel> 
+0

Tak, dziękuję! Jest to bardzo zbliżone do tego, czego potrzebuję. Działa dobrze, z tym że zestaw obrazów dla MenuIcon nie jest wyświetlany. Ustawiam BitmapImage MenuIcon na nowe BitmapImage (nowe Uri ("pack: // application: ,,,/Resources/OpenDocument16.png")) statyczny zasób w mojej aplikacji. Jakieś pomysły? – genus

+0

Co, jeśli zwrócisz ciąg znaków, taki jak "Resources/OpenDocument16.png"? – user195275

+0

Działa to w takim stopniu, że czas potrzebny na rozwiązanie ikony od momentu kliknięcia jest zbyt długi, aby opóźnienie miało miejsce przy otwieraniu menu. Wkrótce dodam moje rozwiązanie do pytania w edycji. – genus