Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.
This repository was archived by the owner on May 1, 2024. It is now read-only.

Xamarin.Forms.CollectionView Spec #3172

Closed
@hartez

Description

@hartez

CollectionView

The current Forms ListView design and implementations are very complex and challenging to maintain, while often not providing the flexibility that users would like. Because of the complexity of the implementations and the backward compatibility requirements, ListView bugs can be difficult to find and fix.

The goal of the CollectionView effort is to address these problems by simplifying the API and embracing the capabilities of the native platforms' list-building controls. To that end:

  • CollectionView removes the concept of Cells entirely. While convenient in some cases, the Cell concept introduces a great deal of complexity into the native implementations of ListView. Everything that is possible with Cells can be accomplished with reusable DataTemplates.

  • CollectionView reduces the API surface. Several properties and events from ListView are not available in CollectionView. Several of these are easily replaceable within DataTemplates; others were very specific to particular platforms and never really belonged in the first place. A list of these changes can be found below.

  • CollectionView aims to be more flexible by not baking in assumptions about layout. This allows us to support layouts which users have long been asking for (e.g., a HorizontalListView) and which the native implementations already provide.

Note: Nothing in this document should be taken as an indication that the current ListView will be removed or will cease to be maintained. This effort is simply aiming to provide an alternative control which will more easily serve the needs of many Forms users.

Note: Nothing in this specification is guaranteed to be final; all features, implementations, and interfaces are subject to change.

Current completion status of the features specified below

Supporting APIs

In order to provide users with a more modern list, the CollectionView effort will include a some supporting APIs:

  • FontIconSource - using scalable glyphs as icons; the intent is to support glyphs whereever Forms currently supports images. This is obviously a desirable feature in many parts of Forms, but it's also absolutely essential to support contextual swipe gestures in CollectionView (see the FontIconSource spec).

  • SwipeView - Provides support for swiping on an element to execute or reveal further actions (see the SwipeView spec).

Layout Specification

This spec provides for the developer to specify whether items to be laid out in a vertical list, horizontal list, or a grid. The underlying native classes all support these layout types. (Note that on iOS, this spec assumes the use of UICollectionView rather than UITableView.)

The specifications given to the CollectionView are mapped to native layouts in each renderer. For example, if a grid layout is specified, on iOS the renderer will (by default) use a UICollectionViewFlowLayout. On Android, the default will be GridLayoutManager; on UWP, GridView.

Forms does not participate in the layout of the items in the repeater (beyond generating the items Views themselves); this portion of the layout is entirely native.

The selection of layout methods to use in each renderer will be user-modifiable; if a user would rather use StaggeredGridLayoutManager on Android, this could be achieved by modifying the selection method and returning the desired layout manager.

ListView API Removals

  • IsPullToRefreshEnabled - this is now handled by the RefreshView.
  • IsRefreshing - this is now handled by the RefreshView.
  • RefreshCommand - this is now handled by the RefreshView.
  • HasUnevenRows - this is now ItemSizingStrategy.
  • RowHeight - this is now determined by the first item to be laid out.
  • SeparatorVisibility - CollectionView does not include built-in separators; users can provide these (if desired) in their templates.
  • SeparatorColor - CollectionView does not include built-in separators; users can provide these (if desired) in their templates.
  • GroupShortName - this property existed to support jump lists and semantic zoom; CollectionView phase 1 does not support these features.

API

ContextItem

ContextItem provides a way to specify a contextual interaction, often displayed as part of a context menu.

public class ContextItem
{
	public static readonly BindableProperty TextProperty;
	public string Text { get; set; }
	
	public static readonly BindableProperty CommandProperty;
	public ICommand Command { get; set; }
	
	public static readonly BindableProperty CommandParameterProperty;
	public object CommandParameter { get; set; }
	
	public static readonly BindableProperty IsEnabledProperty;
	public bool IsEnabled { get; set; }
	
	public static readonly BindableProperty IconProperty;
	public ImageSource Icon { get; set; }
	
	public event EventHandler Invoked;
}

Properties

API Description
Text Gets or sets the text description displayed on the item.
Command Gets or sets the command to execute when this item is invoked.
CommandParameter
IsEnabled Gets or sets a value indicating whether the user can interact with the context item.
Icon Gets or sets the graphic content of the item.

Events

API Description
Invoked Occurs when user interaction indicates that the command represented by this item should execute.

ContextMenu

Provides a way to specify a set of interactions to be displayed in a context menu. A ContextMenu can be provided for any VisualElement, and is activated/displayed according to the native platform conventions.

