将 CollectionViewSource 与 TabControl 结合使用
Use CollectionViewSource with TabControl
我试图通过使用 XAML 代码来分组和显示 ObservableCollection 的项目。它使用简单的 CollectionViewSource 和 ListBox[1] 效果很好。
实际上,我更愿意在选项卡控件中显示组的内容。 Google 让我看到了以下 social.msdn 文章,其中介绍了一种使用隐藏代码将组显示为 TabControl 的解决方法:
但是,由于我使用的是 MVVM 并且必须仅依赖 xaml,因此我无法使其正常工作。实际上,CollectionViewSource 填充了组(TabControl 显示了正确的 tabItemHeaders),但是单击这些 TabItem 中的任何一个都会冻结应用程序。这是我尝试过的:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<Grid >
<TabControl ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups, Mode=OneWay}" DataContext="{Binding Source={StaticResource cvs}, Mode=OneWay}">
<!-- First Level -->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Items}">
Second Level
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Name}">
<ListBox ItemsSource="{Binding Items}">
The Item of the Collection
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value.Comment}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</StackPanel>
[1]:xaml 的和平确实按预期工作,但使用包装面板显示组内容:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" VerticalContentAlignment="Top" ItemContainerStyle="{StaticResource ModulSelectInputParameterListBoxItemContainerStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="DarkGray" BorderThickness="2" Margin="2">
<TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Path=Name}" HorizontalAlignment="Center" MinWidth="100"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="2"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Border BorderThickness="2" BorderBrush="DarkGray">
<StackPanel>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
<ItemsPresenter Margin="2,0,2,2" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Vertical" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>
我认为您的绑定有问题您的代码应该可以工作。
要在两个 ListBox 中获得相同的项目,请尝试将第二个 ListBox Itemssource 绑定到第一个 ListBox Itemssource,如下所示:
<ListBox ItemsSource="{Binding Items}" Name="ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Key}">
<ListBox ItemsSource="{Binding ItemsSource, ElementName=ListBox}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
感谢 Joh 在适当的数据绑定方面提供的帮助。然而,原因完全不同,下面给出了一个快速而肮脏的解决方案[1]:
基本上,我没有注意到上面提到的选项卡控件嵌套在我的主 window 中的外部选项卡控件中。我不太确定以下描述是否完全正确[1],但在我看来原因如下:
- 外层的 TabControl 使用样式来显示其内容。此内容适用于包含上述可观察集合的 ViewModel,而后者又应该是为内部 tabControl 提供组的 CollectionViewSource 的 ItemsSource。
- 由于我只在外部TabControl.Resources定义了这种样式,而没有为内部选项卡控件定义单独的样式,因此内部选项卡控件继承了外部样式并尝试使用相同的方式显示其数据内容。
- 这个内容又是另一个内部tabControl,调用另一个内部tabControl等等。
[1] 在 inenr 中定义一个空样式 tabControl.Resources 解决了问题:
<TabControl.Resources>
<Style TargetType="TabItem">
</Style>
</TabControl.Resources>
如果有人可以证实这个想法或提供一些指向嵌套控件中共享样式的众所周知问题的链接,我会很高兴。
我试图通过使用 XAML 代码来分组和显示 ObservableCollection 的项目。它使用简单的 CollectionViewSource 和 ListBox[1] 效果很好。
实际上,我更愿意在选项卡控件中显示组的内容。 Google 让我看到了以下 social.msdn 文章,其中介绍了一种使用隐藏代码将组显示为 TabControl 的解决方法:
但是,由于我使用的是 MVVM 并且必须仅依赖 xaml,因此我无法使其正常工作。实际上,CollectionViewSource 填充了组(TabControl 显示了正确的 tabItemHeaders),但是单击这些 TabItem 中的任何一个都会冻结应用程序。这是我尝试过的:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<Grid >
<TabControl ItemsSource="{Binding Source={StaticResource cvs}, Path=Groups, Mode=OneWay}" DataContext="{Binding Source={StaticResource cvs}, Mode=OneWay}">
<!-- First Level -->
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding Items}">
Second Level
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Name}">
<ListBox ItemsSource="{Binding Items}">
The Item of the Collection
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value.Comment}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</StackPanel>
[1]:xaml 的和平确实按预期工作,但使用包装面板显示组内容:
<StackPanel x:Key="ModulSelectInputParameterView">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" x:Name="collectionViewSource" Source="{Binding ReferencedLmtItem.ModulInputParameterCollection }">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" VerticalContentAlignment="Top" ItemContainerStyle="{StaticResource ModulSelectInputParameterListBoxItemContainerStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="DarkGray" BorderThickness="2" Margin="2">
<TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Path=Name}" HorizontalAlignment="Center" MinWidth="100"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="2"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Border BorderThickness="2" BorderBrush="DarkGray">
<StackPanel>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
<ItemsPresenter Margin="2,0,2,2" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Vertical" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>
我认为您的绑定有问题您的代码应该可以工作。 要在两个 ListBox 中获得相同的项目,请尝试将第二个 ListBox Itemssource 绑定到第一个 ListBox Itemssource,如下所示:
<ListBox ItemsSource="{Binding Items}" Name="ListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Key}">
<ListBox ItemsSource="{Binding ItemsSource, ElementName=ListBox}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text=" - "/>
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
感谢 Joh 在适当的数据绑定方面提供的帮助。然而,原因完全不同,下面给出了一个快速而肮脏的解决方案[1]:
基本上,我没有注意到上面提到的选项卡控件嵌套在我的主 window 中的外部选项卡控件中。我不太确定以下描述是否完全正确[1],但在我看来原因如下:
- 外层的 TabControl 使用样式来显示其内容。此内容适用于包含上述可观察集合的 ViewModel,而后者又应该是为内部 tabControl 提供组的 CollectionViewSource 的 ItemsSource。
- 由于我只在外部TabControl.Resources定义了这种样式,而没有为内部选项卡控件定义单独的样式,因此内部选项卡控件继承了外部样式并尝试使用相同的方式显示其数据内容。
- 这个内容又是另一个内部tabControl,调用另一个内部tabControl等等。
[1] 在 inenr 中定义一个空样式 tabControl.Resources 解决了问题:
<TabControl.Resources>
<Style TargetType="TabItem">
</Style>
</TabControl.Resources>
如果有人可以证实这个想法或提供一些指向嵌套控件中共享样式的众所周知问题的链接,我会很高兴。