2012年10月12日星期五

从WPF到WinRT/Metro: ICollectionView中分类的变化

从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>

 

运行:

image

 

当然我们可以通过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>

 

运行结果:

image

 

 

 

返回目录

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,前者会把每一个项目放在同一个大小的区域内。

 

结果:

image


TAG: