diff --git a/Xamarin.Forms.ControlGallery.WindowsUniversal/Xamarin.Forms.ControlGallery.WindowsUniversal.csproj b/Xamarin.Forms.ControlGallery.WindowsUniversal/Xamarin.Forms.ControlGallery.WindowsUniversal.csproj index ab9c3ab2c..0af75d518 100644 --- a/Xamarin.Forms.ControlGallery.WindowsUniversal/Xamarin.Forms.ControlGallery.WindowsUniversal.csproj +++ b/Xamarin.Forms.ControlGallery.WindowsUniversal/Xamarin.Forms.ControlGallery.WindowsUniversal.csproj @@ -151,6 +151,7 @@ + diff --git a/Xamarin.Forms.ControlGallery.WindowsUniversal/cardBackground.png b/Xamarin.Forms.ControlGallery.WindowsUniversal/cardBackground.png new file mode 100644 index 000000000..b5b6d1fa4 Binary files /dev/null and b/Xamarin.Forms.ControlGallery.WindowsUniversal/cardBackground.png differ diff --git a/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselItemsGallery.cs b/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselItemsGallery.cs index e4419a2aa..a7d205d9f 100644 --- a/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselItemsGallery.cs +++ b/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselItemsGallery.cs @@ -10,9 +10,17 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi [Preserve(AllMembers = true)] public class CarouselItemsGallery : ContentPage { - public CarouselItemsGallery(bool empty, bool async, bool nativeIndicator) + CarouselItemsGalleryViewModel _viewModel; + bool _setPositionOnAppering; + public CarouselItemsGallery(bool startEmptyCollection = false, bool setCollectionWithAsync = false, + bool useNativeIndicators = false, bool setPositionOnConstructor = false, + bool setPositionOnAppearing = false, bool useScrollAnimated = true) { - var viewModel = new CarouselItemsGalleryViewModel(empty, async); + _viewModel = new CarouselItemsGalleryViewModel(startEmptyCollection, setCollectionWithAsync); + _setPositionOnAppering = setPositionOnAppearing; + + if (setPositionOnConstructor) + _viewModel.CarouselPosition = 3; Title = $"CarouselView (Indicators)"; @@ -39,14 +47,14 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi { ItemsLayout = itemsLayout, ItemTemplate = itemTemplate, - IsScrollAnimated = true, + IsScrollAnimated = useScrollAnimated, IsBounceEnabled = true, EmptyView = "This is the empty view", PeekAreaInsets = new Thickness(50), - BindingContext = viewModel }; - carouselView.SetBinding(CarouselView.ItemsSourceProperty, nameof(viewModel.Items)); + carouselView.SetBinding(CarouselView.ItemsSourceProperty, nameof(_viewModel.Items)); + carouselView.SetBinding(CarouselView.PositionProperty, nameof(_viewModel.CarouselPosition)); var absolute = new AbsoluteLayout(); absolute.Children.Add(carouselView, new Rectangle(0, 0, 1, 1), AbsoluteLayoutFlags.All); @@ -59,7 +67,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi IndicatorsShape = IndicatorShape.Square }; - if (!nativeIndicator) + if (!useNativeIndicators) { indicators.IndicatorTemplate = new DataTemplate(() => { @@ -92,12 +100,12 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi addItemButton.Clicked += (sender, e) => { - viewModel.Items.Add(new CarouselData + _viewModel.Items.Add(new CarouselData { Color = Color.Red, - Name = $"{viewModel.Items.Count + 1}" + Name = $"{_viewModel.Items.Count + 1}" }); - carouselView.Position = viewModel.Items.Count - 1; + _viewModel.CarouselPosition = _viewModel.Items.Count - 1; }; var removeItemButton = new Button @@ -107,11 +115,11 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi removeItemButton.Clicked += (sender, e) => { - if (viewModel.Items.Any()) - viewModel.Items.RemoveAt(viewModel.Items.Count - 1); + if (_viewModel.Items.Any()) + _viewModel.Items.RemoveAt(_viewModel.Items.Count - 1); - if (viewModel.Items.Count > 0) - carouselView.Position = viewModel.Items.Count - 1; + if (_viewModel.Items.Count > 0) + _viewModel.CarouselPosition = _viewModel.Items.Count - 1; }; var clearItemsButton = new Button @@ -121,7 +129,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi clearItemsButton.Clicked += (sender, e) => { - viewModel.Items.Clear(); + _viewModel.Items.Clear(); }; var lbl = new Label(); @@ -136,7 +144,15 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi grid.Children.Add(stacklayoutButtons, 0, 1); Content = grid; - BindingContext = viewModel; + BindingContext = _viewModel; + } + + protected override void OnAppearing() + { + if (_viewModel.CarouselPosition != 3) + _viewModel.CarouselPosition = 3; + + base.OnAppearing(); } internal DataTemplate GetCarouselTemplate() @@ -193,6 +209,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi public class CarouselItemsGalleryViewModel : BindableObject { ObservableCollection _items; + int _carouselPosition; public CarouselItemsGalleryViewModel(bool empty, bool async) { @@ -244,5 +261,15 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi OnPropertyChanged(nameof(Items)); } } + + public int CarouselPosition + { + get => _carouselPosition; + set + { + _carouselPosition = value; + OnPropertyChanged(nameof(CarouselPosition)); + } + } } } diff --git a/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselViewGallery.cs b/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselViewGallery.cs index 488b9e3b0..8d529191a 100644 --- a/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselViewGallery.cs +++ b/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselViewGallery.cs @@ -34,11 +34,11 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi GalleryBuilder.NavButton("CarouselView (XAML, Horizontal)", () => new CarouselXamlGallery(), Navigation), GalleryBuilder.NavButton("CarouselView (Indicators Forms)", () => - new CarouselItemsGallery(false,false,false), Navigation), + new CarouselItemsGallery(), Navigation), GalleryBuilder.NavButton("CarouselView (Indicators Default (Native))", () => - new CarouselItemsGallery(false,false,true), Navigation), + new CarouselItemsGallery(useNativeIndicators: true), Navigation), GalleryBuilder.NavButton("CarouselView Async", () => - new CarouselItemsGallery(false,true,true), Navigation), + new CarouselItemsGallery(setCollectionWithAsync:true, useNativeIndicators: true), Navigation), GalleryBuilder.NavButton("CarouselView Snap", () => new CarouselSnapGallery(), Navigation), GalleryBuilder.NavButton("ObservableCollection and CarouselView", () => @@ -46,7 +46,13 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi GalleryBuilder.NavButton("CarouselView EmptyView", () => new EmptyCarouselGallery(), Navigation), GalleryBuilder.NavButton("IndicatorView", () => - new IndicatorCodeGallery(), Navigation) + new IndicatorCodeGallery(), Navigation), + GalleryBuilder.NavButton("CarouselView SetPosition Ctor", () => + new CarouselItemsGallery(useNativeIndicators: true, setPositionOnConstructor: true), Navigation), + GalleryBuilder.NavButton("CarouselView SetPosition Appearing", () => + new CarouselItemsGallery(useNativeIndicators: true, setPositionOnAppearing: true), Navigation), + GalleryBuilder.NavButton("CarouselView SetPosition Ctor No Animation", () => + new CarouselItemsGallery(useNativeIndicators: true, setPositionOnConstructor: true, useScrollAnimated: false), Navigation), } } }; diff --git a/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselXamlGallery.xaml.cs b/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselXamlGallery.xaml.cs index 2dadd31ef..ab738fbef 100644 --- a/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselXamlGallery.xaml.cs +++ b/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CarouselViewGalleries/CarouselXamlGallery.xaml.cs @@ -17,7 +17,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi protected override void OnAppearing() { base.OnAppearing(); - // (BindingContext as CarouselViewModel).Position = 2; + //(BindingContext as CarouselViewModel).Position = 2; } } @@ -45,7 +45,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.CarouselVi switch (_type) { case CarouselXamlSampleType.Peek: - items.Add(new CarouselItem(i, "cardBackground")); + items.Add(new CarouselItem(i, "cardBackground.png")); break; default: items.Add(new CarouselItem(i)); diff --git a/Xamarin.Forms.Core.UITests.Shared/Tests/CarouselViewUITests.cs b/Xamarin.Forms.Core.UITests.Shared/Tests/CarouselViewUITests.cs index d74b660bd..10dfd8653 100644 --- a/Xamarin.Forms.Core.UITests.Shared/Tests/CarouselViewUITests.cs +++ b/Xamarin.Forms.Core.UITests.Shared/Tests/CarouselViewUITests.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using NUnit.Framework; using Xamarin.UITest; @@ -190,14 +190,22 @@ namespace Xamarin.Forms.Core.UITests void VisitSubGallery(string galleryName, bool enableIndicator = false) { + App.ScrollUp(); + App.ScrollUp(); + if (enableIndicator) App.Tap(t => t.Marked("EnableIndicatorView")); App.QueryUntilPresent(() => { - App.ScrollDown(); - return App.Query(t => t.Marked(galleryName)); - }); + var query = App.Query(t => t.Marked(galleryName)); + if (query.Count() == 0) + { + App.ScrollDown(); + return null; + } + return query; + }, delayInMs: 500); App.Tap(t => t.Marked(galleryName)); } diff --git a/Xamarin.Forms.Platform.Android/CollectionView/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.Android/CollectionView/CarouselViewRenderer.cs index 36440e993..7417101dd 100644 --- a/Xamarin.Forms.Platform.Android/CollectionView/CarouselViewRenderer.cs +++ b/Xamarin.Forms.Platform.Android/CollectionView/CarouselViewRenderer.cs @@ -62,7 +62,6 @@ namespace Xamarin.Forms.Platform.Android AddLayoutListener(); UpdateIsSwipeEnabled(); UpdateIsBounceEnabled(); - UpdateInitialPosition(); UpdateItemSpacing(); } @@ -131,7 +130,6 @@ namespace Xamarin.Forms.Platform.Android if (adapter != null) { adapter.NotifyItemChanged(_oldPosition); - Carousel.ScrollTo(_oldPosition, position: Xamarin.Forms.ScrollToPosition.Center); } base.UpdateItemSpacing(); @@ -192,11 +190,14 @@ namespace Xamarin.Forms.Platform.Android ItemsViewAdapter = new ItemsViewAdapter(ItemsView, (view, context) => new SizedItemContentView(Context, GetItemWidth, GetItemHeight)); - - + _gotoPosition = -1; + SwapAdapter(ItemsViewAdapter, false); + + if (_oldPosition > 0) + UpdateInitialPosition(); if (ItemsViewAdapter?.ItemsSource is ObservableItemsSource observableItemsSource) observableItemsSource.CollectionItemsSourceChanged += CollectionItemsSourceChanged; @@ -297,7 +298,10 @@ namespace Xamarin.Forms.Platform.Android _oldPosition = position; + _gotoPosition = _oldPosition; + SetCurrentItem(_oldPosition); + Carousel.ScrollTo(_oldPosition, position: Xamarin.Forms.ScrollToPosition.Center, animate: Carousel.AnimatePositionChanges); } void UpdateVisualStates() @@ -406,7 +410,12 @@ namespace Xamarin.Forms.Platform.Android var carouselPosition = Carousel.Position; if (itemCount == 0) + { + //we are trying to set a position but our Collection doesn't have items still + _oldPosition = carouselPosition; return; + } + if (carouselPosition >= itemCount || carouselPosition < 0) throw new IndexOutOfRangeException($"Can't set CarouselView to position {carouselPosition}. ItemsSource has {itemCount} items."); @@ -443,9 +452,9 @@ namespace Xamarin.Forms.Platform.Android { if (!_initialized) { + UpdateInitialPosition(); Carousel.Scrolled += CarouselViewScrolled; - UpdateInitialPosition(); _initialized = true; } diff --git a/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewController.cs b/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewController.cs index d4be0581c..678b8839f 100644 --- a/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewController.cs +++ b/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewController.cs @@ -9,7 +9,7 @@ namespace Xamarin.Forms.Platform.iOS { protected readonly CarouselView Carousel; - bool _viewInitialized; + bool _initialPositionSet; List _oldViews; int _gotoPosition = -1; @@ -33,21 +33,10 @@ namespace Xamarin.Forms.Platform.iOS return cell; } - public override void ViewWillLayoutSubviews() - { - base.ViewWillLayoutSubviews(); - if (!_viewInitialized) - { - _viewInitialized = true; - UpdateInitialPosition(); - } - - UpdateVisualStates(); - } - public override void ViewDidLayoutSubviews() { base.ViewDidLayoutSubviews(); + UpdateInitialPosition(); } public override void DraggingStarted(UIScrollView scrollView) @@ -65,6 +54,8 @@ namespace Xamarin.Forms.Platform.iOS UnsubscribeCollectionItemsSourceChanged(ItemsSource); base.UpdateItemsSource(); SubscribeCollectionItemsSourceChanged(ItemsSource); + _initialPositionSet = false; + UpdateInitialPosition(); } protected override bool IsHorizontal => (Carousel?.ItemsLayout as ItemsLayout)?.Orientation == ItemsLayoutOrientation.Horizontal; @@ -200,18 +191,18 @@ namespace Xamarin.Forms.Platform.iOS void UpdateFromCurrentItem() { var currentItemPosition = GetIndexForItem(Carousel.CurrentItem).Row; - - ScrollToPosition(currentItemPosition, Carousel.Position); + + ScrollToPosition(currentItemPosition, Carousel.Position, Carousel.AnimateCurrentItemChanges); UpdateVisualStates(); } - void ScrollToPosition(int goToPosition, int carouselPosition) + void ScrollToPosition(int goToPosition, int carouselPosition, bool animate, bool forceScroll = false) { - if (_gotoPosition == -1 && goToPosition != carouselPosition) + if (_gotoPosition == -1 && (goToPosition != carouselPosition || forceScroll)) { _gotoPosition = goToPosition; - Carousel.ScrollTo(goToPosition, position: Xamarin.Forms.ScrollToPosition.Center, animate: Carousel.AnimateCurrentItemChanges); + Carousel.ScrollTo(goToPosition, position: Xamarin.Forms.ScrollToPosition.Center, animate: animate); } } @@ -222,35 +213,39 @@ namespace Xamarin.Forms.Platform.iOS if (carouselPosition == _gotoPosition) _gotoPosition = -1; - if(!Carousel.IsDragging) - ScrollToPosition(carouselPosition, currentItemPosition); + if (!Carousel.IsDragging) + ScrollToPosition(carouselPosition, currentItemPosition, Carousel.AnimatePositionChanges); SetCurrentItem(carouselPosition); } void UpdateInitialPosition() { - if (Carousel.CurrentItem != null) + var itemsCount = ItemsSource?.ItemCount; + + if (itemsCount == 0) + return; + + if (!_initialPositionSet) { - int position = 0; + _initialPositionSet = true; - var items = Carousel.ItemsSource as IList; - - for (int n = 0; n < items?.Count; n++) + int position = Carousel.Position; + var currentItem = Carousel.CurrentItem; + if (currentItem != null) { - if (items[n] == Carousel.CurrentItem) - { - position = n; - break; - } + position = ItemsSource.GetIndexForItem(currentItem).Row; + } + else + { + SetCurrentItem(position); } - ScrollToPosition(position, Carousel.Position); - } - else - { - SetCurrentItem(Carousel.Position); + if (position > 0) + ScrollToPosition(position, Carousel.Position, Carousel.AnimatePositionChanges, true); } + + UpdateVisualStates(); } void UpdateVisualStates() diff --git a/Xamarin.Forms.Platform.iOS/Renderers/IndicatorViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/IndicatorViewRenderer.cs index ecd349992..26764a413 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/IndicatorViewRenderer.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/IndicatorViewRenderer.cs @@ -186,7 +186,9 @@ namespace Xamarin.Forms.Platform.iOS if (UIPager == null) return; - UIPager.Pages = GetMaximumVisible(); + UIPager.Pages = GetMaximumVisible(); + + UpdateCurrentPage(); } void UpdateHidesForSinglePage()