使用带有 HierarchicalDataTemplate 的 TreeView。
但是请注意,如果没有在您的模型上实现 INotifyPropertyChanged,您的绑定将不会在任何属性更改时更新。此外,如果不使用 ObservableCollections 替换您的列表,您的视图将不会随着您向列表中添加更多项目而更新。
像这样的东西应该可以工作,首先定义你的模板:
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Parent}" ItemsSource="{Binding Childs}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:Child}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock>, </TextBlock>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
然后您可以将它们与这样的 TreeView 一起使用(假设 Parents 是 TreeView 的 DataContext 中的一个属性):
<TreeView ItemsSource="{Binding Parents}"/>
如果您不想要 TreeView,您可以轻松地使用 ListView 列出此内容,将 DataTemplates 更改为:
<DataTemplate DataType="{x:Type local:Parent}">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<ListView ItemsSource="{Binding Childs}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Child}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock>, </TextBlock>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
</DataTemplate>
然后你可以像这样绑定它:
<ListView ItemsSource="{Binding Parents}"/>
注意:您可能想要稍微摆弄一下样式,因为开箱即用,这看起来有点垃圾。至少,您可能希望缩进子 ListView(在父 DataTemplate 中定义的那个)并去掉它的边框。
另请注意:为名称和年龄布局多个 TextBlock 的 StackPanel 也不理想,但它又快又脏。你可能想以不同的方式处理它。您可以使用(多)转换器对其进行格式化,使用 StringFormat 或将另一个属性添加到您的模型中以供显示,甚至只是覆盖子类上的 ToString。
另一个编辑
使用 DataGrid 的一个快速(且丑陋)的示例:
<DataGrid ItemsSource="{Binding Parents}" AutoGenerateColumns="False">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding Childs}"/>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}"/>
</DataGrid.Columns>
</DataGrid>
这使用行详细信息模板将数据网格放置在数据网格内。如果单击一行,它会将子项显示为行详细信息。如果您希望详细信息始终可用,您可以删除 RowDetailsTemplate 并将 DataGridTextColumn 替换为 DataGridTemplateColumn,然后为您的数据定义一个模板。