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

[Spec] CarouselView #4996

@rmarinho

Description

@rmarinho

CarouselView

CarouselView is a concept used in a lot of mobile applications. We already had a CarouselPage since the initial XF version, but that only supported Pages and didn't support virtualisation. Based on the new CollectionView we are introducing CarouselView that allows to create performant CarouselView's that could be snap to position, swiped and provide position and paging info.

We are looking at other existing versions try to have a full feature set to make our users happy, this will be allowing them to specify scroll direction, position, spacing between items , how the pager dot works and feels.

CarouselView will always have all items sized to fit the viewport and taking in account the visible items on each side the user specified, and all items will have the same size.

CarouselView must easily integrate with IndicatorsView #6577

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

Related issues

API

CarouselView

public class CarouselView :  ItemsView
{
	public const string CurrentItemVisualState = "CurrentItem";
	public const string NextItemVisualState = "NextItem";
	public const string PreviousItemVisualState = "PreviousItem";
	public const string VisibleItemVisualState = "VisibleItem";
	public const string DefaultItemVisualState = "DefaultItem";

	public static readonly BindableProperty PeekAreaInsetsProperty = BindableProperty.Create(nameof(PeekAreaInsets), typeof(Thickness), typeof(CarouselView), default(Thickness));

	public Thickness PeekAreaInsets
	{
		get { return (Thickness)GetValue(PeekAreaInsetsProperty); }
		set { SetValue(PeekAreaInsetsProperty, value); }
	}

       static readonly BindablePropertyKey IsScrollingPropertyKey = BindableProperty.CreateReadOnly(nameof(IsScrolling), typeof(bool), typeof(CarouselView), false);

	public static readonly BindableProperty IsScrollingProperty = IsScrollingPropertyKey.BindableProperty;

	public bool IsScrolling => (bool)GetValue(IsScrollingProperty);

        public static readonly BindablePropertyKey IsDraggingPropertyKey = BindableProperty.CreateReadOnly(nameof(IsDragging), typeof(bool), typeof(CarouselView), false);

	public static readonly BindableProperty IsDraggingProperty = IsDraggingPropertyKey.BindableProperty;

	public bool IsDragging => (bool)GetValue(IsDraggingProperty);

	public static readonly BindableProperty IsBounceEnabledProperty =
			BindableProperty.Create(nameof(IsBounceEnabled), typeof(bool), typeof(CarouselView), true);

	public bool IsBounceEnabled
	{
		get { return (bool)GetValue(IsBounceEnabledProperty); }
		set { SetValue(IsBounceEnabledProperty, value); }
	}

	public static readonly BindableProperty IsLoopEnabledProperty = BindableProperty.Create(nameof(IsLoopEnabled), typeof(bool), typeof(CarouselView), false);

	public bool IsLoopEnabled
	{
		get { return (bool)GetValue(IsLoopEnabledProperty); }
		set { SetValue(IsLoopEnabledProperty, value); }
	}

	public static readonly BindableProperty IsSwipeEnabledProperty = BindableProperty.Create(nameof(IsSwipeEnabled), typeof(bool), typeof(CarouselView), true);

	public bool IsSwipeEnabled
	{
		get { return (bool)GetValue(IsSwipeEnabledProperty); }
		set { SetValue(IsSwipeEnabledProperty, value); }
	}

	public static readonly BindableProperty IsScrollAnimatedProperty =
		BindableProperty.Create(nameof(IsScrollAnimated), typeof(bool), typeof(CarouselView), true);

	public bool IsScrollAnimated
	{
		get { return (bool)GetValue(IsScrollAnimatedProperty); }
		set { SetValue(IsScrollAnimatedProperty, value); }
	}

	public static readonly BindableProperty NumberOfSideItemsProperty =
			BindableProperty.Create(nameof(NumberOfSideItems), typeof(int), typeof(CarouselView), 0);

	public int NumberOfSideItems
	{
		get { return (int)GetValue(NumberOfSideItemsProperty); }
		set { SetValue(NumberOfSideItemsProperty, value); }
	}

	public static readonly BindableProperty PositionProperty =
		BindableProperty.Create(nameof(Position), typeof(int), typeof(CarouselView), default(int), BindingMode.TwoWay,
			propertyChanged: PositionPropertyChanged);

	public static readonly BindableProperty PositionChangedCommandProperty =
			BindableProperty.Create(nameof(PositionChangedCommand), typeof(ICommand), typeof(CarouselView));

	public static readonly BindableProperty PositionChangedCommandParameterProperty =
			BindableProperty.Create(nameof(PositionChangedCommandParameter), typeof(object),
				typeof(CarouselView));

	public int Position
	{
		get => (int)GetValue(PositionProperty);
		set => SetValue(PositionProperty, value);
	}

	public ICommand PositionChangedCommand
	{
		get => (ICommand)GetValue(PositionChangedCommandProperty);
		set => SetValue(PositionChangedCommandProperty, value);
	}

	public object PositionChangedCommandParameter
	{
		get => GetValue(PositionChangedCommandParameterProperty);
		set => SetValue(PositionChangedCommandParameterProperty, value);
	}
        
        public static readonly BindableProperty CurrentItemProperty =
		BindableProperty.Create(nameof(CurrentItem), typeof(object), typeof(CarouselView),  default(object), BindingMode.TwoWay, propertyChanged: CurrentItemPropertyChanged);

	public static readonly BindableProperty CurrentItemChangedCommandProperty =
			BindableProperty.Create(nameof(CurrentItemChangedCommand), typeof(ICommand), typeof(CarouselView));

	public static readonly BindableProperty CurrentItemChangedCommandParameterProperty =
			BindableProperty.Create(nameof(CurrentItemChangedCommandParameter), typeof(object), typeof(CarouselView));

	public object CurrentItem
	{
		get => GetValue(CurrentItemProperty);
		set => SetValue(CurrentItemProperty, value);
	}

	public ICommand CurrentItemChangedCommand
	{
		get => (ICommand)GetValue(CurrentItemChangedCommandProperty);
		set => SetValue(CurrentItemChangedCommandProperty, value);
	}

	public object CurrentItemChangedCommandParameter
	{
		get => GetValue(CurrentItemChangedCommandParameterProperty);
		set => SetValue(CurrentItemChangedCommandParameterProperty, value);
	}

	public event EventHandler<CurrentItemChangedEventArgs> CurrentItemChanged;
	public event EventHandler<PositionChangedEventArgs> PositionChanged;
        public event EventHandler<ScrolledEventArgs> Scrolled;

        //Methods to be called by the platforms
        void SendScrolled(double value, ScrollDirection direction);
	void SetCurrentItem(object item);
	void SetIsScrolling(bool value);
	void SetIsDragging(bool value);
}

Properties

API Description
CurrentItem Current item visible and centered on the CarouselView
Position Current position of the current item in the collection, when changed raises PositionChanged should also sync with CurrentItem
IsLoopEnabled Specify if when arriving to last source position we should show next the first position
IsScrollAnimated Specify if should see animation when changing position or selected item
IsSwipeEnabled If swipe left and right are enable to switch times
NumberOfSideItems Number of visible items on each side of the current item , defaults to 0
IsBounceEnabled If the CarouselView bounces when reaching the end of the list
IsDragging Readonly that is True when the user is interacting and dragging the carousel
IsScrolling Readonly that is True when the CarouselView is being scrolled by changing the position value
PeakAreaInsets Set the viewport insets, this will allow to "peek" items
VisualStateManager States Description
CurrentItemVisualState VisualState set for when a view is set as CurrentItem and visible on screen
NextItemVisualState VisualState set for when a view is visible on screen and next the current item
PreviousItemVisualState VisualState when a view is on screen and before the current item
VisibleItemVisualState VisualState when a view is visible on screen but not Next,Previous or Current state
DefaultItemVisualState VisualState default when a view is created initially

Events

API Description
Scrolled Fires when the carousel is scrolling
PositionChanged [API documentation/description]
CurrentItemChanged [API documentation/description]

CurrentItemChangedEventArgs

public class CurrentItemChangedEventArgs : EventArgs
{
	public object PreviousItem { get; }
	public object CurrentItem { get; }

	internal CurrentItemChangedEventArgs(object previousItem, object currentItem)
	{
		PreviousItem = previousItem;
		CurrentItem = currentItem;
	}
}

Properties

API Description
PreviousItem Get's the previous item.
CurrentItem Get's the current item.

Extend ScrolledEventArgs

//extend existing ScrolledEvent args with new Ctor and DeltaX and DeltaY properties 
public class ScrolledEventArgs : EventArgs
{
	public ScrolledEventArgs(double scrollX, double scrollY, double deltaX, double  deltaY)
	{
		ScrollX = scrollX;
		ScrollY = scrollY;
		DeltaX = deltaX;
		DeltaY = deltaY;
	}
	public double ScrollX { get; private set; }
	public double ScrollY { get; private set; }
        public double DeltaX { get; private set; }
	public double DeltaY { get; private set; }
}

Properties

API Description
ScrollX Get's the current scroll x offset
DeltaX Get's the current delta x from the last scroll event
ScrollY Get's the current scroll y offset
DeltaY Get's the current delta y from the last scroll event

PositionChangedEventArgs

public class PositionChangedEventArgs : EventArgs
{
	public int PreviousPosition { get; }
	public int CurrentPosition { get; }
}

Properties

API Description
PreviousPosition Gets the previous position before the position changed.
CurrentPosition Gets the current position

3D and Custom layouts

Users can achieve other layouts in the future we can provide a CoverFlowItemsLayout and a StackedItemsLayout

public class CoverFlowItemsLayout : ItemsLayout
{
	public CoverFlowItemsLayout(ItemsLayoutOrientation orientation) : base(orientation)
	{
	}
	public static readonly BindableProperty ViewPointOffSetProperty =
		BindableProperty.Create(nameof(SnapPointsAlignment), typeof(double), typeof(CoverFlowItemsLayout), 2.2);

	public double ViewPointOffSet
	{
		get => (double)GetValue(ViewPointOffSetProperty);
		set => SetValue(ViewPointOffSetProperty, value);
	}

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

	public static readonly IItemsLayout Vertical = new StackedItemsLayout(ItemsLayoutOrientation.Vertical);
	public static readonly IItemsLayout Horizontal = new StackedItemsLayout(ItemsLayoutOrientation.Horizontal);
}

Scenarios

var carouselView = new CarouselView
{
	ItemsSource = itemsSource,
	ItemsLayout = itemsLayout,
	ItemTemplate = itemTemplate,
	Position = 2,
	NumberofSideItems = 1
};

Backward Compatibility

We should make sure there's no conflicts with old versions of CarouselView created by us or other.

Difficulty :

High

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions