延续上文,我对代码略作了改动:
(1)增加了一个ControlStyleSelector,用来调试GroupItem真正的绑定对象。
(2)实现了GroupStyle.ContainerStyle。为了调试,我先实现了一个Auto-Apply的Style,在文章最后给出了真正的实现。
数据结构:
class Data { public string Value { get; set; } public string Type { get; set; } }
XAML:
<Window x:Class="WpfApplication2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication2" Title="MainWindow" SizeToContent="WidthAndHeight"> <Window.Resources> <local:ControlStyleSelector x:Key="selector" /> <Style TargetType="GroupItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <StackPanel> <Expander Header="{Binding Name}"> <ItemsPresenter /> </Expander> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> <DataTemplate DataType="{x:Type local:Data}"> <TextBlock Text="{Binding Value}"/> </DataTemplate> </Window.Resources> <StackPanel Margin="5"> <ListBox ItemsSource="{StaticResource data}"> <ListBox.GroupStyle> <GroupStyle ContainerStyleSelector="{StaticResource selector}"> </GroupStyle> </ListBox.GroupStyle> </ListBox> </StackPanel> </Window>
代码:
/// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { ObservableCollection<Data> data = new ObservableCollection<Data>(); for (int i = 1; i <= 9; i += 2) data.Add(new Data() { Value = i.ToString(), Type = "Odd" }); for (int i = 0; i < 10; i += 2) data.Add(new Data() { Value = i.ToString(), Type = "Even" }); this.Resources.Add("data", data); InitializeComponent(); ICollectionView vw = CollectionViewSource.GetDefaultView(data); vw.GroupDescriptions.Add(new PropertyGroupDescription("Type")); } } class ControlStyleSelector : StyleSelector { public override Style SelectStyle(object item, DependencyObject container) { return base.SelectStyle(item, container); } }
运行截图:
可以看到分组展开与收缩的功能已经实现,原先GroupItem下的Visual Tree中的StackPanel也已经被替换成了Expander了。在Expander内,我直接把ItensPresenter放到里面,他自动显示出了分组的内容。根据调试,其实GroupItem绑定到的是一个MS内部结构:
MS.Internal.Data.CollectionViewGroupInternal,派生自CollectionViewGroup类型,父类提供了Name和Items属性,所以我可以用来做绑定!上面的XAML是调试性质,真真的写法应该把Style放在ListBox的GroupStyle内部,或者变成一个Resource,代码如下:
<Window x:Class="WpfApplication2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication2" Title="MainWindow" SizeToContent="WidthAndHeight"> <Window.Resources> <DataTemplate DataType="{x:Type local:Data}"> <TextBlock Text="{Binding Value}"/> </DataTemplate> </Window.Resources> <StackPanel Margin="5"> <ListBox ItemsSource="{StaticResource data}"> <ListBox.GroupStyle> <GroupStyle> <GroupStyle.ContainerStyle> <Style TargetType="GroupItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Expander Header="{Binding Name}"> <ItemsPresenter /> </Expander> </ControlTemplate> </Setter.Value> </Setter> </Style> </GroupStyle.ContainerStyle> </GroupStyle> </ListBox.GroupStyle> </ListBox> </StackPanel> </Window>