New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xamarin.Forms.CollectionView Spec #3172

Open
hartez opened this Issue Jun 27, 2018 · 76 comments

Comments

@hartez
Copy link
Member

hartez commented Jun 27, 2018

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

Represents a container control that provides refresh functionality for its content.

public class RefreshView : ContentView
{
	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 VisualizationStateProperty;
	public RefreshVisualizationState VisualizationState { get; set; }

	public static readonly BindableProperty PullDirectionProperty;
	public RefreshPullDirection PullDirection { get; set; }

	public void RequestRefresh();
}

Properties

API Description
Command Gets or sets the command to execute when this item is invoked.
CommandParameter Optional parameter passed to the refresh command.
IsEnabled Gets or sets a value indicating whether the user can interact with RefreshView.
VisualizationState Gets or sets the state of the refresh visualization.
PullDirection Gets or sets a value that specifies the direction to pull to initiate a refresh.

Methods

API Description
RequestRefresh Invokes the refresh command and manages the value of RefreshVisualizationState.

RefreshVisualizationState

public enum RefreshVisualizationState
{
	Idle,
	Active
}

Enum Values

API Description
Idle The refresh visualization is not displayed.
Active The refresh visualization is displayed.

RefreshPullDirection

public enum RefreshPullDirection
{
	LeftToRight,
	TopToBottom,
	RightToLeft,	
	BottomToTop
}

Enum Values

API Description Notes
LeftToRight The user must pull from left to right to refresh. May be pushed to a future phase.
TopToBottom The user must pull from top to bottom to refresh.
RightToLeft The user must pull from right to left to refresh. May be pushed to a future phase.
BottomToTop The user must pull from bottom to top to refresh. May be pushed to a future phase.

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);
}

Properties

API Description
ItemsLayoutOrientation Specifies the direction in the which the CollectionView expands as items are added.

ListItemsLayout

public class ListItemsLayout : ItemsLayout
{
	public ListItemsLayout(ItemsLayoutOrientation orientation) : base (orientation);
	
	public static readonly BindableProperty SnapPointsAlignmentProperty;
	public SnapPointsAlignment SnapPointsAlignment { get; set; }

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

	public static readonly IItemsLayout VerticalList = new ListItemsLayout(ItemsLayoutOrientation.Vertical); 
	public static readonly IItemsLayout HorizontalList = new ListItemsLayout(ItemsLayoutOrientation.Horizontal); 
}

Properties

