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

Xamarin.Forms.CollectionView Spec #3172

Closed
hartez opened this issue Jun 27, 2018 · 178 comments · Fixed by #5364
Closed

Xamarin.Forms.CollectionView Spec #3172

hartez opened this issue Jun 27, 2018 · 178 comments · Fixed by #5364

Comments

@hartez
Copy link
Contributor

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

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.

@pauldipietro pauldipietro added this to New in Triage Jun 27, 2018
@hartez hartez self-assigned this Jun 27, 2018
@dhaligas
Copy link

How about Scrolling event to support parallax headers

@hartez hartez removed this from New in Triage Jun 27, 2018
@dansiegel
Copy link
Contributor

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
Copy link

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
Copy link

How about also adding a GroupFooterTemplate?

@VincentH-Net
Copy link
Contributor

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
Copy link

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
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
Copy link
Contributor

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
Copy link
Contributor

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
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
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
Copy link
Contributor

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

@bmacombe
Copy link
Contributor

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
Copy link
Contributor

@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
Copy link
Contributor

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
Copy link
Contributor

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

@bmacombe
Copy link
Contributor

@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
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
Copy link
Contributor

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
Copy link
Contributor

bmacombe commented Jun 28, 2018

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

@hartez
Copy link
Contributor Author

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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

@rafiwardak2003
Copy link

CollectionView seems to be bugged on Android when using IsGrouped.

CollectionView.GroupHeaderTemplate is not rendered at all. On iOS it works fine. You could just use the grouping example from the Microsoft site to test it out.

    public class AnimalGroup : List<Animal>
    {
        public string Name { get; private set; }

        public AnimalGroup(string name, List<Animal> animals) : base(animals)
        {
            Name = name;
        }
    }

    public class Animal
    {
        public string Name { get; set; }
        public string Location { get; set; }
        public string Details { get; set; }
        public string ImageUrl { get; set; }
    }

    public List<AnimalGroup> Animals { get; private set; } = new List<AnimalGroup>();

    Animals.Add(new AnimalGroup("Bears", new List<Animal>
        {
            new Animal
            {
                Name = "American Black Bear",
                Location = "North America",
                Details = "Details about the bear go here.",
                ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/0/08/01_Schwarzbär.jpg"
            },
            new Animal
            {
                Name = "Asian Black Bear",
                Location = "Asia",
                Details = "Details about the bear go here.",
                ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b7/Ursus_thibetanus_3_%28Wroclaw_zoo%29.JPG/180px-Ursus_thibetanus_3_%28Wroclaw_zoo%29.JPG"
            },
        }));
<CollectionView ItemsSource="{Binding Animals}"
                        IsGrouped="true">
            <CollectionView.GroupHeaderTemplate>
                <DataTemplate>
                    <Label Text="Header"
                           BackgroundColor="LightGray"
                           FontSize="Large"
                           FontAttributes="Bold" />
                </DataTemplate>
            </CollectionView.GroupHeaderTemplate>
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <Grid Padding="10">
                        <Image Grid.RowSpan="2"
                               Source="{Binding ImageUrl}"
                               Aspect="AspectFill"
                               HeightRequest="60"
                               WidthRequest="60" />
                        <Label Grid.Column="1"
                               Text="{Binding Name}"
                               FontAttributes="Bold" />
                        <Label Grid.Row="1"
                               Grid.Column="1"
                               Text="Child"
                               FontAttributes="Italic"
                               VerticalOptions="End" />
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>

https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/grouping

@Dresel
Copy link
Contributor

Dresel commented Oct 3, 2019

Important

Grouping data with CollectionView is currently only supported on iOS.

@rafiwardak2003
Copy link

Ops, I missed that part. Thanks.

For those of us who want to display a grouped list on both iOS and Android, but want to avoid ListView, is there anything else that we can use that’s as flexible as the CollectionView for now? Or are we stuck with ListView?

@cabal95
Copy link

cabal95 commented Oct 4, 2019

I hope this is the right place to ask. I see in the original posted spec there are properties for IsHeaderSticky and IsFooterSticky, but I don't see any further mention of them anywhere in the discussion or the implemented source code. Are those still on the roadmap for this view?

@IosDeveloperHarsh
Copy link

In collectionview we need ItemAppeared, ITemDisappeared.
is there any way to achieve ?

@adrianknight89
Copy link
Contributor

@IosDeveloperHarsh The Scrolled event provides a lot of useful information in the event args. Look at first and last visible item indexes there.

@adrianknight89
Copy link
Contributor

@rafiwardak2003 Gallery samples with CollectionView groups work on Android as of today though I haven't run the sample you've provided. Which Nuget were you using? Try with the latest pre-release or nightly.

@adrianknight89
Copy link
Contributor

@cabal95 I'm not qualified to answer this, but if I had to guess they will eventually be implemented. Perhaps, we need to have an active issue to track this. I do know that the initial header/footer work excluded sticky stuff.

@samhouts samhouts added this to To do in Sprint 160 via automation Oct 7, 2019
@samhouts samhouts moved this from To do to In progress in Sprint 160 Oct 7, 2019
@dhindrik
Copy link
Contributor

dhindrik commented Oct 9, 2019

I think that SelectionChangedCommandParameter should be removed and instead we will have the selected item automatically set as a parameter.

If it is possible it would be nice to use the property like this

