WPF菜单使用HierarchicalDataTemplate进行绑定时,无法正常呈现菜单项。

8
我正在构建一个WPF应用程序,力求紧密遵循MVVM原则。我在渲染菜单时遇到了问题。我尝试了几种方法但卡住了。看起来我的绑定是正确的,但我对样式操作不确定。
以下是我遇到问题的代码。正如我所说,绑定似乎是好的,我甚至可以使用Snoop看到Header菜单项的正确值,但我看到的只是菜单项的空容器。
 <DockPanel>
        <DockPanel.Resources>
            <HierarchicalDataTemplate x:Key="TopMenuHDT" ItemsSource="{Binding Children}">
                <HierarchicalDataTemplate.ItemContainerStyle>
                    <Style TargetType="{x:Type MenuItem}">
                        <Setter Property="Command" Value="{Binding Command}" />
                        <Setter Property="Header" Value="{Binding MenuText}" />
                        <Setter Property="Icon">
                            <Setter.Value>
                                <Image Source="{Binding MenuIcon}" Height="16px" Width="16px" />
                            </Setter.Value>
                        </Setter>
                    </Style>
                </HierarchicalDataTemplate.ItemContainerStyle>
            </HierarchicalDataTemplate>

        </DockPanel.Resources>
        <Menu DockPanel.Dock="Top" Height="auto"
              ItemsSource="{Binding TopMenuItems}" 
              ItemTemplate="{StaticResource TopMenuHDT}"/>

在我的主ViewModel中:
    private ObservableCollection<MenuViewModel> _topMenuItems;
    public ObservableCollection<MenuViewModel> TopMenuItems
    {
        get { return _topMenuItems; }
        set
        {
            if (_topMenuItems == value)
                return;

            _topMenuItems = value; base.RaisePropertyChanged("TopMenuItems");
        }
    }
...
    public void LoadMainMenu()
    {
        IList<ViewModels.MenuViewModel> fileMenuItems = PopulateFileMenuEntries();
        IList<ViewModels.MenuViewModel> editMenuItems = PopulateEditMenuEntries();

        _topMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "_File", Children = new ObservableCollection<ViewModels.MenuViewModel>(fileMenuItems) });
        _topMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "_Edit", Children = new ObservableCollection<ViewModels.MenuViewModel>(editMenuItems) });

   private IList<ViewModels.MenuViewModel> PopulateFileMenuEntries()
    {
        List<ViewModels.MenuViewModel> fileMenuItems = new List<ViewModels.MenuViewModel>();

        fileMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "_Open", MenuIcon = new BitmapImage(new Uri("pack://application:,,,/Resources/OpenDocument16.png")) , Command = _mainWindowViewModel.OpenCommand });
        fileMenuItems.Add(new ViewModels.MenuViewModel() { MenuText = "Open _Recent" });

        return fileMenuItems;
    }

菜单视图模型:

public class MenuViewModel : ObservableObject
{
    internal MenuViewModel()
    {
        IsEnabled = true;
    }

    private string _menuText;
    public string MenuText
    {
        get { return _menuText; }
        set
        {
            if (_menuText == value)
                return;

            _menuText = value; base.RaisePropertyChanged("MenuText");
        }
    }

    private ICommand _command;
    public ICommand Command
    {
        get { return _command; }
        set
        {
            if (_command == value)
                return;

            _command = value; base.RaisePropertyChanged("Command");
        }
    }

    private BitmapImage _menuIcon;
    public BitmapImage MenuIcon
    {
        get { return _menuIcon; }
        set
        {
            if (_menuIcon == value)
                return;

            _menuIcon = value; base.RaisePropertyChanged("MenuIcon");
        }
    }


    private ObservableCollection<MenuViewModel> _children;
    public ObservableCollection<MenuViewModel> Children
    {
        get { return _children; }
        set
        {
            _children = value; base.RaisePropertyChanged("Children");
        }
    }
}

任何对于正确渲染此内容的帮助都将不胜感激。
编辑:
如果有人遇到类似问题,这里是最终解决方案:
<DockPanel>
    <Menu DockPanel.Dock="Top" Height="auto" ItemsSource="{Binding TopMenuItems}" >
        <Menu.Resources>
            <Image x:Key="MenuIconResource" Height="16" Width="16" Source="{Binding MenuIcon}" x:Shared="False" />
            <Style TargetType="{x:Type MenuItem}">
                <Setter Property="Command" Value="{Binding Command}" />
                <Setter Property="Header" Value="{Binding MenuText}" />
                <Setter Property="InputGestureText" Value="{Binding ShortcutText}" />
                <Setter Property="IsEnabled" Value="{Binding IsEnabled}" />
                <Setter Property="Icon" Value="{StaticResource MenuIconResource}" />
                <Setter Property="ItemsSource" Value="{Binding Children}"/>

                <Style.Triggers>
                    <DataTrigger Binding="{Binding }" Value="{x:Null}">
                        <Setter Property="Template" >
                            <Setter.Value>
                                <ControlTemplate>
                                    <Separator Style="{StaticResource {x:Static MenuItem.SeparatorStyleKey}}" />
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Menu.Resources>
    </Menu>

1
你的HierarchicalDataTemplate中模板在哪里?我只能看到定义了ItemContainerStyle。 - Dtex
我明白你的意思。我从错误的角度考虑这个问题,看起来为了以最小的努力维持菜单的标准外观,以下所概述的自定义样式是更好的方式? - genus
@genus 有没有想法如何编辑以支持分隔符? - Lee Louviere
1个回答

12

试试这个替代你的DataTemplate

 <DockPanel>
    <Menu DockPanel.Dock="Top" Height="auto"
          ItemsSource="{Binding TopMenuItems}">

        <Menu.Resources>
            <Style TargetType="{x:Type MenuItem}">
                <Setter Property="Command" Value="{Binding Command}" />
                <Setter Property="Header" Value="{Binding MenuText}" />
                <Setter Property="Icon">
                    <Setter.Value>
                        <Image Source="{Binding MenuIcon}" Height="16px" Width="16px" />
                    </Setter.Value>
                </Setter>
                <Setter Property="ItemsSource" Value="{Binding Children}"/>
            </Style>
        </Menu.Resources>

    </Menu>
</DockPanel>

是的,谢谢!这非常接近我需要的东西。它运行良好,除了菜单图标设置的图像未显示出来。我将我的 BitmapImage MenuIcon 设置为应用程序中的静态资源,即 new BitmapImage(new Uri("pack://application:,,,/Resources/OpenDocument16.png"))。有什么想法吗? - genus
如果您返回一个类似于“Resources/OpenDocument16.png”的字符串,会怎样呢? - user195275
这个方法在一定程度上是有效的,因为从你点击到解析图标所需的时间太长了,会导致菜单打开时出现延迟。我很快就会在问题中添加我的解决方案。 - genus
今天花了好几个小时试图让一个数据库驱动的嵌套菜单项工作。这个方法完美地解决了问题。谢谢你! - Michael Norgren
我觉得如果不说“谢谢”就太过失礼了——我花了一些时间来尝试让我的“菜单”正常工作,而你的答案正是我所需要的。+1 - Wai Ha Lee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接