2009-07-01 86 views
15

当我绑定菜单项与一个ObservableCollection的菜单项的只有“内”区域是可点击:如何将ViewModels的ObservableCollection绑定到MenuItem?

alt text 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有一个属性标题IDCODE,上面的代码工作正常一见钟情。

无论其,问题是,菜单项在DataTemplate中实际上是内部另一个菜单项(,就好像它被绑定两次),使得在与背景以上的DataTemplate =”红色“有一个每个菜单项内的红色框只有这个区域可以被点击,而不是整个菜单项区域本身(例如,如果用户点击选中区域或右边或左边内部cl ickable区域,然后什么也没有发生,而如果你没有一个单独的颜色是非常混乱。)

什么是的MenuItems结合的ViewModels的一个ObservableCollection使每个菜单项里面的整个区域是正确的方法点击?

UPDATE:

所以我做了基于以下建议如下更改,现在有这样的:

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

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

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

我把命令绑定到Menu.ItemCo ntainerStyle但他们现在不火:

<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> 

回答

35

我发现使用MVVM与MenuItems是非常具有挑战性的。我的应用程序的其余部分使用DataTemplates将视图与ViewModel配对,但由于您描述的原因,这似乎不适用于菜单。这是我最终如何解决它的。我查看看起来是这样的:

<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的接口,这是视图模型的菜单项。以下是此代码:

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项目,这是您如何获得子菜单。此外,IsSeparator是一种在菜单中定义分隔符的方法(另一个棘手的小技巧)。如果IsSeparator为true,您可以在xaml中看到它如何使用DataTrigger将样式更改为现有的分隔符样式。以下是MainViewModel如何定义MainMenu属性(视图绑定到的):

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

这似乎很好。我假设你可以使用MainMenu的ObservableCollection。实际上,我使用MEF从部分组成菜单,但之后项目本身是静态的(即使每个菜单项的属性都不是)。我也使用一个实现IMenuItem的AbstractMenuItem类,它是一个辅助类来实例化各个部分中的菜单项。

UPDATE:

关于你的色差问题,确实this thread帮助?

14

不要把MenuItemDataTemplateDataTemplate定义MenuItem内容。相反,通过ItemContainerStyle指定MenuItem无关性:

<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> 

而且,看看HierarchicalDataTemplate秒。

+0

你的意思是在Menu.ItemContainerStyle中定义标题/颜色,然后在DataTemplate中放置一个定义Command和CommandParameter的HierarchicalDataTemplate? – 2009-07-01 08:47:32

+0

谢谢,这正是我一直在寻找的。很棒。谢谢! – 2010-05-04 20:06:06

+0

+1 - HierarchicalDataTemplates使得整个问题几乎无关紧要。 – 2010-12-25 18:21:51

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控制SubMenuItems的显示方式。每个SubMenuItem都有其自己的MenuName属性,类型为ICommand的Command属性以及可能的另一个SubMenuItems集合。

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

希望这会有所帮助。

2

只要让你的DataTemplate成为一个TextBlock(或者可能是一个带有图标和TextBlock的堆栈面板)。

相关问题