API Description
SnapPointsType Specifies the behavior of snap points when scrolling the list.
SnapPointsAlignment Specifies how snap points are aligned with items in the list.

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 FooterProperty;
    public object Footer { get; set; }

    public static readonly BindableProperty FooterTemplateProperty;
    public DataTemplate FooterTemplate { 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 SelectedItemsProperty;
    public IList<object> SelectedItems { get; set; }

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

@pauldipietro pauldipietro added this to New in Triage Jun 27, 2018

@hartez hartez self-assigned this Jun 27, 2018

@dhaligas

This comment has been minimized.

Copy link

dhaligas commented Jun 27, 2018

How about Scrolling event to support parallax headers

@hartez hartez removed this from New in Triage Jun 27, 2018

@dansiegel

This comment has been minimized.

Copy link

dansiegel commented Jun 28, 2018

There's a lot to love about this.

FontIconSource

For the FontIconSource though we really do need a way to not use the Unicode value (if we don't want to). If you're looking at a FontIconSource with a value f370 you have no freaking clue what that is supposed to be.

On the other hand, if I can pick and choose what Icon Font's I'm using and give Forms a way to convert fa-app-store-ios to it's unicode equivalent of f370, now I can at a glance know that the icon I'm using is from Font Awesome and it's the iOS App Store icon. By no means am I saying that Forms should have that understanding for every available font baked in (or any for that matter), that can come from either some public gists or be added to the Community Toolkit, etc. Either way we do need a way to use meaningful names.

Lastly I would hope we could get a baked in XAML Extension for this so you might have something like:

<Image Source="{FontIcon fa-app-store-ios,Color={StaticResource primaryColor}}" />

Menus

As I was discussing with @davidortinau earlier, it would be particularly ideal if we can mix and match on the Context Menus to achieve the appropriate results. Some good context on this would be to think of an email client in which you may get one menu when you swipe left and another when you swipe right. Another scenario I would love to see supported would be the cell menu approach like shown in the docs for the iOS CollectionView.

Events

I think it's safe to say I'm biased, but a very large portion of the community is following an MVVM design pattern. Having the SelectionChanged & RemainingItemsThresholdReached events are both important to the API, but it would be fantastic if we could find a way to support these OOB with a ViewModel's version of an event handler... an ICommand.

Since RemainingItemsThresholdReached just sends a plain old EventArgs I think we can safely assume passing null to the command should be fine since we just need to execute the command. For the SelectionChanged, I would tend to think it would be more vital to know the new items, vs the old items so simply passing the new items OOB to a SelectionChangedCommand should be fine. This may not hit every use case, but it at least provides a minimum amount of overhead that a developer needs to add with some EventToCommandBehavior.

@boguslawblonski

This comment has been minimized.

Copy link

boguslawblonski commented Jun 28, 2018

Perfect sample that kills current listview is making Calendar control where:
-days (cells) can have many styles :
as promotion day, birthday, disabled / no sale day/ bank holiday ...
embedded icons with different custom fonts in it, cell have shadows if selected, other style when in r
rage, other when at begin and end of range
-selecting date rages is possible
-month header and customized header style
-month headers have buttons that are allowing some special view/actions/display on current month

@gmwilhelm

This comment has been minimized.

Copy link

gmwilhelm commented Jun 28, 2018

How about also adding a GroupFooterTemplate?

@VincentH-Net

This comment has been minimized.

Copy link

VincentH-Net commented Jun 28, 2018

This is so much needed in Forms :-) CollectionView would eliminate expensive workarounds, solve limitations and probably improve performance in just about every Forms app I made in the past 5 years. Exactly the right approach too - leverage and expose the power of the native platforms.

If there was only ONE enhancement I could request for Forms, this would be it.
(actually, I did that in 2017 in response to @davidortinau)

@cyrilcathala

This comment has been minimized.

Copy link

cyrilcathala commented Jun 28, 2018

This is truly awesome and long awaited! The API is clean and straightforward (thanks for renaming HasUnevenRows !).

Scrolling events are very much needed, as suggested by @dhaligas. For parallax animations but also to hide floating action button while scrolling for example.

FontIconSource is a good idea. It might be great to provide a default list of icons, with unicode mapped for every platform (similar to iOS or UWP).

@alexhardwicke

This comment has been minimized.

Copy link
Contributor

alexhardwicke commented Jun 28, 2018

This sounds like exactly what's needed. The API looks much cleaner, having multiple views/containers structured this way should help keep the code cleaner and simpler.

My comments so far:

@dansiegel A command when the Remaining Items Threshold is met would be welcome. Not sure it's needed for SelectedItems though - everything you say (notified of changes, what has changed, and even why) is handled automatically as long as SelectedItems is an ObservableCollection (or anything that implements INotifyCollectionChanged). I'd rather have as little code as possible in CollectionView to keep complexity down.

Scrolling: I completely agree about the scrolling event. We can get it at the moment for Android, but for iOS you have to wrap the UITableViewSource and hijack the Scrolled method.

Most important of all for me would be extensibility. Please don't use internal classes for adapters and TableViewSources, and please in the native renderers expose "CreateAdapter" and "CreateViewSource" methods (and similar for other platforms) where we can create our own type if we want to. We have a custom ListAdapter for drag and drop support on Android, and as mentioned before, a custom UITableViewSources for both scrolling and drag and drop. It's not code I like though because there's wrapping of internal classes etc.

This doesn't just apply to adapters/viewsources. In general in the new API, please add points where we can extend behaviour if we need to.

@krdmllr

This comment has been minimized.

Copy link

krdmllr commented Jun 28, 2018

I think this is a really great and important feature!
iOS is already using the UICollectionView name, that might lead to confusion and incorrect google results when searching for CollectionView. Is this a problem?

@GalaxiaGuy

This comment has been minimized.

Copy link

GalaxiaGuy commented Jun 28, 2018

Just to get it out of the way, I love basically everything about CollectionView spec. It might be worth splitting some of the things out into separate issues though, since I have thoughts on them, as I'm sure many others do and I can see the conversations getting hard to follow. For example:

This could be added later, but something like:

public class MultipleFontIconsSource : ImageSource
{
    public List<FontIconSource> Icons { get; set; }
}

would be useful, to support stacking icons: https://fontawesome.com/how-to-use/on-the-web/styling/stacking-icons

@andreinitescu

This comment has been minimized.

Copy link
Contributor

andreinitescu commented Jun 28, 2018

My feedback:

  1. I'd not put the item selection capabilities in the CollectionView class. Instead, I'd add an abstract base class ListCollectionView derived from CollectionView which has specific capabilities:
public class CollectionView : View
{
   // Common capabilities related to a view capable of displaying a list of items
    public static readonly BindableProperty ItemsLayoutProperty;
    public IItemsLayout ItemsLayout { get; set; }

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

public class ListCollectionView : CollectionView
{
     // Item selection capabilities and other capabilities 

    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 SelectedItemsProperty;
    public IList<object> SelectedItems { get; set; }
}

My idea is to have a CollectionView similar to the ItemsControl in Windows XAML. The ItemsControl takes a Panel instance which positions the items. In Forms, we'll have CollectionView and IItemsLayout.
By default, if no IItemLayout is given, the default layout could a stack layout. This way we could basically have ItemsSource combined with StackPanel from Windows XAML, or in other words, a bindable StackLayout !

Beside item selection, there are other capabilities which I'd not put in the CollectionView, I'd add these to the ListCollectionView. For example, the CollectionView should not have scrolling. This way it will cover this thread

  1. I'd rename IItemsLayout interface to ICollectionViewLayout. The framework already has Layout and ItemsView. It would be a longer name, but the ICollectionViewLayout is a strong indication what it's for, it's specific to the CollectionView, not some general way of displaying items.

  2. I'd rename ItemsLayout class to OrientedCollectionViewLayout. It's a strong indication what the abstract base class is really for, all the derived classes layout the items in an oriented way. Moreover, using "Oriented" in the name gives the option in the future to add an abstract base class called just "CollectionViewLayout". if some new common functionality is required for all collection view layout classes, not just ones which have orientation.

  3. Removing ViewCell and just use DataTemplate is very welcome, it improves reusability.

@andreinitescu

This comment has been minimized.

Copy link
Contributor

andreinitescu commented Jun 28, 2018

  1. Can you share more details about RefreshView?
    I assume RefreshView somehow should map to UIRefreshControl on iOS? How will it work on Android and UWP?
@andreinitescu

This comment has been minimized.

Copy link
Contributor

andreinitescu commented Jun 28, 2018

In case anyone is interested, I made some updates to my first comment above.

@bmacombe

This comment has been minimized.

Copy link
Contributor

bmacombe commented Jun 28, 2018

I agree with @andreinitescu that it would be good to have a way to have a non-scrolling collection. I'm neutral on if that means breaking it into multiple classes or possibly having something like a ScrollMode property. I'll defer to those with a lot more API design experience than myself on that. But it is fairly trivial to implement your own StackLayout based item list with a DataTemplate or template selector, so maybe it's not needed.

While I like the idea of controlling the scroll function, I'm also not coming up with any compelling use cases for a non-scrolling "Collection" that includes all the advanced features of ListView or the proposed CollectionView off the top of my head. My use case for a non scrolling list has always been solved by the trivial item list. My normal reason for not using a ListView is because I don't want selection or I want to horizontally layout the list. Both of those cases appeared to be covered by the proposed spec. I would be interested in any use examples anyone else has for no scroll.

@andreinitescu

This comment has been minimized.

Copy link
Contributor

andreinitescu commented Jun 28, 2018

@bmacombe I agree with you that it's "simple" to implement a bindable StackLayout, but that's not the point I think. It's a very common functionality, why not have it in the framework? And with some planning, I think it can be done. Like I already said above, it can be solved by just spreading the functionality in two classes, a base class (similar to ItemsControl in Windows XAML) and CollectionView (ListView). Ideally there should be a Selector class in the hierarchy (see https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.primitives.selector)

@adammeaney

This comment has been minimized.

Copy link
Contributor

adammeaney commented Jun 28, 2018

I would say the trivial reason to have a non-scrolling option is that some pages need to have a ScrollView for the whole page, and a list of items.

If you do that, right now it is not recommended to put a ListView in a ScrollView. A bindable CollectionView that you could put in there, that still potentially has all the features of a ListView, is something I have had a need for multiple times.

I wrote the same BindableStack thing that everyone else does, but a baked in one would be much nicer to work with.

@bmacombe

This comment has been minimized.

Copy link
Contributor

bmacombe commented Jun 28, 2018

@adammeaney That makes sense and I've done similar now that I think about it.

@bmacombe

This comment has been minimized.

Copy link
Contributor

bmacombe commented Jun 28, 2018

@andreinitescu I don't disagree it would be great to have it built in. I think everyone is on the same page for what is needed, just how to best do it. I'm ok with either approach as long as there is a "no scroll" option of some type.

In my own frameworks I always go back and forth in build lots of features into one control or have lots of similar classes with incremental functions. I'll be good with however the Forms team decides to implement it the best, especially considering the underlying platform implementations.

@andreinitescu

This comment has been minimized.

Copy link
Contributor

andreinitescu commented Jun 28, 2018

@bmacombe With some planning, I'm hoping we can get two birds with one stone.
For example, an instance of the CollectionView class working in the way I mentioned, if it has no IItemLayout set, it could just be mapped by the renderer mechanism to a native UIStackView on iOS or StackPanel on UWP or LinearLayout on Android. Or, to be easier to implement, it could just use the existing StackLayout to arrange views.

@andreinitescu

This comment has been minimized.

Copy link
Contributor

andreinitescu commented Jun 28, 2018

My impression was that a new ListView should aim both fix challenges with existing ListView but also provide a solution to the "RepeatableBinder" thread here

We want to see where ListView2 lands before pushing this spec forward. It contains many of the same needed components and it would be best to avoid duplicating them.

I don't see how putting all the functionality in the CollectionView like the spec here suggests would do the job right.

@bmacombe

This comment has been minimized.

Copy link
Contributor

bmacombe commented Jun 28, 2018

#2680 Could be added to this spec? With regards to ScrollBar enablement.

@hartez

This comment has been minimized.

Copy link
Member

hartez commented Jun 28, 2018

@andreinitescu

Can you share more details about RefreshView?
I assume RefreshView somehow should map to UIRefreshControl on iOS? How will it work on Android and UWP?

On UWP, we'll likely be using RefreshContainer. On Android, probably SwipeRefreshLayout.

@hartez

This comment has been minimized.

Copy link
Member

hartez commented Jun 28, 2018

@krdmllr

iOS is already using the UICollectionView name, that might lead to confusion and incorrect google results when searching for CollectionView. Is this a problem?

It's a problem, but not a new one. We have the same issue with GestureRecognizer, ListView, Grid, and many other things in Forms. We struggled with this a bit internally; there are only so many possible names for "a bunch of items on a screen".

"CollectionView" doesn't conflict with anything we currently have in Forms, it doesn't conflict with any UWP control names (the way "ListView" does), and it's not exactly like UICollectionView (because of the "UI" prefix). It's not 100% ideal for search purposes, but we feel like it's the best expression of the intent of the control.

That said, if someone proposes an alternate name that we immediately fall in love with, I'm happy to do the search and replace.

(I wanted to name it "ListView2ColonRevengeOfTheListView", but @davidortinau shot me down :) )

@hartez

This comment has been minimized.

Copy link
Member

hartez commented Jun 28, 2018

@dansiegel

Some good context on this would be to think of an email client in which you may get one menu when you swipe left and another when you swipe right.

Take a look at the SwipeView spec - that's precisely what we're aiming for.

@hartez

This comment has been minimized.

Copy link
Member

hartez commented Jun 28, 2018

@GalaxiaGuy

support stacking icons

That's a great point, we should support that if possible. I know it works with the built-in UWP glyphs; I'd have to do a little research to make sure that type of stacking would work on the other platforms (I suspect the answer is "yes").

@vd3d

This comment has been minimized.

Copy link

vd3d commented Dec 10, 2018

And, will it be possible to create irregular and dynamic layouts, like PInterest by example:

You see, every image has its own height :-)

