为什么我会对此进行研究呢?是因为我想看看,能否增强一下ListBox的分组功能,让他支持组内元素展开与收缩(类似于TreeView):
WPF 的 ItemsControl 天生支持Grouping(分类),据Pro WPF in C#2010所说:When you use grouping, your list creates a separate GroupItem object for each group, and it adds these GroupItem objects to the list. The GroupItem is a content control, so each GroupItem holds the appropriate container (like ListBoxItem objects) with your actual data. The secret to showing your groups is formatting the GroupItem element so it stands out. You could use a style that applies formatting to all the GroupItem objects in a list.
也就是说,启用了Grouping之后,会生成一些新的对象,GroupItem对象,那么如何启用Grouping呢?在我的测试下,发现,要启用Grouping并且使ListBox显示出Grouping的效果,要满足2个条件:
1)本身,DataSource要启用Grouping
2)ListBox的GroupStyle不能是null。
下面的就是具体测试与分析:
数据结构:
class Data { public string Value { get; set; } public string Type { get; set; } }
启用Grouping,为了简单测试,我生成了奇数序列和偶数序列,分别用Type=Odd/Even来区分,Model是为了测试多层分组功能,请暂且忽略:
/// <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() { Name = i.ToString(), Type = "Odd" }); for (int i = 0; i < 10; i += 2) data.Add(new Data() { Name = i.ToString(), Type = "Even" }); this.Resources.Add("data", data); InitializeComponent(); ICollectionView vw = CollectionViewSource.GetDefaultView(data); vw.GroupDescriptions.Add(new PropertyGroupDescription("Type")); } }
在UI上开启Grouping:
<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" Height="350" Width="525"> <Window.Resources> <DataTemplate DataType="{x:Type local:Data}"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Value}"/> <TextBlock Text=" "/> <TextBlock Text="{Binding Type}" /> </StackPanel> </DataTemplate> </Window.Resources> <StackPanel Margin="5"> <ListBox ItemsSource="{StaticResource data}"> <ListBox.GroupStyle> <GroupStyle> </GroupStyle> </ListBox.GroupStyle> </ListBox> </StackPanel> </Window>
注意,我只是是简单的将一个原始的GroupStyle赋值给了ListBox的GroupStyle属性,下面是ListBox的Visual Tree和界面:
可以看到,UI上面出现了2个分组:Odd和Even;Visual Tree里面,ListBox里面有一个StackPanel,里面放了2个GroupItem控件(GroupItem是ContentControl),GroupItem的Content是一个StackPanel,里面有一个ContentPresenter(用于显示Group的信息)和一个ItemsPresenter(用于显示ListItems)。
接下啦,我对GroupStyle进行设置,先试试看GroupStyle.Panel 属性:
<ListBox.GroupStyle> <GroupStyle> <GroupStyle.Panel> <ItemsPanelTemplate> <UniformGrid Columns="2" /> </ItemsPanelTemplate> </GroupStyle.Panel> </GroupStyle> </ListBox.GroupStyle>
可以看到,UniformGrid 替代了原先的StackPanel,好我们再设置GroupStyle的HeaderTemplate属性,我先把他设置成一个Expander,看看效果,其实我很清楚,这个Expander不会起到展开/收缩的效果,因为ContentPresentator和ItemsPresentator是同级的,注意我将Header绑定到了Name属性,而不是Data.Value属性,据Pro WPF记载:However, there’s one trick. When you write your binding expression, you aren’t binding against the data object from your list (in this case, the Product object). Instead, you’re binding against the PropertyGroupDescription object for that group. That means if you want to display the field value for that group, you need to bind the PropertyGroupDescription.Name property. 我照做了,也确实成功了!但是,这个并不是如书中所写的PropertyGroupDescription.Name,而是。。。(下面一篇博文会写)
<ListBox.GroupStyle> <GroupStyle> <GroupStyle.Panel> <ItemsPanelTemplate> <UniformGrid Columns="2" /> </ItemsPanelTemplate> </GroupStyle.Panel> <GroupStyle.HeaderTemplate> <DataTemplate> <Expander Header="{Binding Name}" /> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ListBox.GroupStyle>
到此为止,我会在下一篇博文中分析GroupStyle的ContainerStyle属性,试试看,能否使用Expander实现分组的展开与收缩。