ItemsControl是Windows.UI.Xaml.Control派生出来的三大Control之一。其特点是它的内容是一个条目列表,有点像编程语言中的Vector类型的数据结构。

ItemsControl实现的接口只有一个,那就是IItemContainerMapping。IItemContainerMapping是一个比较抽象的接口,它把列表中的每个条目看成是放在相依对象容器中,所以ContainerFromIndex(Int32) 就是从列表中按照索引获取一个条目,返回的是相依对象; ContainerFromItem(Object) 则是按照对象去获取其容器;IndexFromContainer(DependencyObject)则是根据容器获取索引;ItemFromContainer(DependencyObject)则是根据容器获取条目。IItemContainerMapping的一个相关类型是ItemContainerGenerator

ItemsControl的条目列表则是由ItemCollection进行管理的。它实现了Windows.Foundation.Collections中的若干接口:IIterable<IInspectable>, IObservableVector<IInspectable>, IVector<IInspectable>。这些接口的泛化参数都是IInspectable,表面这些个容器装的都是通用的WinRT对象。

IIterable是一个简单的接口,它的First()提供返回一个迭代器类型IIteratorIIterator提供集合遍历支持,它的Current()方法返回当前对象,如果已经遍历完所有对象了,HasCurrent()会返回假。MoveNext()则前进到下一个对象。GetMany(T[])则返回所有对象。

IVectorItemCollection所实现的另一个接口,它包含了IIterable,并添加了一个额外的辖属Size和若干方法:GetAt()/SetAt()、InsertAt()/RemoveAt()、RemoveAtEnd()/Append()、Clear()/ReplaceAll()、IndexOf()、GetMany()等等。

另外IVector还有一个GetView()方法可以返回IVectorViewIVectorView是一个简化版的IVector接口,只提供Size辖属还有GetAt, GetMany, IndexOf方法。

ItemCollection还实现了另外一个简单的接口IObservableVector,来提供一个事件VectorChanged,作为数据变更通知。该事件的参数类型为IVectorChangedEventArgs,里面带有一个参数CollectionChange,是枚举类型,用来说明改动类型:ItemChanged,ItemInserted,ItemInserted,Reset。

ItemsControl的Items辖属返回一个静态的ItemCollection,里面的条目的具体类型必须是FrameworkElement类型。但是ItemsControl也可以通过ItemsSource辖属绑定到一个动态的数据源。ItemsSource接受的是一个IInspectable,但是对这个IInspectable的具体类型有一些假定,必须实现这些接口中一个:IVector<IInspectable>或者IBindableObservableVector或者IObservableVector<IInspectable>

ItemsControl的辖属ItemTemplate可以用来指定一个DataTemplate,用来指定如何显示列表中的条目。ItemsPanel辖属则可以指定一个ItemsPanelTemplate用来组织条目列表。下面的例子使用 ItemsStackPanel作为ListView的ItemsPanelTemplate:

<ListView>
    <x:String>Hello</x:String>
    <x:String>World</x:String>

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <ItemsStackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ListView>

不是所有的控件都支持更换Panel,ComboBox只支持使用CarouselPanel。

通过ItemsPanelRoot辖属可以获取ItemsPanelTemplate应用之后产生的对象树根节点。

ItemsControl的ItemTemplateSelector辖属可以给条目选择具体的模板。其类型为DataTemplateSelector,和DataTemplate一样,都实现了IElementFactory接口。XAML框架会调用它的SelectTemplate方法来选择一个模板。DataTemplateSelector的派生类需要重载SelectTemplateCore来实现自定义的模板选择逻辑。SelectTemplateCore有SelectTemplateCore(Object)和SelectTemplateCore(Object, DependencyObject)两种变体,如果ItemsControl.ItemsPanel是ItemsStackPanel或者ItemsWrapGrid,则需要重载SelectTemplateCore(Object);如果是VirtualizingStackPanel或者WrapGrid,则需要提供SelectTemplateCore(Object, DependencyObject)。

