如何将 ViewModels 的 ObservableCollection 绑定到 MenuItem? [英] How can I bind an ObservableCollection of ViewModels to a MenuItem?

查看:21
本文介绍了如何将 ViewModels 的 ObservableCollection 绑定到 MenuItem?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我用 ObservableCollection 绑定 Menu Items 时,只有 MenuItem 的内部"区域是可点击的:

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

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

<菜单项Header="Options" ItemsSource="{Binding ManageMenuPageItemViewModels}"ItemTemplate="{StaticResource MainMenuTemplate}"/></菜单>

然后我用这个DataTemplate绑定它:

<菜单项Header="{绑定标题}"命令="{绑定 DataContext.SwitchPageCommand,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}"背景=红色"CommandParameter="{Binding IdCode}"/></数据模板>

由于 ObservableCollection ManageMenuPageItemViewModels 中的每个 ViewModel 都有一个属性 TitleIdCode,上面的代码乍一看还不错.

然而,问题是DataTemplate中的MenuItem实际上是里面另一个MenuItem(>好像它被绑定了两次) 这样在上面带有 Background="Red" 的 DataTemplate 中,每个菜单项内都有一个 红色框 并且只能点击这个区域,而不是整个菜单项区域本身(例如,如果用户点击复选标记所在的区域或内部可点击区域的右侧或左侧,则什么也不会发生,这,如果没有单独的颜色很混乱.)

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

更新:

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

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

我的 DataTemplate 中只有一个 TextBlock,但我仍然无法为整个 MenuItem 着色",而只能为 TextBlock 着色:

<TextBlock Text="{绑定标题}"/></数据模板>

我将 Command 绑定放入 Menu.ItemContainerStyle 但它们现在不触发:

<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}"/></风格></Menu.ItemContainerStyle><菜单项Header="MVVM" ItemsSource="{绑定 MvvmMenuPageItemViewModels}"ItemTemplate="{StaticResource MainMenuTemplate}"/><菜单项Header="Application" ItemsSource="{Binding ApplicationMenuPageItemViewModels}"ItemTemplate="{StaticResource MainMenuTemplate}"/><菜单项Header="Manage" ItemsSource="{Binding ManageMenuPageItemViewModels}"ItemTemplate="{StaticResource MainMenuTemplate}"/></菜单>

解决方案

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

<Menu DockPanel.Dock="Top" ItemsSource="{绑定路径=(local:MainViewModel.MainMenu)}"><Menu.ItemContainerStyle><风格><Setter Property="MenuItem.Header" Value="{绑定路径=(contracts:IMenuItem.Header)}"/><Setter Property="MenuItem.ItemsSource" Value="{绑定路径=(contracts:IMenuItem.Items)}"/><Setter Property="MenuItem.Icon" Value="{绑定路径=(contracts:IMenuItem.Icon)}"/><Setter Property="MenuItem.IsCheckable" Value="{绑定路径=(contracts:IMenuItem.IsCheckable)}"/><Setter Property="MenuItem.IsChecked" Value="{绑定路径=(contracts:IMenuItem.IsChecked)}"/><Setter Property="MenuItem.Command" Value="{Binding}"/><Setter Property="MenuItem.Visibility" Value="{Binding Path=(contracts:IMenuItem.Visible),转换器={StaticResource BooleanToVisibilityConverter}}"/><Setter Property="MenuItem.ToolTip" Value="{绑定路径=(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}}"/></控制模板></Setter.Value></Setter></数据触发器></Style.Triggers></风格></Menu.ItemContainerStyle></菜单></DockPanel>

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

公共接口 IMenuItem : ICommand{字符串标题{获取;}IEnumerable项目{得到;}对象图标 { 得到;}bool IsCheckable { 获取;}bool IsChecked { 获取;放;}布尔可见{得到;}bool IsSeparator { 获取;}字符串工具提示 { 获取;}}

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

public IEnumerable主菜单{得到;放;}

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

更新:

关于您的颜色问题,这个主题有帮助吗?>

When I bind Menu Items with an ObservableCollection, only the "inner" area of the MenuItem is clickable:

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

In my View I have this menu:

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

Then I bind it with this 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>

Since each ViewModel in the ObservableCollection ManageMenuPageItemViewModels has a property Title and IdCode, the above code works fine at first sight.

HOWEVER, the problem is that the MenuItem in the DataTemplate is actually inside another MenuItem (as if it is being bound twice) so that in the above DataTemplate with Background="Red" there is a Red box inside each menu item and only this area can be clicked, not the whole menu item area itself (e.g. if the user clicks on the area where the checkmark is or to the right or left of the inner clickable area, then nothing happens, which, if you don't have a separate color is very confusing.)

What is the correct way to bind MenuItems to an ObservableCollection of ViewModels so that the whole area inside each MenuItem is clickable?

UPDATE:

So I made the following changes based on advice below and now have this:

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

I have only a TextBlock inside my DataTemplate, but I still can't "color the whole MenuItem" but just the TextBlock:

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

And I put the Command binding into Menu.ItemContainerStyle but they don't fire now:

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

解决方案

I found using MVVM with MenuItems to be very challenging. The rest of my application uses DataTemplates to pair the View with the ViewModel, but that just doesn't seem to work with Menus because of exactly the reasons you've described. Here's how I eventually solved it. My View looks like this:

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

If you notice, I defined an interface called IMenuItem, which is the ViewModel for a MenuItem. Here's the code for that:

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

Notice that the IMenuItem defines IEnumerable Items, which is how you get sub-menus. Also, the IsSeparator is a way to define separators in the menu (another tough little trick). You can see in the xaml how it uses a DataTrigger to change the style to the existing separator style if IsSeparator is true. Here's how MainViewModel defines the MainMenu property (that the view binds to):

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

This seems to work well. I assume you could use an ObservableCollection for the MainMenu. I'm actually using MEF to compose the menu out of parts, but after that the items themselves are static (even though the properties of each menu item are not). I also use an AbstractMenuItem class that implements IMenuItem and is a helper class to instantiate menu items in the various parts.

UPDATE:

Regarding your color problem, does this thread help?

这篇关于如何将 ViewModels 的 ObservableCollection 绑定到 MenuItem?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