16

当我将菜单项与 ObservableCollection 绑定时,只有 MenuItem 的“内部”区域是可点击的:

替代文字 http://tanguay.info/web/external/mvvmMenuItems.png

在我看来我有这个菜单:

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

然后我将它与这个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>

由于 ObservableCollection ManageMenuPageItemViewModels中的每个 ViewModel都有一个属性TitleIdCode,所以上面的代码乍一看可以正常工作。

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

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

更新:

因此,我根据以下建议进行了以下更改,现在有了:

替代文字 http://tanguay.info/web/external/mvvmMenuItemsYellow.png

我的 DataTemplate 中只有一个 TextBlock,但我仍然不能“为整个 MenuItem 着色”,只能为 TextBlock:

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

我将 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>
4

4 回答 4

37

我发现将 MVVM 与 MenuItems 一起使用非常具有挑战性。我的应用程序的其余部分使用 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>

如果您注意到,我定义了一个名为 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; }
}

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

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

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

更新:

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

于 2009-07-01T12:55:49.443 回答
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>

另外,看看HierarchicalDataTemplates。

于 2009-07-01T08:26:32.577 回答
2

这是我做菜单的方法。它可能不是您所需要的,但我认为它非常接近。

  <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 是将出现在菜单栏上的菜单的集合。它们每个都包含将显示的 MenuName 和一个名为 SubMenuItems 的集合,我将其设置为 ItemsSource。

我通过样式 SumMenuItemStyle 控制子菜单项的显示方式。每个 SubMenuItem 都有自己的 MenuName 属性、ICommand 类型的 Command 属性以及可能的另一个 SubMenuItem 集合。

结果是我能够将所有菜单信息存储在数据库中,并在运行时动态切换显示的菜单。整个 menuitem 区域是可点击的并正确显示。

希望这可以帮助。

于 2009-07-01T12:33:13.170 回答
2

只需将您的 DataTemplate 设置为 TextBlock(或者可能是带有图标和 TextBlock 的堆栈面板)。

于 2009-07-02T03:16:52.090 回答