public ICommand => new Command<MySelectedItemModel>((item) => {}

But this should be ok as well:

public ICommand => new Command<object>((item) =>{ var selectedItem = item as MySelectedItemModel;}

@hartez
Copy link
Contributor Author

hartez commented Oct 9, 2019

I think that SelectionChangedCommandParameter should be removed and instead we will have the selected item automatically set as a parameter.

If it is possible it would be nice to use the property like this

public ICommand => new Command<MySelectedItemModel>((item) => {}

But this should be ok as well:

public ICommand => new Command<object>((item) =>{ var selectedItem = item as MySelectedItemModel;}

What if the user wants to use something other than the SelectedItem as the parameter to their Command?

@dhindrik
Copy link
Contributor

dhindrik commented Oct 9, 2019

I think that SelectionChangedCommandParameter should be removed and instead we will have the selected item automatically set as a parameter.
If it is possible it would be nice to use the property like this
public ICommand => new Command<MySelectedItemModel>((item) => {}
But this should be ok as well:
public ICommand => new Command<object>((item) =>{ var selectedItem = item as MySelectedItemModel;}

What if the user wants to use something other than the SelectedItem as the parameter to their Command?

You have a point, but I have hard to see a use case for that because you are setting the parameter on CollectionView level. I expected that it to be the selected item the first time I used it. But you maybe can set the selected item as default? If you use the event you will get so I think many developers expect it for the command as well.

@Nullstr1ng
Copy link

Nullstr1ng commented Oct 14, 2019

I'd like to also request for a feature that this CollectionView does not stretch up on the entire screen and just use the height depends on the number of item.

I am trying to have a multple lists in view
i.e
Purchases
<Items 1>
Deductions
<items 2>
Total
total
tot

but Items 1 and 2 (a CollectionView) just took too much height even if the I only have 3 or 4 items and there is a BIIIIIIIIIIIIIIIG gap below before I can see the Deductions section

I ended up making my own ItemsCollection control based on StackLayout

@hartez
Copy link
Contributor Author

hartez commented Oct 14, 2019

I'd like to also request for a feature that this CollectionView does not stretch up on the entire screen and just use the height depends on the number of item.
I ended up making my own ItemsCollection control based on StackLayout

I think what you are looking for may be Bindable Layouts.

@Nullstr1ng
Copy link

I'd like to also request for a feature that this CollectionView does not stretch up on the entire screen and just use the height depends on the number of item.
I ended up making my own ItemsCollection control based on StackLayout

I think what you are looking for may be Bindable Layouts.

Hey! Exactly what I need. Since when BindableLayouts existed?

felipebaltazar pushed a commit to felipebaltazar/Xamarin.Forms that referenced this issue Oct 16, 2019
* Move all the header/footer adjustment to IItemsViewSource
fixes xamarin#7121
fixes xamarin#7102
partially implements xamarin#3172
fixes xamarin#7243

* Fix selection bugs introduced by header/footer on Android

* Implement grouping for CollectionView on Android

* Enable grouping tests for Android

* Naming and comment cleanup

* Update Xamarin.Forms.Platform.Android/CollectionView/ListSource.cs

Co-Authored-By: Gerald Versluis <gerald.versluis@microsoft.com>

* Update Xamarin.Forms.Platform.Android/CollectionView/ObservableGroupedSource.cs
felipebaltazar pushed a commit to felipebaltazar/Xamarin.Forms that referenced this issue Oct 16, 2019
…) partially implements xamarin#3172

* Finish ScrollTo implementations for CollectionView on UWP

* Fix NRE when attempting to scroll to index greater than source length
felipebaltazar pushed a commit to felipebaltazar/Xamarin.Forms that referenced this issue Oct 16, 2019
* Move all the header/footer adjustment to IItemsViewSource
fixes xamarin#7121
fixes xamarin#7102
partially implements xamarin#3172
fixes xamarin#7243

* Fix selection bugs introduced by header/footer on Android

* Implement grouping for CollectionView on Android

* Enable grouping tests for Android

* Naming and comment cleanup

* Update Xamarin.Forms.Platform.Android/CollectionView/ListSource.cs

Co-Authored-By: Gerald Versluis <gerald.versluis@microsoft.com>

* Update Xamarin.Forms.Platform.Android/CollectionView/ObservableGroupedSource.cs
felipebaltazar pushed a commit to felipebaltazar/Xamarin.Forms that referenced this issue Oct 16, 2019
…) partially implements xamarin#3172

* Finish ScrollTo implementations for CollectionView on UWP

* Fix NRE when attempting to scroll to index greater than source length
@samhouts
Copy link
Member

Thank you everyone for the discussion on this issue! If you have any additional feature requests or bug reports, please open new issues for each one. We are now closing this issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
a/collectionview blocker Issue blocks next stable release. Prioritize fixing and reviewing this issue. roadmap t/enhancement ➕
Projects
No open projects
Sprint 138
  
In progress
Sprint 139
  
In progress
Sprint 140
  
In progress
Sprint 141
  
In progress
Sprint 142
  
In progress
Sprint 143
  
In progress
Sprint 145
  
In progress
Sprint 146
  
In progress
Sprint 147
  
Done
Sprint 148
  
Done
Sprint 152
  
To be continued next sprint
Sprint 153
  
Continued in next sprint
Sprint 154
  
In progress
Sprint 155
  
Continued in next sprint
Sprint 156
  
Continued next sprint
Sprint 157
  
Continued in next sprint
Sprint 158
  
Continued in next sprint
Sprint 159
  
Continued in next sprint
Sprint 160
  
Done
v3.5.0
  
Done
v4.3.0
  
Done
Development

Successfully merging a pull request may close this issue.