从WPF到WinRT/Metro: ICollectionView中分类的变化
目录
- WPF中ICollectionView的分类
- WinRT/Metro中ICollectionView的分类
返回目录
WPF中ICollectionView的分类
在WPF中,ICollectionView的分类是靠着System.ComponentModel中的ICollectionView的GroupDescriptions属性中添加GroupDescription对象。而WPF对GroupDescription的执行类型是PropertyGroupDescription,后者用一个IValueConverter还获取所有数据的接着在UI上,绑定到ICollectionView,然后在设置ItemsControl的GroupStyle来规划具体的布局。
如下WPF代码:
//listBox变量是XAML中的ListBox名称
//获取数据
var data = Enumerable.Range(1, 10);
//ICollectionView
var view = CollectionViewSource.GetDefaultView(data);
//添加GroupDescription
view.GroupDescriptions.Add(new PropertyGroupDescription("", new IntConverter()));
//绑定
listBox.ItemsSource = view;
IntConvert是把数据转换成分类名称的IValueConverter,它的Convert方法就是非常简单的返回一个整数余3的结果:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((int)value) % 3;
}
最后在界面上,设置好ListBox的ItemsPanel和GroupStyle就可以了:
<ListBox Name="listBox" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
运行:
当然我们可以通过GroupStyle的ItemContainerStyle属性来定义背后GroupItem的具体模板,比如可以把分类数据放在Expander里,那么这样设置GroupStyle:
<GroupStyle>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Margin" Value="5"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" Header="{Binding}" BorderBrush="Red" BorderThickness="2">
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
运行结果:
返回目录
WinRT/Metro中ICollectionView的分类
在WinRT/Metro中,ICollectionView本身被削弱了很多,不仅没有GroupDescription,SortDescription和Filter也没了。原因是微软推荐使用LINQ来进行这些数据操作。
注意WPF正式出生在.NET Framework 3.0中,那时还没有LINQ,所以那时ICollectionView提供了这些操作,但是和随后.NET Framework 3.5中的LINQ比简直是弱爆了,因此WinRT作为XAML进化的最新产物,抛弃了原始ICollectionView的那些功能。
所以在WPF中,数据源本身就是原始的类似数组的存储,ICollectionView提供了分类(还有排序,帅选)操作。但是在WinRT中,这些统统交给LINQ了,那么分类的数据在数据源中就已经被分类的。
所以代码是这样的:
//gridView是XAML中GridView的名称
//数据(使用LINQ分类过的)
var data = Enumerable.Range(1, 10).GroupBy(i => i % 3);
//从CollectionViewSource中获取ICollectionView
var viewSource = new CollectionViewSource()
{
Source = data
};
//设置IsSourceGrouped和ItemPath
viewSource.IsSourceGrouped = true;
viewSource.ItemsPath = new PropertyPath("");
//绑定到UI
gridView.ItemsSource = viewSource.View;
在UI上,一个很无语的变化是:“在WPF中ItemsControl.ItemsPanel是规划分类内的项目,而GroupStyle.Panel是规划分类。而在WinRT中,是反的,也就是说ItemsControl.ItemsPanel规划的是分类。WinRT的逻辑倒是更符合常理,因为ItemsControl包含的是分类数据,分类数据再包含子数据”。那么如果想达到分类为WrapGrid,分类内是横向的StackPanel,WinRT的XAML是这样定义的:
<GridView Name="gridView" SelectionMode="None">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
注意GroupStyle的Panel不能设置任何VirtualizingPanel,否则会抛出异常,错误信息:“Error HRESULT E_FAIL has been returned from a call to a COM component.”。
注意:WrapGrid不同于WPF的WrapPanel,前者会把每一个项目放在同一个大小的区域内。
结果:
TAG: