Pergunta

Quando eu ligar itens de menu com uma ObservableCollection, apenas a área "interior" do MenuItem é clicável:

alt texto http://tanguay.info/web/external/mvvmMenuItems.png

Na minha Ver eu tenho este menu:

<Menu>
    <MenuItem 
        Header="Options" ItemsSource="{Binding ManageMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>

Então eu vinculá-lo com este DataTemplate :

<DataTemplate x:Key="MainMenuTemplate">
    <MenuItem
        Header="{Binding Title}" 
        Command="{Binding DataContext.SwitchPageCommand,
        RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}" 
        Background="Red"
        CommandParameter="{Binding IdCode}"/>
</DataTemplate>

Uma vez que cada ViewModel no ObservableCollection ManageMenuPageItemViewModels tem uma propriedade Título e IDCODE , o código acima funciona bem à primeira vista.

NO ENTANTO , o problema é que o MenuItem no DataTemplate é realmente dentro outro MenuItem ( como se ele está sendo obrigado duas vezes ) para que no exemplo acima DataTemplate com Background = "Red" há um caixa vermelha dentro de cada item do menu e só nesta área pode ser clicado, e não a toda a área item de menu em si (por exemplo, se o usuário clica sobre a área onde a marca é ou para a direita ou esquerda da área clicável interior, então nada acontece, o que , se você não tem uma cor separada é muito confuso.)

O que é a maneira correta de MenuItems ligar a uma ObservableCollection de ViewModels para que toda a área dentro de cada MenuItem é clicável?

UPDATE:

Então eu fiz as seguintes alterações com base em conselhos abaixo e agora tenho este:

alt texto http://tanguay.info/web/external/mvvmMenuItemsYellow.png

Eu só tenho um TextBlock dentro do meu DataTemplate, mas eu ainda não posso "de cor todo o MenuItem", mas apenas o TextBlock:

<DataTemplate x:Key="MainMenuTemplate">
    <TextBlock Text="{Binding Title}"/>
</DataTemplate>

E eu coloquei a ligação do comando em Menu.ItemContainerStyle mas eles não disparam agora:

<Menu DockPanel.Dock="Top">
    <Menu.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Background" Value="Yellow"/>
            <Setter Property="Command" Value="{Binding DataContext.SwitchPageCommand,
        RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}"/>
            <Setter Property="CommandParameter" Value="{Binding IdCode}"/>
        </Style>
    </Menu.ItemContainerStyle>
    <MenuItem 
        Header="MVVM" ItemsSource="{Binding MvvmMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
    <MenuItem 
        Header="Application" ItemsSource="{Binding ApplicationMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
    <MenuItem 
        Header="Manage" ItemsSource="{Binding ManageMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>
Foi útil?

Solução

Eu encontrei usando MVVM com MenuItems ser muito desafiador. O resto do meu aplicativo usa DataTemplates para emparelhar a vista com o ViewModel, mas que apenas não parece trabalhar com Menus causa de exatamente as razões que você descreveu. Aqui está como eu finalmente resolveu. Meus view parece com isso:

<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=(local:MainViewModel.MainMenu)}">
    <Menu.ItemContainerStyle>
        <Style>
            <Setter Property="MenuItem.Header" Value="{Binding Path=(contracts:IMenuItem.Header)}"/>
            <Setter Property="MenuItem.ItemsSource" Value="{Binding Path=(contracts:IMenuItem.Items)}"/>
            <Setter Property="MenuItem.Icon" Value="{Binding Path=(contracts:IMenuItem.Icon)}"/>
            <Setter Property="MenuItem.IsCheckable" Value="{Binding Path=(contracts:IMenuItem.IsCheckable)}"/>
            <Setter Property="MenuItem.IsChecked" Value="{Binding Path=(contracts:IMenuItem.IsChecked)}"/>
            <Setter Property="MenuItem.Command" Value="{Binding}"/>
            <Setter Property="MenuItem.Visibility" Value="{Binding Path=(contracts:IMenuItem.Visible), 
                Converter={StaticResource BooleanToVisibilityConverter}}"/>
            <Setter Property="MenuItem.ToolTip" Value="{Binding Path=(contracts:IMenuItem.ToolTip)}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=(contracts:IMenuItem.IsSeparator)}" Value="true">
                    <Setter Property="MenuItem.Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type MenuItem}">
                                <Separator Style="{DynamicResource {x:Static MenuItem.SeparatorStyleKey}}"/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Menu.ItemContainerStyle>
</Menu>
</DockPanel>

Se você notar, eu defini uma interface chamada IMenuItem, que é o ViewModel para um MenuItem. Aqui está o código para isso:

public interface IMenuItem : ICommand
{
    string Header { get; }
    IEnumerable<IMenuItem> Items { get; }
    object Icon { get; }
    bool IsCheckable { get; }
    bool IsChecked { get; set; }
    bool Visible { get; }
    bool IsSeparator { get; }
    string ToolTip { get; }
}

Observe que o IMenuItem define Itens IEnumerable, que é como você começa sub-menus. Além disso, o isSeparator é uma forma de definir separadores no menu (outro truque pouco difícil). Você pode ver no XAML como ele usa um DataTrigger para mudar o estilo para o estilo de separador existente se isSeparator é verdade. Veja como MainViewModel define a propriedade MainMenu (que a visão se liga a):

public IEnumerable<IMenuItem> MainMenu { get; set; }

Isso parece funcionar bem. Eu suponho que você poderia usar um ObservableCollection para o MainMenu. Na verdade, estou usando MEF para compor o menu fora de peças, mas depois que os próprios itens são estáticos (mesmo que as propriedades de cada item de menu não são). Eu também usar uma classe AbstractMenuItem que implementos IMenuItem e é uma classe auxiliar para itens de menu instanciar em várias partes.

UPDATE:

Quanto à sua problema de cor, faz esta discussão ajuda?

Outras dicas

Não coloque o MenuItem na DataTemplate. O DataTemplate define o conteúdo do MenuItem. Em vez disso, especifique as propriedades estranhas para o MenuItem através da ItemContainerStyle:

<Menu>
    <Menu.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Header" Value="{Binding Title}"/>
            ...
        </Style>
    </Menu.ItemContainerStyle>
    <MenuItem 
        Header="Options" ItemsSource="{Binding ManageMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>

Além disso, dê uma olhada HierarchicalDataTemplates.

Aqui está como eu fiz meus menus. Pode não ser exatamente o que você precisa, mas eu acho que é bastante estreita.

  <Style x:Key="SubmenuItemStyle" TargetType="MenuItem">
    <Setter Property="Header" Value="{Binding MenuName}"></Setter>
    <Setter Property="Command" Value="{Binding Path=MenuCommand}"/>
    <Setter Property="ItemsSource" Value="{Binding SubmenuItems}"></Setter>
  </Style>

  <DataTemplate DataType="{x:Type systemVM:TopMenuViewModel}" >
    <Menu>
      <MenuItem Header="{Binding MenuName}"         
                    ItemsSource="{Binding SubmenuItems}" 
                    ItemContainerStyle="{DynamicResource SubmenuItemStyle}" />
    </Menu>
  </DataTemplate>

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

TopMenuViewModel é uma coleção de menus que aparecerão na barra de menu. cada um deles contém o menuname que será exibido e uma coleção chamada SubMenuItems que eu definido para ser o ItemsSource.

Eu controlar a forma como os SubMenuItems são exibidos por meio do SumMenuItemStyle estilo. Cada SubMenuItem tem a sua própria propriedade menuname, propriedade Comando do tipo ICommand e, possivelmente, uma outra coleção de SubMenuItems.

O resultado é que eu sou capaz de armazenar todas as minhas informações menu em um banco de dados e mudar dinamicamente o que os menus são exibidos em tempo de execução. Toda a área menuitem é clicável e exibe corretamente.

Espero que isso ajude.

Apenas faça o seu DataTemplate para ser um TextBlock (ou talvez um painel de pilha com um ícone e um TextBlock).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top