我为自己设计了一个非常基本的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/