我为自己设计了一个非常基本的WPF练习,这很奇怪,即从ViewModel动态填充菜单。给定以下主窗口标记:

<Window x:Class="Demosne.Client.WPF.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525"
    xmlns:project="clr-namespace:Demosne.Client.WPF">
<Grid>
    <Menu Height="26" Name="menu1" VerticalAlignment="Top" HorizontalAlignment="Stretch" ItemsSource="{Binding MainMenuItems}">
        <Menu.ItemTemplate>
            <HierarchicalDataTemplate >
                <MenuItem Header="{Binding Text, Mode=OneTime}" ItemsSource="{Binding MenuItems}"/>
            </HierarchicalDataTemplate>
        </Menu.ItemTemplate>
        <!--<MenuItem Header="File" />
        <MenuItem Header="Edit" />-->
    </Menu>
</Grid>

和ViewModel:
public class MainWindowViewModel
{
    private IList<MenuItemViewModel> _menuItems = new List<MenuItemViewModel>()
    {
        new MenuItemViewModel() { Text = "File" },
        new MenuItemViewModel() { Text = "Edit" }
    };

    public IList<MenuItemViewModel> MainMenuItems
    {
        get
        {
            return _menuItems;
        }
    }
}

    public class MenuItemViewModel
{
    public string Text { get; set; }

    public IList<MenuItemViewModel> MenuItems
    {
        get
        {
            return _menuItems;
        }
    }

    private IList<MenuItemViewModel> _menuItems = new List<MenuItemViewModel>();
}

我希望GUI能够完全重现标记中两个注释掉的行的结果-两个名为File和Edit的MenuItems。

但是,绑定(bind)版本在鼠标悬停时表现异常:

标记版本:

装订版本:

他们为什么不同?

最佳答案

您得到的结果很有趣,因为您没有真正正确地使用HierarchicalDataTemplate

当您在Menu上设置itemssource时,它将为集合中的每个对象创建一个MenuItem,并且如果您还提供了一个带有itemssource集合的HierarchicalDataTemplate,它将为该集合中的每个子对象也创建MenuItems。层次结构。

在您的情况下,您已经在模板中自己添加了MenuItem,这不是必需的。框架会为您隐式创建这些项目。这导致菜单行为异常。

因此,要获得正确的结果,您应该执行以下操作:

<HierarchicalDataTemplate ItemsSource="{Binding MenuItems}">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Text}" />
    </StackPanel>
</HierarchicalDataTemplate>

更新
通过在某个对象上设置DataTemplate,您可以告诉WPF您要控制的每个项目的显示方式。

在这种情况下,将使用HierarchicalDataTemplate,它是用于生成 header 控件的模板。这种控件包含标题和项目集合。

当您将这种模板应用于对象时,您在模板中放置的内容都将用作标题,并且通过将模板应用于集合集中每个子对象(作为ItemsSource on)来创建items集合。模板。因此,它将以递归方式将模板应用于层次结构中的所有对象。

在您的示例中,您有一个菜单。您可以通过执行以下操作来创建它:
<Menu ItemsSource="{Binding MainMenuItems}" />

它将正常工作,但是由于您尚未应用模板来告诉它应如何显示集合中的项目,因此它将仅为itemssource中的每个对象创建一个MenuItem并在其上调用ToString()。然后,此值将用作MenuItem上的Header属性。

由于那不是您想要的,因此您必须应用一个模板,以告诉WPF您想要在隐式生成的MenuItem的 header 中显示为什么内容。

在我的示例中,我只是制作了一个包含TextBlock的模板,该模板绑定(bind)到viewmodel上的Text属性。

更新2
如果现在要在隐式创建的菜单项上设置属性,则必须通过在ItemContainerStyle上设置HierarchicalDataTemplate属性来实现。此处定义的样式将应用于所有生成的菜单项。

因此,要将MenuItem的Command属性绑定(bind)到viewmodel上的Command属性,可以执行以下操作:
<HierarchicalDataTemplate.ItemContainerStyle>
    <Style TargetType="MenuItem">
        <Setter Property="Command"
                Value="{Binding Command}" />
    </Style>
</HierarchicalDataTemplate.ItemContainerStyle>

关于wpf - WPF DataTemplates-为什么渲染有所不同?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13539942/

10-12 07:03