ItemsControl的DisplayMemberPath辖属可以把条目的数据来源设置为条目的某个辖属。ItemsControl还支持Grouping,也就是条目分组,不过需要CollectionViewSource的支持。

ItemsControl的派生类有以下几个:

  • AutoSuggestBox
  • CommandBarOverflowPresenter
  • MenuFlyoutPresenter
  • Pivot
  • Selector

其中比较重要的是Selector,它是ComboBox、ListBox、ListViewBase以及FlipView的前序类型。SelectorItemsControl加上选择功能,通过Selector提供的SelectedIndex/SelectValue/SelectedItem三个辖属来反映哪个条目在列表中被选中。Selector的SelectedValuePath辖属可以用来控制SelectValue的取值来源。Selector的一些后继类型,比如ListBox,、ListView、 还有GridView,支持多选,也就是同时选中多个条目。多选的话,可以根据SelectedItems来判断其选中的所有条目。根据ItemsSource类型的不同,Selector会有不同的默认行为,如果ItemsSource是CollectionViewSource,那么选项将会默认到当前条目。其他情况下,选项将会默认是第一个条目。如果不喜欢第一个条目被选中,那个可以为ListView和GridView把IsSynchronizedWithCurrentItem设为false(这样SelectedItem和Items里面的当前选项就不会同步了) 。

Selector的GetIsSelectionActive是一个静态方法,可以用来判断某个Selector是否具有焦点。Selector的SelectionChanged是一个事件,可以用来通告选项的变更。SelectionChangedEventArgs有两个辖属,一个是AddedItems,表示新添加的选项;另一个是RemovedItems,表示新删减的选项。对于ListView或者GridView,如果ItemsSource实现了IItemsRangeInfo接口,然后选项的变更是通过SelectRange和DeselectRange触发的,那么SelectionChangedEventArgs为空,需要通过ListViewBase的SelectedRanges来获取选项变更。

ListView和GridView控制条目如何排布,以及如何和用户交互,但是不负责条目如何显示。条目的界面呈现是由条目容器来管理的。ListView的默认条目容器时ListViewItem,对于GridView则是GridViewItem。ListViewItems和GridViewItems有不同的设计风格,前者偏文本,容器多为长方形,容器左边可以放置图片;后者偏图片,容器多为正方形,图片下方可以放置文本描述。ListViewItem或者GridViewItem的Data Template和Control Template合在一起来决定一个条目是如何显示的。提到的Data Template可以通过ItemsControl的ItemTemplate辖属来设置,而ItemContainerStyle则可以通过ItemsControl的ItemContainerStyle辖属来设置。Item containers and templates提供了更多关于ListViewItem和GridViewItem的自定义信息。

其他

更多关于ListView,GridView的使用指导可以参考List view and grid view

更多关于ComboBox的使用指南可以参考Combo box and list box

(本篇完)

2020-11-25更新

ItemsControl自身具有绘制能力,可以直接作为UI控件使用。可以通过ItemCollection来定义供ItemsControl使用的资源。

IItemContainerMapping需要深入学习下,其提供了Container到ItemCollection条目的映射。ContainerFromIndex(Int32)和ContainerFromItem(Object)用于获取条目对应的Container,IndexFromContainer(DependencyObject)和ItemFromContainer(DependencyObject)则是相反的操作。

ItemsControl的ItemCollection或者ItemSource中的条目,会套上ItemTemplate,然后装进ItemContainter,在ItemsPanel上集结显示。对于ListView,其Continer就是ListViewItem。ItemsControl默认的Container是ContentPresenter。

ListViewItem其实是一个SelectorItem,更上一层,是一个ContentControl。在ListViewItem的默认Style中,定义了ListViewItemPresenter作为ListViewItem的ControlTemplate。

查看ItemContainerStyle。通过ItemContainerStyleSelector可以预设一些ItemContainerStyle的逻辑。

(更新完)