Not sure that such kind of layout can be supported ?

image

@migueldeicaza

This comment has been minimized.

Copy link
Member

migueldeicaza commented Dec 10, 2018

Update: from the list of name proposals, the closest that we have come up with is "ItemsView", this is to avoid using the term "CollectionView" that gives folks the impression that this is as complete as the iOS "UICollectionView"

@uvirra

This comment has been minimized.

Copy link

uvirra commented Dec 13, 2018

Hi, I'm still new to Xamarin, but have been working with ListView by using DataTemplateSelector.
When I tried to test out using CollectionView with DataTemplateSelector , it throws error

Unhandled Exception:

System.InvalidOperationException: LoadTemplate should not be null occurred

Is there a known bug related to DataTemplateSelector?
I could be implementing it wrongly. Is there any example on using DataTemplateSelector with CollectionView?

@adrianknight89

This comment has been minimized.

Copy link
Contributor

adrianknight89 commented Dec 26, 2018

@hartez @davidortinau any reason why KeepItemsInView is not named KeepFirstItemInView to align with KeepLastItemInView? The current name is plural (i.e. suggests multiple items) whereas the description says it only keeps the first visible item in the viewport.

Also, is there a way you could expose an API to get the list of views currently in the viewport? The other night, I was working on creating a video view to be embedded in a ListView cell and autoplayed/paused based on viewport visibility, but it was near impossible with the current ItemAppearing and ItemDisappearing events especially when they kept firing in all kinds of unexpected ways with cell recycling and/or fast scrolling. Really looking forward to CollectionView supporting this kind of functionality in a more reliable way.

