先来看看ListView的层级

  • ItemsControl
    • Selector
      • ListViewBase
        • ListView

ItemsControl

ItemsControl Class用来表示一系列条目(Items)的集合。它直接继承自Control,所以也有ControlTemplate属性,一般是由派生类提供(比如ListView)。它提供了一个ItemsSource辖属,可以用来绑定到一个数据源,从而获取数据条目,甚至可以获取条目的更新。如果数据比较静态,可以使用ItemsControl的另一个辖属Items,直接喂给静态的数据。

鉴于XAML通常的做法是将视觉相关的细节往后推延,ItemsControl可以配置ItemTemplate(DataTemplate类型)和ItemsPanel(ItemsPanelTemplate类型)来指定如果显示条目以及列表面板。默认的ItemsPanel将条目放置在一个StackPanel中。DisplayMemberPath可以用来指定对象的某个辖属作为条目(而不是对象本身),默认是“”。IItemContainerMapping 辖属允许ItemsControl将条目映射到UI元素。

Selector

Selector Class继承自ItemsControl。可以在ItemSource上增加一层选择操作。SelectedIndex辖属可以指定为选中条目的下标(默认为-1,表示没有选中任何条目)。SelectedItem则直接返回选中的条目。SelectedValue则可以返回SelectedItem的某个辖属(通过SelectedValuePath指定)的值。对于可以多选的列表,可以使用SelectedItems来返回选中的所有条目。

如果ItemSource是CollectionViewSource,那么Selector默认会和其同步。当CollectionViewSource加载时,会选中第一个条目。如果不想要这个行为,可以将 IsSynchronizedWithCurrentItem置为false。

ListViewBase

ListViewBase Class从Selector派生。是ListView和GridView的父类,实现两者之间的重叠的功能。

ListView

ListView Class从ListViewBase派生。默认支持单选,如果需要改变其选项行为,可以修改其SelectionMode。ListView有若干交互行为,查看:

If you populate the ListView by setting the ItemsSource property, the ItemTemplate is applied to every item. If you populate the Items collection directly, the ItemTemplate is applied only if the item is not a ListViewItem. See Examples for more info.

ListView可以支持大规模的数据显示。为此,不同的语言需要支持不同的接口,比如C++需要实现IObservableVector。通过ISupportIncrementalLoading接口,ListView可以支持渐进式加载,并且可以使用DataFetchSize, IncrementalLoadingThreshold, IncrementalLoadingTrigger, LoadMoreItemsAsync等辖属来控制加载过程。

ListView还支持ISemanticZoomInformation,可以作为一个视图,在SemanticZoom中使用(注意要把ListView自身ControlTemplate中的ScrollViewer.IsVerticalScrollChainingEnabled 设置为False)。ListView在SemanticZoom中使用的时候,下列成员有效:IsActiveView, IsZoomedInView, SemanticZoomOwner, CompleteViewChange, CompleteViewChangeFrom, CompleteViewChangeTo, InitializeViewChange, MakeVisible, StartViewChangeFrom, StartViewChangeTo。

2019-11-16更新

DataTemplate Class

  • ItemsControl.ItemTemplate (which is inherited by various items controls such as ListView, GridView, ListBox )
  • ContentControl.ContentTemplate (which is inherited by various content controls such as Button, Frame, SettingsFlyout )
  • HeaderTemplate and FooterTemplate properties of various items control classes
  • ItemsPresenter.HeaderTemplate and ItemsPresenter.FooterTemplate
  • HeaderTemplate and FooterTemplate properties of text controls such as RichEditBox, TextBox
  • HeaderTemplate property of controls such as ComboBox, DatePicker, Hub, HubSection, Pivot, Slider, TimePicker, ToggleSwitch; some of these also have FooterTemplate

DataTemplate带有一个连带辖属,ExtensionInstance,可以用来操作阶段化的渲染。对应的支撑辖属是ExtensionInstanceProperty,支撑的的方法有:getExtensionInstance和setExtensionInstance。

DataTemplate.LoadContent方法创建模板中指定的UIElement。创建生成的UI元素子树可以作为Child,插入到现有的可视化树中。

VisualTreeHelper.GetChildrenCount和VisualTreeHelper.GetChild可以用来遍历可视化树。

DataTemplate.GetElemen和DataTemplate.RecycleElement是1809新加的,可以把元素从可视化树上拆下来,然后复用,避免每次调用LoadContent。

DataTemplateSelector Class

DataTemplateSelector Class并不能直接作为XAML元素标签使用,通常情况下,它们是以索引的方式存在在资源字典中。

通过在子类重定义DataTemplateSelect的SelectTemplateCore(Object)或者SelectTemplateCore(Object, DependencyObject)(这两都返回DataTempalte实例),可以重新定义SelectTemplate 方法的行为。

App code typically doesn’t call SelectTemplate methods; the methods exists so that the infrastructure can call it while choosing the correct templates based on using a DataTemplateSelector instance from a property value such as ItemsControl.ItemsTemplateSelector.

其他参考

(更新完)

2020-11-08 更新

Item selection and interaction看,ListView的交互模式中并不包含双击(double tapped)。如果强行给它加上的Tapped和DoubleTapped事件处理的话,和会SelectionChanged叠加到一起,其顺序是:

  • SelectionChanged
  • Tapped
  • DoubleTapped

If a user interaction also raises DoubleTapped, Tapped will be raised first to represent the first tap, but the second tap won’t raise an additional Tapped. If you want different logic for Tapped versus DoubleTapped, your Tapped handler may need to use app-specific variables and a timer in order to avoid running on interactions that are eventually interpreted as a DoubleTap action. 来自UIElement.Tapped Event

这种交互选项,可能是源自于Windows 8,参考How to change the interaction mode (XAML)。虽然把ListView的IsItemClickEnabled设置为true,可以产生ItemClick事件,但是和DoubleTapped拌在一起的话,其顺序是:

  • ItemClick
  • SelectionChanged
  • DoubleTapped
  • ItemClick

双击操作可能对触屏用户不太友好吧?

ItemClick可以通过ItemClickEventArgs.ClickedItem直接给出被点击的Item

double tap on list view看,从DoubleTapped事件中找到被点中的ListViewItem,可能需要自己计算坐标:

private void ListView_Tapped(object sender, TappedRoutedEventArgs e)
{
    int item = 0;
    Double coY = e.GetPosition((UIElement)sender).Y;

    ListView lv = sender as ListView;
    if (sender is ListView)
    {
        lv.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        Size lvSize = lv.DesiredSize;
        item = (int)(coY / lvSize.Height * lv.Items.Count);
        item = item > lv.Items.Count ? lv.Items.Count : item;
    }

    var TappedItem = lv.Items[item];
}

上述代码同样来自Get Tapped Item GridView

通过FrameworkElement.DataContext可以在不配知道ListViewItem的情况下,从DoubleTapped事件的OriginalSource中获取被点击的ListViewItem所绑定的数据对象。前提要把OriginalSource先转为FrameworkElement。

对于WPF来说,ListView似乎有HitTest方法,可以判断那个子项被点击了。double tap on list view

这个帖子UWP - ListBoxItem - Click Trigger介绍了另外一个方法,简单地说就是在SelectionChanged里面将SelectedIndex设为-1。这时候双击产生的第二个SelectionChanged将会被触发。

List View Double Tapped Item介绍了另外一种办法,就是将ListViewItem之下的元素的IsHitTestVisible全部设置为false,那么双击产生的DoubleTapped事件的OriginalSource就会是ListViewItem级别的了。

How to: Handle the MouseDoubleClick Event for Each Item in a ListView这个例子看起来象使WPF的,在Style中使用了不受UWP支持的EventSetter。

其他参考

(更新完)