public static class ContextMenu
{
	public static readonly BindableProperty MenuItemsProperty =
			BindableProperty.CreateAttached("MenuItems", typeof(ContextMenuItems), typeof(VisualElement));
}


public class ContextMenuItems : IList<ContextItem>
{
}

RefreshView

Moved to #5882

SelectionMode

Provides the selection mode behaviors for a CollectionView.

public enum SelectionMode 
{
	None,
	Single,
	Multiple
}

SelectionChangedEventArgs

public class SelectionChangedEventArgs : EventArgs
{
	public IReadOnlyList<object> PreviousSelection { get; }
	public IReadOnlyList<object> CurrentSelection { get; }
}

Properties

API Description
PreviousSelection Gets the list of items that were selected before the selection changed.
CurrentSelection Gets the list of items that are selected after the selection change.

IItemsLayout

Marker interface to indicate that the implementing class specifies a layout used by CollectionView to arrange its items.

public interface IItemsLayout {}

ItemsLayoutOrientation

Enumerates the possible orientations for an ItemsLayout. As items are added, the CollectionView expands in the orientation direction.

public enum ItemsLayoutOrientation
{
    Vertical,
    Horizontal
}

ItemsLayout

Base class for Forms-provided items layouts.

public abstract class ItemsLayout : IItemsLayout
{
	public ItemsLayoutOrientation Orientation { get; }
    
	protected ItemsLayout(ItemsLayoutOrientation orientation);

	public static readonly BindableProperty SnapPointsAlignmentProperty;
	public SnapPointsAlignment SnapPointsAlignment { get; set; }

	public static readonly BindableProperty SnapPointsTypeProperty;
	public SnapPointsType SnapPointsType { get; set; }	

	public static readonly BindableProperty ItemSpacingProperty;
	public Thickness ItemSpacing {get; set;}
}

Properties

API Description
ItemsLayoutOrientation Specifies the direction in the which the CollectionView expands as items are added.
SnapPointsType Specifies the behavior of snap points when scrolling the view.
SnapPointsAlignment Specifies how snap points are aligned with items in the view.
ItemSpacing Specifies the empty space around each item.

ListItemsLayout

public class ListItemsLayout : ItemsLayout
{
	public ListItemsLayout(ItemsLayoutOrientation orientation) : base (orientation);
	
	public static readonly IItemsLayout VerticalList = new ListItemsLayout(ItemsLayoutOrientation.Vertical); 
	public static readonly IItemsLayout HorizontalList = new ListItemsLayout(ItemsLayoutOrientation.Horizontal); 
}

Static Members

API Description
VerticalList Specifies a single column list in which the list grows vertically as new items are added.
HorizontalList Specifies a single row list in which the list grows horizontally as new items are added.

GridItemsLayout

Defines a multi-row or multi-column layout.

public class GridItemsLayout : ItemsLayout
{
	public static readonly BindableProperty SpanProperty;
	public int Span { get; set; }

	public GridItemsLayout([Parameter("Span")] int span, [Parameter("Orientation")] ItemsLayoutOrientation orientation) : base (orientation);
}

SnapPointsAlignment

Enumerates the possible alignments for snap points in a ListItemsLayout.

public enum SnapPointsAlignment
{
	Start,
	Center,
	End
}

Enum Values

API Description
Start Snap points are aligned with the leading edge of items.
Center Snap points are aligned with the center of items.
End Snap points are aligned with the trailing edge of items.

SnapPointsType

Enumerates the possible behaviors for snap points in a ListItemsLayout.

public enum SnapPointsType
{
	None,
	Optional,
	Mandatory,
	OptionalSingle,
	MandatorySingle,
}

Enum Values

API Description
None Scrolling does not snap to items.
Optional Content snaps to the closest snap point to where scrolling would naturally stop along the direction of inertia, if any snap points are sufficiently close.
Mandatory Content always snaps to the closest snap point to where scrolling would naturally stop along the direction of inertia.
OptionalSingle Same behavior as Optional, but only scrolls one item at a time.
MandatorySingle Same behavor as Mandatory, but only scrolls one item at a time.

Properties

API Description
Span Specifies the number of items to lay out in the constrained direction.

ItemSizingStrategy

Provides the possible item measurement strategies to be used by the CollectionView.

public enum ItemSizingStrategy
{
	MeasureAllItems,	
	MeasureFirstItem
}

Enum Values

API Description
MeasureAllItems Each item is measured individually.
MeasureFirstItem Only the first item is measured; all subsequent items are assumed to be the same size as the first.

ItemsUpdatingScrollMode

Defines constants that specify the scrolling behavior of items while updating.

public enum ItemsUpdatingScrollMode
{
	KeepItemsInView,
	KeepScrollOffset,
	KeepLastItemInView
}

Enum Values

API Description
KeepItemsInView Adjusts the scroll offset to keep the first visible item in the viewport when items are added.
KeepScrollOffset Maintains the scroll offset relative to the beginning of the list when items are added.
KeepLastItemInView Adjusts the scroll offset to keep the last visible item in the viewport when items are added.

CollectionView

Displays a list of items.

public class CollectionView : View
{
    public static readonly BindableProperty ItemsLayoutProperty;
    public IItemsLayout ItemsLayout { get; set; }

    public static readonly BindableProperty ItemsSourceProperty;
    public IEnumerable ItemsSource { get; set; }

    public static readonly BindableProperty ItemTemplateProperty;
    public DataTemplate ItemTemplate { get; set; }

    public static readonly BindableProperty ItemsUpdatingScrollMode;
    publio ItemsUpdatingScrollMode ItemsUpdatingScrollMode { get; set; }

    public static readonly BindableProperty HeaderProperty;
    public object Header { get; set; }

    public static readonly BindableProperty HeaderTemplateProperty;
    public DataTemplate HeaderTemplate { get; set; }

    public static readonly BindableProperty IsHeaderStickyProperty;
    public bool IsHeaderSticky { get; set; }

    public static readonly BindableProperty FooterProperty;
    public object Footer { get; set; }

    public static readonly BindableProperty FooterTemplateProperty;
    public DataTemplate FooterTemplate { get; set; }

    public static readonly BindableProperty IsFooterStickyProperty;
    public bool IsFooterSticky { get; set; }

    public static readonly BindableProperty EmptyViewProperty;
    public object EmptyView { get; set; }

    public static readonly BindableProperty EmptyViewTemplateProperty;
    public DataTemplate EmptyViewTemplate { get; set; }

    public static readonly BindableProperty GroupDisplayBindingProperty;
    public BindingBase GroupDisplayBinding { get; set; }

    public static readonly BindableProperty GroupHeaderTemplateProperty;
    public DataTemplate GroupHeaderTemplate { get; set; }

    public static readonly BindableProperty GroupFooterTemplateProperty;
    public DataTemplate GroupFooterTemplate { get; set; }

    public static readonly BindableProperty ItemSizingStrategy;
    public bool ItemSizingStrategy { get; set; }

    public static readonly BindableProperty IsGroupingEnabledProperty;
    public bool IsGroupingEnabled { get; set; }

    public static readonly BindableProperty SelectionModeProperty;
    public SelectionMode SelectionMode { get; set; }

    public static readonly BindableProperty SelectedItemProperty;
    public object SelectedItem { get; set; }

    public static readonly BindableProperty SelectedItemsProperty;
    public IList<object> SelectedItems { get; set; }

    public static readonly BindableProperty SelectionChangedCommandProperty;
    public ICommand SelectionChangedCommand;

    public static readonly BindableProperty SelectionChangedCommandParameterProperty;
    public object SelectionChangedCommandParameter;

    public static readonly BindableProperty RemainingItemsThresholdProperty;
    public int RemainingItemsThreshold { get; set; }

    public void ScrollTo(object item, object group = null, 
        ScrollToPosition position = ScrollToPosition.MakeVisible, bool animate = true);

    public void ScrollTo(int index, int groupIndex = -1, 
        ScrollToPosition position = ScrollToPosition.MakeVisible, bool animate = true);

    public event EventHandler<SelectionChangedEventArgs> SelectionChanged;

    public event EventHandler<EventArgs> RemainingItemsThresholdReached; 
}

Properties