autoplayvideos demo

Another thing that would be great to have is the ability to create a dependent, minimized view that is detached from the parent while the user has the luxury of scrolling on the current feed. I've seen this on YouTube as we're now able to continue watching the current video while having the ability to continue browsing around. I'm not sure if this is something CollectionView can or should support btw, but it's something to think about.

youtube1-5abab8210e23d9003787855d

@krdmllr

This comment has been minimized.

Copy link

krdmllr commented Dec 31, 2018

So i played around with the current nightly build a bit (4.0.1.43780-nightly) which resolved some resizing issues compared to the current preview build.

A suggestion i have would is to add something like RowSpacing/ColumnSpacing or GridSpacing to grid layouts since that makes it much easier to space your items correctly without using margin/padding on the individual items.
Another thing would be to add Padding or maybe EdgeOffset to the CollectionView or Layout to avoid using Margin, which cuts of the items like shown here:
android-margin-image
Margin also crops the "drag" animation on Android like you can see here:
android-margin

@hartez

This comment has been minimized.

Copy link
Member

hartez commented Dec 31, 2018

A suggestion i have would is to add something like RowSpacing/ColumnSpacing or GridSpacing to grid layouts since that makes it much easier to space your items correctly without using margin/padding on the individual items.

Agreed, and that's on our radar - see #4681.

@samhouts samhouts added this to In progress in Sprint 147 Jan 9, 2019

@SagarPanwala

This comment has been minimized.

Copy link

SagarPanwala commented Jan 9, 2019

Update: from the list of name proposals, the closest that we have come up with is "ItemsView", this is to avoid using the term "CollectionView" that gives folks the impression that this is as complete as the iOS "UICollectionView"

what about "FloatView" ?

@samhouts samhouts added this to In Progress in vNext (Target 3.5.0) Jan 11, 2019

@samhouts samhouts moved this from In progress to Done in Sprint 147 Jan 11, 2019

@samhouts samhouts moved this from In Progress to Done in vNext (Target 3.5.0) Jan 11, 2019

@hartez hartez referenced this issue Jan 14, 2019

Open

[iOS] CollectionView single selection #4985

2 of 3 tasks complete
@andreinitescu

This comment has been minimized.

Copy link
Contributor

andreinitescu commented Jan 14, 2019

Beside ItemAppearing/Disappearing events mentioned above, any plans for ways to get view for an item:
View GetView(object item)
If item is not materialized it should return null I guess.

@shwanton

This comment has been minimized.

Copy link

shwanton commented Jan 15, 2019

I'm using a Vertical List and there seems to be inconstancies in how it's rendered on iOS vs Android.

On iOS, with the label will take up 100% of the width.
screenshot 2019-01-15 13 56 01

On Android, it will only take up it's content width.
screenshot 2019-01-15 13 57 59

I could only get it to layout correctly with a static width definition.
Is there an Android workaround that respects the Grid Width="Auto" property?

@hartez

This comment has been minimized.

Copy link
Member

hartez commented Jan 15, 2019

I'm using a Vertical List and there seems to be inconstancies in how it's rendered on iOS vs Android.

@shwanton Please open a separate issue for this bug, and I'll take a look.

@krdmllr

This comment has been minimized.

Copy link

krdmllr commented Jan 17, 2019

@shwanton i had similiar issues which got fixed when i switched to the nightly feed

@shwanton

This comment has been minimized.

Copy link

shwanton commented Jan 18, 2019

@krdmllr I can confirm, it has been fixed in the latest build. Thank you.

@samhouts samhouts added this to To do in Sprint 148 Jan 18, 2019

@samhouts samhouts moved this from To do to Done in Sprint 148 Jan 22, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment