如何将ViewModel的ObservableCollection绑定到MenuItem?

Edw*_*uay 15 wpf xaml datatemplate mvvm menuitem

当我使用ObservableCollection绑定菜单项时,只能点击MenuItem的"内部"区域:

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

在我的视图中我有这个菜单:

<Menu>
    <MenuItem 
        Header="Options" ItemsSource="{Binding ManageMenuPageItemViewModels}"
              ItemTemplate="{StaticResource MainMenuTemplate}"/>
</Menu>
Run Code Online (Sandbox Code Playgroud)

然后我用这个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>
Run Code Online (Sandbox Code Playgroud)

由于ObservableCollection ManageMenuPageItemViewModels中的每个ViewModel 都有一个属性TitleIdCode,因此上面的代码一见即可.

但是,问题是DataTemplate 中的MenuItem实际上另一个MenuItem中(就像它被绑定两次一样),因此在上面的DataTemplate中,Background ="Red",每个菜单项中都有一个Red框,只有这个区域可以单击,而不是整个菜单项区域本身(例如,如果用户点击复选标记所在的区域或内部可点击区域的右侧或左侧,则没有任何反应,如果您没有单独的颜色很混乱.)

将MenuItems绑定到ViewModel的ObservableCollection的正确方法是什么,以便每个MenuItem中的整个区域都是可点击的?

更新:

所以我根据下面的建议进行了以下更改,现在有了这个:

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

我只有我的DataTemplate中TextBlock的,但我还是不能"色全菜单项",但只是将TextBlock:

<DataTemplate x:Key="MainMenuTemplate">
    <TextBlock Text="{Binding Title}"/>
</DataTemplate>
Run Code Online (Sandbox Code Playgroud)

我将Command绑定到Menu.ItemContainerStyle,但它们现在不会触发:

<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>
Run Code Online (Sandbox Code Playgroud)

Sco*_*ock 35

我发现使用带有MenuItems的MVVM非常具有挑战性.我的应用程序的其余部分使用DataTemplates将View与ViewModel配对,但由于您所描述的原因,这似乎与Menus不兼容.这是我最终解决它的方式.我的视图如下所示:

<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>
Run Code Online (Sandbox Code Playgroud)

如果你注意到,我定义了一个名为IMenuItem的接口,它是MenuItem的ViewModel.这是代码:

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; }
}
Run Code Online (Sandbox Code Playgroud)

请注意,IMenuItem定义IEnumerable Items,这是您获取子菜单的方式.此外,IsSeparator是一种在菜单中定义分隔符的方法(另一个棘手的小技巧).如果IsSeparator为true,您可以在xaml中看到它如何使用DataTrigger将样式更改为现有的分隔符样式.以下是MainViewModel定义MainMenu属性(视图绑定到)的方式:

public IEnumerable<IMenuItem> MainMenu { get; set; }
Run Code Online (Sandbox Code Playgroud)

这似乎运作良好.我假设您可以为MainMenu使用ObservableCollection.我实际上是使用MEF来编写部分菜单,但之后项目本身是静态的(即使每个菜单项的属性都不是).我还使用了一个实现IMenuItem的AbstractMenuItem类,并且是一个帮助器类来实例化各个部分中的菜单项.

更新:

关于你的颜色问题,这个线程有帮助吗?


Ken*_*art 14

不要把MenuItemDataTemplate.该DataTemplate定义内容MenuItem.相反,为MenuItemvia 指定无关属性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>
Run Code Online (Sandbox Code Playgroud)

另外,看看HierarchicalDataTemplates.