API Description
ItemsLayout Gets or sets the layout specification for the list.
ItemSizingStrategy User hint which can be provided to the control to improve performance. If this is set to MeasureAllItems (the default), each item will be measured individually. In situations where the item size is intended to be uniform, this value can be set to MeasureFirstItem; only the first item will be measured, and all subsequent items will be given the same size as the first.
ItemTemplate Gets or sets the DataTemplate used to display each item.
ItemsUpdatingScrollMode Gets or sets a value that specifies scrolling behavior when the items are updated.
IsGroupingEnabled Gets or set a value which indicates whether the underlying data should be displayed in groups.
Header Gets or sets the string, binding, or view that will be displayed at the top of the control.
HeaderTemplate Gets or sets a data template to use to format the Header.
IsHeaderSticky Specifies whether the header remains in place as the user scrolls. The default is True
Footer Gets or sets the string, binding, or view that will be displayed at the bottom of the control.
FooterTemplate Gets or sets a data template to use to format the Footer.
IsFooterSticky Specifies whether the footer remains in place as the user scrolls. The default is True
EmptyView Gets or sets the string, binding, or view that will be displayed when the ItemsSource is empty.
EmptyViewTemplate Gets or sets a data template to use to format the EmptyView.
GroupHeaderTemplate Gets or sets a DataTemplate for group headers.
GroupFooterTemplate Gets or sets a DataTemplate for group footers.*
ItemsSource The list of objects to be displayed in the control.
SelectionMode Gets or sets the selection behavior for the control.
SelectedItem Gets or sets the selected item for a SelectionMode of Single. If the selected item is removed from the items source, SelectedItem will be set to null.
SelectedItems Gets or sets the selected items for a SelectionMode of Multiple. If selected items are removed from the items source, they will be removed from SelectedItems and SelectionChanged will be raised.
SelectionChangedCommand Gets or sets the ICommand to execute when the selection changes.
SelectionChangedCommandParameter Gets or sets the parameter for the SelectionChangedCommand.
GroupDisplayBinding Gets or sets the binding to use for displaying the group header.
RemainingItemsThreshold Specifies the threshold of items not yet visible in the CollectionView at which the RemainingItemsThresholdReached event will be raised. The default value is -1, meaning the event will never be raised. A 0, the event will be raised when the final item currently in the ItemsSource is displayed. At values greater than 0, the event will be raised when the ItemsSource currently contains that number of items not yet scrolled to.

Methods

API Description
ScrollTo(object item, object group = null, ScrollToPosition position = ScrollToPosition.MakeVisible, bool animate = true) Scrolls the specified item into view.
ScrollTo(int index, int groupIndex = -1, ScrollToPosition position = ScrollToPosition.MakeVisible, bool animate = true) Scrolls the item at the specified index into view.

Events

API Description
SelectionChanged Raised when the SelectedItem or SelectedItems properties change. This includes changes which occur as a result of changing the SelectionMode property.
RemainingItemsThresholdReached Raised when the CollectionView is scrolled far enough that only RemainingItemsThreshold items have not been displayed. This event can be handled to load more items.

Scenarios

Chat Application

The developer has an app which has a chat client. Messages appear at the bottom of the list and scroll up off the screen. How can the developer achieve this UI with the proposed API?

In order to achieve this behavior, the developer should set the CollectionView.ItemsUpdatingScrollMode property to KeepLastItemInView. If the user's scroll position is at the end of the CollectionView, the new items will appear at the bottom and will be scrolled into view. If the user's scroll position is elsewhere, the new items will appear at the bottom but the scroll position will remain unchanged.

Snap to closest

The developer's app shows a list of real estate listings. As the user scrolls through the list the scrolling should be smooth until the user stops. When scrolling stops the app's interface should snap the scroll (animated) so that the top most listing is perfectly aligned with the top of the page. The snap should always be in the direction that produces the least amount of motion.

In order to achieve this behavior, the developer should use a ListItemLayout with the following settings:

  • Orienation: Vertical
  • SnapPointsAlignment: Start
  • SnapPointsType: Mandatory

TableView

The developer wishes to recreate a settings page using a TableView like appearance.

The developer will want to use a ListItemsLayout with a Vertical Orientation. The ItemsTemplate should be set to a DataTemplate which recreates the look and feel of a table cell. If the "settings" interface is meant to address more than one type of setting (e.g., if some cells are meant to toggle a setting on/off and others are meant to navigate to a secondary settings screen), then the developer may wish to create a DataTemplateSelector which implements a DataTemplate for on/off settings (with a toggle switch) and a DataTemplate for navigation (with a TapGesture which pushes the secondary settings page onto the navigation stack).

Infinite Scroll

The developer has an app which shows a "news" feed. The "news" feed has an infinite number of potential items so when the user nears the end of the current loaded set of data the app needs to make an asynchronous call to the server to load more data. It is critical to the app that data be loaded before the user would see either blank space or be stopped from scrolling within the limits of network latency/bandwidth. How can the developer achieve this with the proposed API?

To achieve this, the developer should set the RemainingItemsThreshold property and handle the RemainingItemsThresholdReached event. When the event is raised, the handler can make the asynchronous call to the server to load more data into the underlying ItemsSource.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions