diff options
29 files changed, 47 insertions, 3534 deletions
diff --git a/Stubs/Xamarin.Forms.Platform.cs b/Stubs/Xamarin.Forms.Platform.cs index 4c828666..70e8084f 100644 --- a/Stubs/Xamarin.Forms.Platform.cs +++ b/Stubs/Xamarin.Forms.Platform.cs @@ -49,10 +49,7 @@ namespace Xamarin.Forms.Platform [RenderWith (typeof (ListViewRenderer))] internal class _ListViewRenderer { } - - [RenderWith (typeof (CarouselViewRenderer))] - internal class _CarouselViewRenderer { } - + [RenderWith (typeof (SliderRenderer))] internal class _SliderRenderer { } diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla39499.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla39499.cs deleted file mode 100644 index 0d63868a..00000000 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla39499.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; - -using Xamarin.Forms.CustomAttributes; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Collections.ObjectModel; -using Xamarin.Forms.Internals; -#if UITEST -using Xamarin.UITest; -using NUnit.Framework; -#endif - -namespace Xamarin.Forms.Controls -{ - [Preserve(AllMembers = true)] - [Issue(IssueTracker.Bugzilla, 39499, "CarouselViewTest")] - public sealed class CarouselViewContentPage : TestContentPage // or TestMasterDetailPage, etc ... - { - [Preserve(AllMembers = true)] - public sealed class Item - { - static int s_id = 0; - - int id; - - internal Item() - { - id = s_id++; - } - - public int Id => id; - - public string Text => $"Item {Id}"; - } - - [Preserve(AllMembers = true)] - public sealed class ItemView : ContentView - { - public ItemView() - { - var idLabel = new Label() { StyleId = "id", TextColor = Color.White }; - idLabel.SetBinding(Label.TextProperty, nameof(Item.Text)); - - var stackLayout = new StackLayout - { - Children = { - new Label { Text = "Target" }, - new Label { Text = "Stack" } - }, - BackgroundColor = Color.Red - }; - - var button = CreateButton("Hide Target Stack", () => - { - stackLayout.IsVisible = false; - }); - - var buttonImage = CreateButton("AddImage", () => - { - stackLayout.IsVisible = true; - stackLayout.Children.Add(new Image { Source = "menuIcon.png" }); - }); - Content = new StackLayout - { - Children = { - idLabel, - button, - buttonImage, - stackLayout, - } - }; - } - - Button CreateButton(string text, Action clicked) - { - var button = new Button(); - button.Text = text; - button.Clicked += (s, e) => - { - clicked(); - }; - return button; - } - } - - static readonly IList<Item> Items = new ObservableCollection<Item>() { - new Item(), - new Item(), - }; - - Button CreateButton(string text, Action onClicked = null) - { - var button = new Button - { - Text = text - }; - - if (onClicked != null) - button.Clicked += (s, e) => onClicked(); - - return button; - } - - protected override void Init() - { - BackgroundColor = Color.Blue; - - var carouselView = new CarouselView - { - BackgroundColor = Color.Purple, - ItemsSource = Items, - ItemTemplate = new DataTemplate(typeof(ItemView)), - Position = 0 - }; - - var moveBar = new StackLayout - { - Orientation = StackOrientation.Horizontal, - HorizontalOptions = LayoutOptions.FillAndExpand, - Children = - { - CreateButton("+", () => Items.Add(new Item())), - CreateButton("<<", () => carouselView.Position = 0), - CreateButton("<", () => - { - try - { - carouselView.Position--; - } - catch - { - } - }), - CreateButton(">", () => - { - try - { - carouselView.Position++; - } - catch - { - } - }), - CreateButton(">>", () => carouselView.Position = Items.Count - 1) - } - }; - - Content = new StackLayout - { - Children = - { - carouselView, - moveBar - } - }; - } - -#if UITEST - //[Test] - public void CarouselViewTest() - { - var app = RunningApp; - app.WaitForElement(q => q.Marked("Item 0")); - app.SwipeRightToLeft(); - app.WaitForElement(q => q.Marked("Item 1")); - app.Tap(c => c.Marked("<")); - app.WaitForElement(q => q.Marked("Item 0")); - } - - [Test] - public void CarouselViewTestAddItem() - { - var app = RunningApp; - app.WaitForElement(q => q.Marked("Hide Target Stack")); - app.Tap(c => c.Marked("+")); - app.SwipeRightToLeft(); - app.SwipeRightToLeft(); - app.WaitForElement(q => q.Marked("Item 2")); - app.Screenshot("I see the Item 2"); - } -#endif - } -} diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/CarouselViewGallery.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/CarouselViewGallery.cs deleted file mode 100644 index 60817b2b..00000000 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/CarouselViewGallery.cs +++ /dev/null @@ -1,297 +0,0 @@ -using System; - -using Xamarin.Forms.CustomAttributes; -using System.Threading; -using System.Linq; -using System.Collections.Generic; -using Xamarin.Forms.Internals; - -#if UITEST -using Xamarin.UITest; -using NUnit.Framework; -#endif - -namespace Xamarin.Forms.Controls -{ - [Preserve(AllMembers = true)] - [Issue(IssueTracker.Github, 900000, "CarouselView General Tests")] - public class CarouselViewGalleryTests - { -#if UITEST - - public interface IUIProxy - { - void Load(IApp app); - } - public interface IGalleryPage : IUIProxy - { - string Name - { - get; - } - } - - public class Gallery - { - static class Id - { - internal const string SearchBar = nameof(SearchBar); - internal const string GoToTestButton = nameof(GoToTestButton); - } - - public static Gallery Launch() - { - var app = AppSetup.Setup(); - app.WaitForElement(Id.SearchBar); - return new Gallery(app); - } - - IApp _app; - - Gallery(IApp app) - { - _app = app; - } - - public TGalleryPage NaviateToGallery<TGalleryPage>() where TGalleryPage : IGalleryPage, new() - { - var galleryPage = new TGalleryPage(); - _app.EnterText(Id.SearchBar, galleryPage.Name); - _app.Tap(Id.GoToTestButton); - galleryPage.Load(_app); - return galleryPage; - } - public void Screenshot(string message) => _app.Screenshot(message); - public IApp App => _app; - } - - public class CarouselViewGallery : IGalleryPage - { - internal const int InitialItems = 4; - internal const int InitialItemId = 1; - internal const string OnItemSelectedAbbr = "i"; - internal const string OnPositionSelectedAbbr = "p"; - internal const int EventQueueDepth = 7; - - private const double SwipePercentage = 0.50; - private const int SwipeSpeed = 2000; - - static class Id - { - internal const string Name = "CarouselView Gallery"; - internal static string ItemId = nameof(ItemId); - internal static string EventLog = nameof(EventLog); - internal static string SelectedItem = nameof(SelectedItem); - internal static string Position = nameof(Position); - internal static string SelectedPosition = nameof(SelectedPosition); - internal static string Next = nameof(Next); - internal static string Previous = nameof(Previous); - internal static string First = nameof(First); - internal static string Last = nameof(Last); - } - enum Event - { - OnItemSelected, - OnPositionSelected - } - - IApp _app; - List<int> _itemIds; - int _currentPosition; - int _currentItem; - Queue<string> _expectedEvents; - int _eventId; - - public CarouselViewGallery() - { - _itemIds = Enumerable.Range(0, InitialItems).ToList(); - _currentPosition = InitialItemId; - _currentItem = _itemIds[_currentPosition]; - _expectedEvents = new Queue<string>(); - _eventId = 0; - } - - void IUIProxy.Load(IApp app) - { - _app = app; - WaitForValue(Id.ItemId, _currentItem); - WaitForValue(Id.Position, _currentPosition); - } - - private void WaitForValue(string marked, object value) - { - var query = $"* marked:'{marked}' text:'{value}'"; - _app.WaitForElement(o => o.Raw(query)); - - } - private void WaitForPosition(int expectedPosition) - { - var expectedItem = _itemIds[expectedPosition]; - - // expect no movement - if (_currentItem == expectedItem) - Thread.Sleep(TimeSpan.FromMilliseconds(500)); - - // wait for for expected item and corresponding event - WaitForValue(Id.ItemId, expectedItem); - WaitForValue(Id.SelectedItem, expectedItem); - _currentItem = expectedItem; - - // wait for for expected position and corresponding event - WaitForValue(Id.Position, expectedPosition); - WaitForValue(Id.SelectedPosition, expectedPosition); - _currentPosition = expectedPosition; - - // check expected events - var expectedEvents = string.Join(", ", _expectedEvents.ToArray().Reverse()); - WaitForValue(Id.EventLog, expectedEvents); - } - private void ExpectMovementEvents(int expectedPosition) - { - if (expectedPosition == _currentPosition) - return; - - ExpectEvent(Event.OnPositionSelected); - ExpectEvent(Event.OnItemSelected); - } - private void ExpectEvent(Event e) - { - if (e == Event.OnItemSelected) - _expectedEvents.Enqueue($"{OnItemSelectedAbbr}/{_eventId++}"); - - if (e == Event.OnPositionSelected) - _expectedEvents.Enqueue($"{OnPositionSelectedAbbr}/{_eventId++}"); - - if (_expectedEvents.Count == EventQueueDepth) - _expectedEvents.Dequeue(); - } - private void Tap(string buttonText, int expectedPosition) - { - // tap - _app.Tap(buttonText); - - // anticipate events - ExpectMovementEvents(expectedPosition); - - // wait - WaitForPosition(expectedPosition); - } - private void Swipe(bool next, int expectedPosition) - { - // swipe - if (next) - _app.SwipeRightToLeft(swipePercentage: SwipePercentage/*, swipeSpeed: SwipeSpeed*/); - else - _app.SwipeLeftToRight(swipePercentage: SwipePercentage/*, swipeSpeed: SwipeSpeed*/); - - // handle swipe past first - if (expectedPosition == -1 && _currentPosition == 0) - expectedPosition = 0; - - // handle swipe past last - else if (expectedPosition == Count && _currentPosition == Count - 1) - expectedPosition = Count - 1; - - // anticipate events - ExpectMovementEvents(expectedPosition); - - // wait - WaitForPosition(expectedPosition); - } - private void Move(int steps, bool swipe) - { - Action next = swipe ? (Action)SwipeNext : StepNext; - Action previous = swipe ? (Action)SwipePrevious : StepPrevious; - - var action = next; - if (steps < 0) - { - action = previous; - steps = -steps; - } - - for (int i = 0; i < steps; i++) - action(); - } - private void MoveToPosition(int position, bool swipe) - { - Assert.True(position >= 0 && position < Count); - Move(position - _currentPosition, swipe); - } - private void MoveToItem(int targetPage, bool swipe) - { - MoveToPosition(_itemIds.IndexOf(targetPage), swipe); - } - public void MoveToFirst(bool swipe) => MoveToPosition(0, swipe); - public void MoveToLast(bool swipe) => MoveToPosition(Count - 1, swipe); - - public int ItemId => int.Parse(_app.Query(Id.ItemId)[0].Text); - - public string Name => Id.Name; - public int Count => _itemIds.Count; - - public void First() => Tap(Id.First, 0); - public void Last() => Tap(Id.Last, _itemIds.Count - 1); - - public void StepNext() => Tap(Id.Next, _currentPosition + 1); - public void StepPrevious() => Tap(Id.Previous, _currentPosition - 1); - public void Step(int steps) => Move(steps, swipe: false); - public void StepToPosition(int position) => MoveToPosition(position, swipe: false); - public void StepToItem(int item) => MoveToItem(item, swipe: false); - public void StepToFirst() => MoveToFirst(swipe: false); - public void StepToLast() => MoveToLast(swipe: false); - - public void SwipeNext() => Swipe(next: true, expectedPosition: _currentPosition + 1); - public void SwipePrevious() => Swipe(next: false, expectedPosition: _currentPosition - 1); - public void Swipe(int swipes) => Move(swipes, swipe: true); - public void SwipeToPosition(int position) => MoveToPosition(position, swipe: true); - public void SwipeToItem(int item) => MoveToItem(item, swipe: true); - public void SwipeToFirst() => MoveToFirst(swipe: true); - public void SwipeToLast() => MoveToLast(swipe: true); - } - - //[Test] - public void SwipeStepJump() - { - var gallery = Gallery.Launch(); - - try - { - var carousel = gallery.NaviateToGallery<CarouselViewGallery>(); - - // start at something other than 0 - Assert.AreNotEqual(0, CarouselViewGallery.InitialItemId); - Assert.AreEqual(CarouselViewGallery.InitialItemId, carousel.ItemId); - - gallery.App.SetOrientationPortrait(); - - for (var i = 0; i < 2; i++) - { - // programatic jump to first/last - carousel.Last(); - carousel.First(); - - // programatic step to page - carousel.StepToLast(); - carousel.StepToFirst(); - - // swiping - carousel.SwipeToLast(); - carousel.SwipeNext(); // test swipe past end - carousel.SwipeToFirst(); - carousel.SwipePrevious(); // test swipe past start - - gallery.App.SetOrientationLandscape(); - } - - gallery.Screenshot("End"); - } - catch (Exception e) - { - gallery.Screenshot("End"); - throw e; - } - } -#endif - } -} diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems index 913a3e69..7a1892b0 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems @@ -142,13 +142,11 @@ <Compile Include="$(MSBuildThisFileDirectory)Bugzilla36788.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla38978.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla38112.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)Bugzilla39499.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla39668.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla21177.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla39829.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla39458.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla39853.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)CarouselViewGallery.cs" /> <Compile Include="$(MSBuildThisFileDirectory)_Template.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Issue1028.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Issue1075.cs" /> diff --git a/Xamarin.Forms.Controls/CoreGallery.cs b/Xamarin.Forms.Controls/CoreGallery.cs index 8a99befe..27c2f0aa 100644 --- a/Xamarin.Forms.Controls/CoreGallery.cs +++ b/Xamarin.Forms.Controls/CoreGallery.cs @@ -196,7 +196,6 @@ namespace Xamarin.Forms.Controls public CorePageView (Page rootPage, NavigationBehavior navigationBehavior = NavigationBehavior.PushAsync) { var pages = new List<Page> { - new CarouselViewGallaryPage {Title = "CarouselView Gallery"}, new AppLinkPageGallery {Title = "App Link Page Gallery"}, new NestedNativeControlGalleryPage {Title = "Nested Native Controls Gallery"}, new CellForceUpdateSizeGalleryPage {Title = "Cell Force Update Size Gallery"}, diff --git a/Xamarin.Forms.Controls/GalleryPages/CarouselViewGallery.cs b/Xamarin.Forms.Controls/GalleryPages/CarouselViewGallery.cs deleted file mode 100644 index 67ea1e12..00000000 --- a/Xamarin.Forms.Controls/GalleryPages/CarouselViewGallery.cs +++ /dev/null @@ -1,366 +0,0 @@ -using System; -using System.Linq; -using Xamarin.Forms.CustomAttributes; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Collections.ObjectModel; -using Xamarin.Forms.Internals; - -#if UITEST -using Xamarin.UITest; -using NUnit.Framework; -#endif - -namespace Xamarin.Forms.Controls -{ - [Preserve(AllMembers = true)] - public sealed class CarouselViewGallaryPage : ContentPage // or TestMasterDetailPage, etc ... - { - public abstract class Item - { - static int s_id = 0; - - int id; - - internal Item() - { - id = s_id++; - } - - public int Id => id; - public string TypeName => GetType().Name; - } - public sealed class Foo : Item - { - } - public sealed class Bar : Item - { - } - public sealed class Baz : Item - { - } - public sealed class Poo : Item - { - } - public sealed class Moo : Item - { - } - - [Preserve(AllMembers = true)] - public sealed class ItemView : ContentView - { - public static readonly BindableProperty TextColorProperty = BindableProperty.Create( - propertyName: nameof(TextColor), - returnType: typeof(Color), - declaringType: typeof(ItemView), - defaultValue: Color.White, - defaultBindingMode: BindingMode.TwoWay - ); - public static readonly BindableProperty ContextProperty = BindableProperty.Create( - propertyName: nameof(Context), - returnType: typeof(CarouselView), - declaringType: typeof(ItemView), - defaultBindingMode: BindingMode.TwoWay - ); - - public ItemView() - { - - var change = CreateButton("Change", "Change", (items, index) => items[index] = new Moo()); - - var removeBar = new StackLayout - { - Orientation = StackOrientation.Horizontal, - HorizontalOptions = LayoutOptions.FillAndExpand, - Children = { - CreateButton ("- Left", "RemoveLeft", (items, index) => items.RemoveAt (index - 1)), - CreateButton ("Remove", "Remove", (items, index) => items.RemoveAt (index)), - CreateButton ("- Right", "RemoveRight", (items, index) => items.RemoveAt (index + 1)), - } - }; - - var addBar = new StackLayout - { - Orientation = StackOrientation.Horizontal, - HorizontalOptions = LayoutOptions.FillAndExpand, - Children = { - CreateButton ("+ Left", "AddLeft", (items, index) => items.Insert (index, new Moo ())), - CreateButton ("+ Right", "AddRight", (items, index) => { - if (index == items.Count - 1) - items.Add (new Moo ()); - else - items.Insert (index + 1, new Moo ()); - }), - } - }; - - var typeNameLabel = new Label() { StyleId = "typename" }; - typeNameLabel.SetBinding(Label.TextProperty, nameof(Item.TypeName)); - - var idLabel = new Label() - { - AutomationId = "ItemId", - StyleId = "id", - TextColor = Color.White - }; - idLabel.SetBinding(Label.TextProperty, nameof(Item.Id)); - - Content = new StackLayout - { - Children = { - typeNameLabel, - idLabel, - change, - removeBar, - addBar, - } - }; - - PropertyChanged += (s, e) => - { - if (e.PropertyName == "TextColor") - typeNameLabel.TextColor = TextColor; - }; - } - - Button CreateButton(string text, string automationId, Action<IList<Item>, int> clicked) - { - var button = new Button(); - button.AutomationId = automationId; - button.Text = text; - button.Clicked += (s, e) => - { - var items = (IList<Item>)Context.ItemsSource; - var index = items.IndexOf(BindingContext); - clicked(items, index); - }; - return button; - } - - public CarouselView Context - { - get - { - return (CarouselView)GetValue(ContextProperty); - } - set - { - SetValue(ContextProperty, value); - } - } - public Color TextColor - { - get - { - return (Color)GetValue(TextColorProperty); - } - set - { - SetValue(TextColorProperty, value); - } - } - } - public sealed class MyDataTemplateSelector : DataTemplateSelector - { - Dictionary<Type, Color> m_colorByType = new Dictionary<Type, Color>(); - Dictionary<Type, DataTemplate> m_dataTemplateByType = new Dictionary<Type, DataTemplate>(); - - public MyDataTemplateSelector() - { - m_colorByType[typeof(Foo)] = Color.Green; - m_colorByType[typeof(Bar)] = Color.Red; - } - - protected override DataTemplate OnSelectTemplate(object item, BindableObject container) - { - return OnSelectTemplate(item.GetType(), container); - } - - DataTemplate OnSelectTemplate(Type itemType, BindableObject container) - { - DataTemplate dataTemplate; - if (!m_dataTemplateByType.TryGetValue(itemType, out dataTemplate)) - m_dataTemplateByType[itemType] = dataTemplate = CreateTemplate(itemType, container); - return dataTemplate; - } - - DataTemplate CreateTemplate(Type itemType, BindableObject container) - { - var dataTemplate = new DataTemplate(typeof(ItemView)); - - Color color; - if (!m_colorByType.TryGetValue(itemType, out color)) - { - color = Color.Pink; - dataTemplate.SetValue(BackgroundColorProperty, Color.Black); - } - else - { - dataTemplate.SetValue(BackgroundColorProperty, Color.Blue); - } - - dataTemplate.SetValue(ItemView.TextColorProperty, color); - dataTemplate.SetValue(ItemView.ContextProperty, container); - return dataTemplate; - } - } - - static Button CreateButton(string text, string automationId, Action onClicked = null) - { - var button = new Button - { - Text = text, - AutomationId = automationId - }; - - if (onClicked != null) - button.Clicked += (s, e) => onClicked(); - - return button; - } - static Label CreateValue(string text, string automationId = "") => CreateLabel(text, Color.Olive, automationId); - static Label CreateCopy(string text, string automationId = "") => CreateLabel(text, Color.White, automationId); - static Label CreateLabel(string text, Color color, string automationId) - { - return new Label() - { - TextColor = color, - Text = text, - AutomationId = automationId - }; - } - - const int StartPosition = 1; - const int EventQueueLength = 7; - - readonly CarouselView _carouselView; - readonly MyDataTemplateSelector _selector; - readonly IList<Item> _items; - readonly Label _position; - readonly Label _selectedItem; - readonly Label _selectedPosition; - readonly Queue<string> _events; - readonly Label _eventLog; - int _eventId; - - void OnEvent(string name) - { - _events.Enqueue($"{name}/{_eventId++}"); - - if (_events.Count == EventQueueLength) - _events.Dequeue(); - _eventLog.Text = string.Join(", ", _events.ToArray().Reverse()); - - _position.Text = $"{_carouselView.Position}"; - } - - public CarouselViewGallaryPage() - { - _selector = new MyDataTemplateSelector(); - _items = new ObservableCollection<Item>() { - new Baz(), - new Poo(), - new Foo(), - new Bar(), - }; - - _carouselView = new CarouselView - { - BackgroundColor = Color.Purple, - ItemsSource = _items, - ItemTemplate = _selector, - Position = StartPosition - }; - - _events = new Queue<string>(); - _eventId = 0; - _position = CreateValue($"{_carouselView.Position}", "Position"); - _selectedItem = CreateValue("?", "SelectedItem"); - _selectedPosition = CreateValue("?", "SelectedPosition"); - _eventLog = CreateValue(string.Empty, "EventLog"); - - _carouselView.ItemSelected += (s, o) => - { - var selectedItem = (Item)o.SelectedItem; - var selectedItemId = selectedItem.Id; - if (selectedItem != _carouselView.Item) - throw new Exception("CarouselView.Item != ItemSelected"); - _selectedItem.Text = $"{selectedItemId}"; - OnEvent("i"); - }; - - _carouselView.PositionSelected += (s, o) => - { - var selectedPosition = (int)o.SelectedPosition; - if (_items[selectedPosition] != _carouselView.Item) - throw new Exception("CarouselView.Item != Items[selectedPosition]"); - _selectedPosition.Text = $"{selectedPosition}"; - OnEvent("p"); - }; - - BackgroundColor = Color.Blue; - - var moveBar = new StackLayout - { - Orientation = StackOrientation.Horizontal, - HorizontalOptions = LayoutOptions.FillAndExpand, - Children = { - CreateButton ("<<", "First", () => _carouselView.Position = 0), - CreateButton ("<", "Previous", () => { - if (_carouselView.Position == 0) - return; - _carouselView.Position--; - }), - CreateButton (">", "Next", () => { - if (_carouselView.Position == _items.Count - 1) - return; - _carouselView.Position++; - }), - CreateButton (">>", "Last", () => _carouselView.Position = _items.Count - 1) - } - }; - - - var statusBar = new StackLayout - { - Orientation = StackOrientation.Horizontal, - Children = { - CreateCopy("Pos:"), _position, - CreateCopy("OnItemSel:"), _selectedItem, - CreateCopy("OnPosSel:"), _selectedPosition, - } - }; - - var logBar = new StackLayout - { - Orientation = StackOrientation.Horizontal, - Children = { _eventLog } - }; - - Content = new StackLayout - { - Children = { - _carouselView, - moveBar, - statusBar, - logBar - } - }; - } - -#if UITEST - //[Test] - //public void CarouselViewTest () - //{ - // var app = RunningApp; - // app.Screenshot ("I am at Issue 1"); - // app.WaitForElement (q => q.Marked ("Remove")); - - // app.Screenshot ("I see the Label"); - // app.SwipeRight (); - // app.SwipeLeft (); - //} -#endif - } -} diff --git a/Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj b/Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj index 7e0f30f3..ade7cd16 100644 --- a/Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj +++ b/Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj @@ -94,7 +94,6 @@ <Compile Include="ControlGalleryPages\PanGestureGalleryPage.cs" /> <Compile Include="CoreGalleryPages\KeyboardCoreGalleryPage.cs" /> <Compile Include="GalleryPages\BackgroundImageGallery.cs" /> - <Compile Include="GalleryPages\CarouselViewGallery.cs" /> <Compile Include="GalleryPages\ControlTemplatePage.cs" /> <Compile Include="GalleryPages\ControlTemplateXamlPage.xaml.cs"> <DependentUpon>ControlTemplateXamlPage.xaml</DependentUpon> diff --git a/Xamarin.Forms.Core/CarouselView.cs b/Xamarin.Forms.Core/CarouselView.cs deleted file mode 100644 index c74463d3..00000000 --- a/Xamarin.Forms.Core/CarouselView.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using Xamarin.Forms.Platform; - -namespace Xamarin.Forms -{ - [RenderWith(typeof(_CarouselViewRenderer))] - public class CarouselView : ItemsView, ICarouselViewController - { - public static readonly BindableProperty PositionProperty = - BindableProperty.Create( - propertyName: nameof(Position), - returnType: typeof(int), - declaringType: typeof(CarouselView), - defaultValue: 0, - defaultBindingMode: BindingMode.TwoWay - ); - - public static readonly BindableProperty ItemProperty = - BindableProperty.Create( - propertyName: nameof(Item), - returnType: typeof(object), - declaringType: typeof(CarouselView), - defaultValue: null, - defaultBindingMode: BindingMode.TwoWay - ); - - object _lastItem; - int _lastPosition; - - public CarouselView() - { - _lastPosition = 0; - _lastItem = null; - VerticalOptions = LayoutOptions.FillAndExpand; - HorizontalOptions = LayoutOptions.FillAndExpand; - } - - object GetItem(int position) - { - var controller = (IItemViewController)this; - object item = controller.GetItem(position); - return item; - } - - // non-public bc unable to implement on iOS - internal event EventHandler<ItemVisibilityEventArgs> ItemAppearing; - internal event EventHandler<ItemVisibilityEventArgs> ItemDisappearing; - - public int Position - { - get - { - return (int)GetValue(PositionProperty); - } - set - { - SetValue(PositionProperty, value); - } - } - public object Item - { - get - { - return GetValue(ItemProperty); - } - internal set - { - SetValue(ItemProperty, value); - } - } - - public event EventHandler<SelectedItemChangedEventArgs> ItemSelected; - public event EventHandler<SelectedPositionChangedEventArgs> PositionSelected; - - protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) - { - var minimumSize = new Size(40, 40); - return new SizeRequest(minimumSize, minimumSize); - } - - void ICarouselViewController.SendPositionAppearing(int position) - { - ItemAppearing?.Invoke(this, new ItemVisibilityEventArgs(GetItem(position))); - } - void ICarouselViewController.SendPositionDisappearing(int position) - { - ItemDisappearing?.Invoke(this, new ItemVisibilityEventArgs(GetItem(position))); - } - void ICarouselViewController.SendSelectedItemChanged(object item) - { - if (item.Equals(_lastItem)) - return; - _lastItem = item; - - Item = item; - ItemSelected?.Invoke(this, new SelectedItemChangedEventArgs(item)); - } - void ICarouselViewController.SendSelectedPositionChanged(int position) - { - if (_lastPosition == position) - return; - _lastPosition = position; - - Item = ((IItemViewController)this).GetItem(position); - PositionSelected?.Invoke(this, new SelectedPositionChangedEventArgs(position)); - } - } -}
\ No newline at end of file diff --git a/Xamarin.Forms.Core/ICarouselViewController.cs b/Xamarin.Forms.Core/ICarouselViewController.cs deleted file mode 100644 index f59bc3d3..00000000 --- a/Xamarin.Forms.Core/ICarouselViewController.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Xamarin.Forms -{ - public interface ICarouselViewController : IItemViewController - { - void SendPositionAppearing(int position); - void SendPositionDisappearing(int position); - void SendSelectedItemChanged(object item); - void SendSelectedPositionChanged(int position); - } -}
\ No newline at end of file diff --git a/Xamarin.Forms.Core/ItemsViewSimple.cs b/Xamarin.Forms.Core/ItemsViewSimple.cs deleted file mode 100644 index 8ad82b7f..00000000 --- a/Xamarin.Forms.Core/ItemsViewSimple.cs +++ /dev/null @@ -1,356 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Runtime.CompilerServices; - -namespace Xamarin.Forms -{ - public abstract class ItemsView : View, IItemViewController - { - public static readonly BindableProperty ItemsSourceProperty = - BindableProperty.Create( - propertyName: "ItemsSource", - returnType: typeof(IEnumerable), - declaringType: typeof(ItemsView), - defaultValue: Enumerable.Empty<object>() - ); - - public static readonly BindableProperty ItemTemplateProperty = - BindableProperty.Create( - propertyName: "ItemTemplate", - returnType: typeof(DataTemplate), - declaringType: typeof(ItemsView) - ); - - ItemSource _itemSource; - - internal ItemsView() - { - } - - public IEnumerable ItemsSource - { - get - { - return (IEnumerable)GetValue(ItemsSourceProperty); - } - set - { - SetValue(ItemsSourceProperty, value); - } - } - - public DataTemplate ItemTemplate - { - get - { - return (DataTemplate)GetValue(ItemTemplateProperty); - } - set - { - SetValue(ItemTemplateProperty, value); - } - } - - int IItemViewController.Count => _itemSource.Count; - - void IItemViewController.BindView(View view, object item) - { - view.BindingContext = item; - } - - View IItemViewController.CreateView(object type) - { - var dataTemplate = (DataTemplate)type; - object content = dataTemplate.CreateContent(); - var view = (View)content; - view.Parent = this; - return view; - } - - object IItemViewController.GetItem(int index) => _itemSource[index]; - - object IItemViewController.GetItemType(object item) - { - DataTemplate dataTemplate = ItemTemplate; - var dataTemplateSelector = dataTemplate as DataTemplateSelector; - if (dataTemplateSelector != null) - dataTemplate = dataTemplateSelector.SelectTemplate(item, this); - - if (item == null) - throw new ArgumentException($"No DataTemplate resolved for item: {item}."); - - return dataTemplate; - } - - protected override void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - if (propertyName == nameof(ItemsSource)) - { - var itemsSource = ItemsSource; - if (itemsSource == null) - itemsSource = Enumerable.Empty<object>(); - - // abstract enumerable, IList, IList<T>, and IReadOnlyList<T> - _itemSource = new ItemSource(itemsSource); - - // subscribe to collection changed events - var dynamicItemSource = _itemSource as INotifyCollectionChanged; - if (dynamicItemSource != null) - { - new WeakNotifyCollectionChanged(this, dynamicItemSource); - } - } - - base.OnPropertyChanged(propertyName); - } - - internal event NotifyCollectionChangedEventHandler CollectionChanged; - - internal void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - CollectionChanged?.Invoke(sender, e); - } - - sealed class WeakNotifyCollectionChanged - { - readonly WeakReference<INotifyCollectionChanged> _weakCollection; - readonly WeakReference<ItemsView> _weakSource; - - public WeakNotifyCollectionChanged(ItemsView source, INotifyCollectionChanged incc) - { - incc.CollectionChanged += OnCollectionChanged; - - _weakSource = new WeakReference<ItemsView>(source); - _weakCollection = new WeakReference<INotifyCollectionChanged>(incc); - } - - void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - ItemsView source; - if (!_weakSource.TryGetTarget(out source)) - { - INotifyCollectionChanged collection; - if (_weakCollection.TryGetTarget(out collection)) - collection.CollectionChanged -= OnCollectionChanged; - - return; - } - - source.OnCollectionChanged(sender, e); - } - } - - sealed class ItemSource : IEnumerable<object>, INotifyCollectionChanged - { - IndexableCollection _indexable; - - internal ItemSource(IEnumerable enumerable) - { - _indexable = new IndexableCollection(enumerable); - var dynamicItemSource = enumerable as INotifyCollectionChanged; - if (dynamicItemSource != null) - dynamicItemSource.CollectionChanged += OnCollectionChanged; - } - - public int Count => _indexable.Count; - - public IEnumerable Enumerable => _indexable.Enumerable; - - public object this[int index] - { - get - { - // madness ported from listProxy - CollectionSynchronizationContext syncContext = SyncContext; - if (syncContext != null) - { - object value = null; - syncContext.Callback(Enumerable, SyncContext.Context, () => value = _indexable[index], false); - - return value; - } - - return _indexable[index]; - } - } - - CollectionSynchronizationContext SyncContext - { - get - { - CollectionSynchronizationContext syncContext; - BindingBase.TryGetSynchronizedCollection(Enumerable, out syncContext); - return syncContext; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator<object> IEnumerable<object>.GetEnumerator() - { - return GetEnumerator(); - } - - public event NotifyCollectionChangedEventHandler CollectionChanged; - - public Enumerator GetEnumerator() - { - return new Enumerator(this); - } - - void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - Action onCollectionChanged = () => - { - if (CollectionChanged != null) - CollectionChanged(this, e); - }; - - // madness ported from listProxy - CollectionSynchronizationContext syncContext = SyncContext; - if (syncContext != null) - { - syncContext.Callback(Enumerable, syncContext.Context, () => Device.BeginInvokeOnMainThread(onCollectionChanged), false); - } - - else if (Device.IsInvokeRequired) - Device.BeginInvokeOnMainThread(onCollectionChanged); - - else - onCollectionChanged(); - } - - internal struct Enumerator : IEnumerator<object> - { - readonly ItemSource _itemSource; - int _index; - - internal Enumerator(ItemSource itemSource) : this() - { - _itemSource = itemSource; - } - - public bool MoveNext() - { - if (_index == _itemSource.Count) - return false; - - Current = _itemSource[_index++]; - return true; - } - - public object Current - { - get; private set; - } - - public void Reset() - { - Current = null; - _index = 0; - } - - public void Dispose() - { - } - } - - struct IndexableCollection : IEnumerable<object> - { - internal IndexableCollection(IEnumerable list) - { - Enumerable = list; - - if (list is IList) - return; - - if (list is IList<object>) - return; - - if (list is IReadOnlyList<object>) - return; - - Enumerable = list.Cast<object>().ToArray(); - } - - internal IEnumerable Enumerable - { - get; - } - - internal int Count - { - get - { - var list = Enumerable as IList; - if (list != null) - return list.Count; - - var listOf = Enumerable as IList<object>; - if (listOf != null) - return listOf.Count; - - var readOnlyList = (IReadOnlyList<object>)Enumerable; - return readOnlyList.Count; - } - } - - internal object this[int index] - { - get - { - var list = Enumerable as IList; - if (list != null) - return list[index]; - - var listOf = Enumerable as IList<object>; - if (listOf != null) - return listOf[index]; - - var readOnlyList = (IReadOnlyList<object>)Enumerable; - return readOnlyList[index]; - } - } - - internal int IndexOf(object item) - { - var list = Enumerable as IList; - if (list != null) - return list.IndexOf(item); - - var listOf = Enumerable as IList<object>; - if (listOf != null) - return listOf.IndexOf(item); - - var readOnlyList = (IReadOnlyList<object>)Enumerable; - return readOnlyList.IndexOf(item); - } - - public IEnumerator<object> GetEnumerator() - { - var list = Enumerable as IList; - if (list != null) - return list.Cast<object>().GetEnumerator(); - - var listOf = Enumerable as IList<object>; - if (listOf != null) - return listOf.GetEnumerator(); - - var readOnlyList = (IReadOnlyList<object>)Enumerable; - return readOnlyList.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - } - } -}
\ No newline at end of file diff --git a/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj b/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj index d8a3772c..dbd109a6 100644 --- a/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj +++ b/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj @@ -62,7 +62,6 @@ <Compile Include="BindingTypeConverter.cs" /> <Compile Include="BoundsConstraint.cs" /> <Compile Include="BoundsTypeConverter.cs" /> - <Compile Include="CarouselView.cs" /> <Compile Include="CastingEnumerator.cs" /> <Compile Include="Cells\EntryCell.cs" /> <Compile Include="Cells\ImageCell.cs" /> @@ -110,7 +109,6 @@ <Compile Include="HandlerAttribute.cs" /> <Compile Include="HtmlWebViewSource.cs" /> <Compile Include="IButtonController.cs" /> - <Compile Include="ICarouselViewController.cs" /> <Compile Include="IControlTemplated.cs" /> <Compile Include="IDefinition.cs" /> <Compile Include="IEffectControlProvider.cs" /> @@ -138,7 +136,6 @@ <Compile Include="IResourcesProvider.cs" /> <Compile Include="IRootObjectProvider.cs" /> <Compile Include="IScrollViewController.cs" /> - <Compile Include="ItemsViewSimple.cs" /> <Compile Include="ItemTappedEventArgs.cs" /> <Compile Include="ItemVisibilityEventArgs.cs" /> <Compile Include="IValueConverterProvider.cs" /> diff --git a/Xamarin.Forms.Platform.Android/Renderers/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/CarouselViewRenderer.cs deleted file mode 100644 index 7b0da7aa..00000000 --- a/Xamarin.Forms.Platform.Android/Renderers/CarouselViewRenderer.cs +++ /dev/null @@ -1,1246 +0,0 @@ -using System; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Collections.Generic; -using System.Linq; -using Android.Content; -using Android.Graphics; -using Android.Views; -using Android.Support.V7.Widget; -using static System.Diagnostics.Debug; -using IntRectangle = System.Drawing.Rectangle; -using IntSize = System.Drawing.Size; -using IntPoint = System.Drawing.Point; -using AndroidView = Android.Views.View; -using Adapter = Android.Support.V7.Widget.RecyclerView.Adapter; -using Recycler = Android.Support.V7.Widget.RecyclerView.Recycler; -using State = Android.Support.V7.Widget.RecyclerView.State; -using ViewHolder = Android.Support.V7.Widget.RecyclerView.ViewHolder; -using Observer = Android.Support.V7.Widget.RecyclerView.AdapterDataObserver; -using LayoutManager = Android.Support.V7.Widget.RecyclerView.LayoutManager; -using LayoutParams = Android.Support.V7.Widget.RecyclerView.LayoutParams; - -namespace Xamarin.Forms.Platform.Android -{ - internal static class CarouselViewExtensions - { - internal static IntVector BoundTranslation(this IntRectangle viewport, IntVector delta, IntRectangle bound) - { - // TODO: generalize the math - Assert(delta.X == 0 || delta.Y == 0); - - IntVector start = viewport.LeadingCorner(delta); - IntVector end = start + delta; - IntVector clampedEnd = end.Clamp(bound); - IntVector clampedDelta = clampedEnd - start; - return clampedDelta; - } - internal static IntVector Clamp(this IntVector position, IntRectangle bound) - { - return new IntVector( - x: position.X.Clamp(bound.Left, bound.Right), - y: position.Y.Clamp(bound.Top, bound.Bottom) - ); - } - internal static IntVector LeadingCorner(this IntRectangle rectangle, IntVector delta) - { - return new IntVector( - x: delta.X < 0 ? rectangle.Left : rectangle.Right, - y: delta.Y < 0 ? rectangle.Top : rectangle.Bottom - ); - } - internal static IntVector Center(this IntRectangle rectangle) - { - return (IntVector)rectangle.Location + (IntVector)rectangle.Size / 2; - } - internal static int Area(this IntRectangle rectangle) - { - return rectangle.Width * rectangle.Height; - } - - internal static Rectangle ToFormsRectangle(this IntRectangle rectangle, Context context) - { - return new Rectangle( - x: context.FromPixels(rectangle.Left), - y: context.FromPixels(rectangle.Top), - width: context.FromPixels(rectangle.Width), - height: context.FromPixels(rectangle.Height) - ); - } - internal static Rect ToAndroidRectangle(this IntRectangle rectangle) - { - return new Rect( - left: rectangle.Left, - right: rectangle.Right, - top: rectangle.Top, - bottom: rectangle.Bottom - ); - } - - internal static bool LexicographicallyLess(this IntPoint source, IntPoint target) - { - if (source.X < target.X) - return true; - - if (source.X > target.X) - return false; - - return source.Y < target.Y; - } - internal static int[] ToRange(this Tuple<int, int> startAndCount) - { - return Enumerable.Range(startAndCount.Item1, startAndCount.Item2).ToArray(); - } - } - - internal struct IntVector - { - public static explicit operator IntVector(IntSize size) - { - return new IntVector(size.Width, size.Height); - } - public static explicit operator IntVector(IntPoint point) - { - return new IntVector(point.X, point.Y); - } - public static implicit operator IntPoint(IntVector vector) - { - return new IntPoint(vector.X, vector.Y); - } - public static implicit operator IntSize(IntVector vector) - { - return new IntSize(vector.X, vector.Y); - } - - public static bool operator ==(IntVector lhs, IntVector rhs) - { - return lhs.X == rhs.X && lhs.Y == rhs.Y; - } - public static bool operator !=(IntVector lhs, IntVector rhs) - { - return !(lhs == rhs); - } - public static IntRectangle operator -(IntRectangle source, IntVector vector) => source + -vector; - public static IntRectangle operator +(IntRectangle source, IntVector vector) => - new IntRectangle(source.Location + vector, source.Size); - - public static IntVector operator -(IntVector vector, IntVector other) => vector + -other; - public static IntVector operator +(IntVector vector, IntVector other) => - new IntVector( - x: vector.X + other.X, - y: vector.Y + other.Y - ); - - public static IntPoint operator -(IntPoint point, IntVector delta) => point + -delta; - public static IntPoint operator +(IntPoint point, IntVector delta) => - new IntPoint( - x: point.X + delta.X, - y: point.Y + delta.Y - ); - - public static IntVector operator -(IntVector vector) => vector * -1; - public static IntVector operator *(IntVector vector, int scaler) => - new IntVector( - x: vector.X * scaler, - y: vector.Y * scaler - ); - public static IntVector operator /(IntVector vector, int scaler) => - new IntVector( - x: vector.X / scaler, - y: vector.Y / scaler - ); - - public static IntVector operator *(IntVector vector, double scaler) => - new IntVector( - x: (int)(vector.X * scaler), - y: (int)(vector.Y * scaler) - ); - public static IntVector operator /(IntVector vector, double scaler) => vector * (1 / scaler); - - internal static IntVector Origin = new IntVector(0, 0); - internal static IntVector XUnit = new IntVector(1, 0); - internal static IntVector YUnit = new IntVector(0, 1); - - #region Fields - readonly int _x; - readonly int _y; - #endregion - - internal IntVector(int x, int y) - { - _x = x; - _y = y; - } - - internal int X => _x; - internal int Y => _y; - - public override bool Equals(object obj) - { - return base.Equals(obj); - } - public override int GetHashCode() - { - return base.GetHashCode(); - } - public override string ToString() - { - return $"{X},{Y}"; - } - } - - [Flags] - internal enum MeasureSpecificationType - { - Unspecified = 0, - Exactly = 0x1 << 31, - AtMost = 0x1 << 32, - Mask = Exactly | AtMost - } - internal struct MeasureSpecification - { - public static explicit operator MeasureSpecification(int measureSpecification) - { - return new MeasureSpecification(measureSpecification); - } - public static implicit operator int(MeasureSpecification measureSpecification) - { - return measureSpecification.Encode(); - } - - #region Fields - readonly int _value; - readonly MeasureSpecificationType _type; - #endregion - - internal MeasureSpecification(int measureSpecification) - { - _value = measureSpecification & (int)~MeasureSpecificationType.Mask; - _type = (MeasureSpecificationType)(measureSpecification & (int)MeasureSpecificationType.Mask); - } - internal MeasureSpecification(int value, MeasureSpecificationType measureSpecification) - { - _value = value; - _type = measureSpecification; - } - - internal int Value => _value; - internal MeasureSpecificationType Type => _type; - internal int Encode() => Value | (int)Type; - - public override string ToString() - { - return string.Format("{0} {1}", Value, Type); - } - } - - public class CarouselViewRenderer : ViewRenderer<CarouselView, RecyclerView> - { - // http://developer.android.com/reference/android/support/v7/widget/RecyclerView.html - // http://developer.android.com/training/material/lists-cards.html - // http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/ - - internal class VirtualLayoutManager : PhysicalLayoutManager.VirtualLayoutManager - { - #region Fields - const int Columns = 1; - IntSize _itemSize; - #endregion - - #region Private Members - int GetPosition(int itemCount, int positionOrigin, int x, bool exclusive = false) - { - int position = x / _itemSize.Width + positionOrigin; - bool hasRemainder = x % _itemSize.Width != 0; - - if (hasRemainder && x < 0) - position--; - - if (!hasRemainder && exclusive) - position--; - - position = position.Clamp(0, itemCount - 1); - return position; - } - #endregion - - internal override bool CanScrollHorizontally => true; - internal override bool CanScrollVertically => false; - - internal override IntRectangle GetBounds(int originPosition, State state) => - new IntRectangle( - LayoutItem(originPosition, 0).Location, - new IntSize(_itemSize.Width * state.ItemCount, _itemSize.Height) - ); - - internal override Tuple<int, int> GetPositions( - int positionOrigin, - int itemCount, - IntRectangle viewport, - bool includeBuffer) - { - // returns one item off-screen in either direction. - int buffer = includeBuffer ? 1 : 0; - int left = GetPosition(itemCount, positionOrigin - buffer, viewport.Left); - int right = GetPosition(itemCount, positionOrigin + buffer, viewport.Right, exclusive: true); - - int start = left; - int count = right - left + 1; - return new Tuple<int, int>(start, count); - } - internal override void Layout(int positionOffset, IntSize viewportSize, ref IntVector offset) - { - int width = viewportSize.Width / Columns; - int height = viewportSize.Height; - - if (_itemSize.Width != 0) - offset *= (double)width / _itemSize.Width; - - _itemSize = new IntSize(width, height); - } - internal override IntRectangle LayoutItem(int positionOffset, int position) - { - // measure - IntSize size = _itemSize; - - // layout - var location = new IntVector((position - positionOffset) * size.Width, 0); - - // allocate - return new IntRectangle(location, size); - } - - public override string ToString() - { - return $"itemSize={_itemSize}"; - } - } - - #region Private Definitions - class OnScrollListener : RecyclerView.OnScrollListener - { - enum ScrollState - { - Idle, - Dragging, - Settling - } - - readonly Action _onDragEnd; - readonly Action _onDragStart; - ScrollState _lastScrollState; - - internal OnScrollListener( - Action onDragEnd, - Action onDragStart) - { - _onDragEnd = onDragEnd; - _onDragStart = onDragStart; - } - - public override void OnScrollStateChanged(RecyclerView recyclerView, int newState) - { - var state = (ScrollState)newState; - if (_lastScrollState != ScrollState.Dragging && state == ScrollState.Dragging) - _onDragStart(); - - if (_lastScrollState == ScrollState.Dragging && state != ScrollState.Dragging) - _onDragEnd(); - - _lastScrollState = state; - base.OnScrollStateChanged(recyclerView, newState); - } - } - class PositionUpdater : Observer - { - #region Fields - readonly CarouselViewRenderer _carouselView; - #endregion - - internal PositionUpdater(CarouselViewRenderer carouselView) - { - _carouselView = carouselView; - } - - public override void OnItemRangeInserted(int positionStart, int itemCount) - { - if (positionStart > _carouselView._position) - { - // removal after the current position won't change current position - } - else - { - // raise position changed - _carouselView._position += itemCount; - _carouselView.OnPositionChanged(); - } - - base.OnItemRangeInserted(positionStart, itemCount); - } - public override void OnItemRangeRemoved(int positionStart, int itemCount) - { - Assert(itemCount == 1); - - if (positionStart > _carouselView._position) - { - // removal after the current position won't change current position - } - else if (positionStart == _carouselView._position && - positionStart != _carouselView.Adapter.ItemCount) - { - // raise item changed - _carouselView.OnItemChanged(); - return; - } - else - { - // raise position changed - _carouselView._position -= itemCount; - _carouselView.OnPositionChanged(); - } - - base.OnItemRangeRemoved(positionStart, itemCount); - } - } - #endregion - - #region Fields - PhysicalLayoutManager _physicalLayout; - int _position; - bool _disposed; - #endregion - - public CarouselViewRenderer() - { - AutoPackage = false; - } - - protected override void Dispose(bool disposing) - { - if (disposing && !_disposed) - { - _disposed = true; - if (Element != null) - Element.CollectionChanged -= OnCollectionChanged; - } - - base.Dispose(disposing); - } - - #region Private Members - void Initialize() - { - // cache hit? Check if the view page is already created - RecyclerView recyclerView = Control; - if (recyclerView != null) - return; - - // cache miss - recyclerView = new RecyclerView(Context); - SetNativeControl(recyclerView); - - // layoutManager - recyclerView.SetLayoutManager( - layout: _physicalLayout = new PhysicalLayoutManager( - context: Context, - virtualLayout: new VirtualLayoutManager(), - positionOrigin: Element.Position - ) - ); - - // swiping - var dragging = false; - recyclerView.AddOnScrollListener( - new OnScrollListener( - onDragStart: () => dragging = true, - onDragEnd: () => - { - dragging = false; - var velocity = _physicalLayout.Velocity; - - var target = velocity.X > 0 ? - _physicalLayout.VisiblePositions().Max() : - _physicalLayout.VisiblePositions().Min(); - _physicalLayout.ScrollToPosition(target); - } - ) - ); - - // scrolling - var scrolling = false; - _physicalLayout.OnBeginScroll += position => scrolling = true; - _physicalLayout.OnEndScroll += position => scrolling = false; - - // appearing - _physicalLayout.OnAppearing += appearingPosition => - { - Controller.SendPositionAppearing(appearingPosition); - }; - - // disappearing - _physicalLayout.OnDisappearing += disappearingPosition => - { - Controller.SendPositionDisappearing(disappearingPosition); - - // animation completed - if (!scrolling && !dragging) - { - _position = _physicalLayout.VisiblePositions().Single(); - - OnPositionChanged(); - OnItemChanged(); - } - }; - - // adapter - var adapter = new ItemViewAdapter(this); - adapter.RegisterAdapterDataObserver(new PositionUpdater(this)); - recyclerView.SetAdapter(adapter); - } - - ItemViewAdapter Adapter => (ItemViewAdapter)Control.GetAdapter(); - PhysicalLayoutManager LayoutManager => (PhysicalLayoutManager)Control.GetLayoutManager(); - - void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - Adapter.NotifyItemRangeInserted( - positionStart: e.NewStartingIndex, - itemCount: e.NewItems.Count - ); - break; - - case NotifyCollectionChangedAction.Move: - for (var i = 0; i < e.NewItems.Count; i++) - Adapter.NotifyItemMoved( - fromPosition: e.OldStartingIndex + i, - toPosition: e.NewStartingIndex + i - ); - break; - - case NotifyCollectionChangedAction.Remove: - if (Controller.Count == 0) - throw new InvalidOperationException("CarouselView must retain a least one item."); - - Adapter.NotifyItemRangeRemoved( - positionStart: e.OldStartingIndex, - itemCount: e.OldItems.Count - ); - break; - - case NotifyCollectionChangedAction.Replace: - Adapter.NotifyItemRangeChanged( - positionStart: e.OldStartingIndex, - itemCount: e.OldItems.Count - ); - break; - - case NotifyCollectionChangedAction.Reset: - Adapter.NotifyDataSetChanged(); - break; - - default: - throw new Exception($"Enum value '{(int)e.Action}' is not a member of NotifyCollectionChangedAction enumeration."); - } - } - ICarouselViewController Controller => Element; - IVisualElementController VisualElementController => Element; - void OnPositionChanged() - { - Element.Position = _position; - Controller.SendSelectedPositionChanged(_position); - } - void OnItemChanged() - { - object item = ((IItemViewController)Element).GetItem(_position); - Controller.SendSelectedItemChanged(item); - } - #endregion - - protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e) - { - base.OnElementChanged(e); - - CarouselView oldElement = e.OldElement; - CarouselView newElement = e.NewElement; - if (oldElement != null) - { - e.OldElement.CollectionChanged -= OnCollectionChanged; - } - - if (newElement != null) - { - if (Control == null) - { - Initialize(); - } - - // initialize properties - _position = Element.Position; - - // initialize events - Element.CollectionChanged += OnCollectionChanged; - } - } - protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "Position" && _position != Element.Position) - _physicalLayout.ScrollToPosition(Element.Position); - - base.OnElementPropertyChanged(sender, e); - } - protected override void OnLayout(bool changed, int left, int top, int right, int bottom) - { - int width = right - left; - int height = bottom - top; - - LayoutManager.Layout(width, height); - - base.OnLayout(changed, left, top, right, bottom); - - Control.Measure( - widthMeasureSpec: new MeasureSpecification(width, MeasureSpecificationType.Exactly), - heightMeasureSpec: new MeasureSpecification(height, MeasureSpecificationType.Exactly) - ); - - Control.Layout(0, 0, width, height); - } - protected override Size MinimumSize() - { - return new Size(40, 40); - } - } - - // RecyclerView virtualizes indexes (adapter position <-> viewGroup child index) - // PhysicalLayoutManager virtualizes location (regular layout <-> screen) - internal class PhysicalLayoutManager : LayoutManager - { - // ObservableCollection is our public entryway to this method and it only supports single item removal - internal const int MaxItemsRemoved = 1; - - internal struct DecoratedView - { - public static implicit operator AndroidView(DecoratedView view) - { - return view._view; - } - - #region Fields - readonly PhysicalLayoutManager _layout; - readonly AndroidView _view; - #endregion - - internal DecoratedView( - PhysicalLayoutManager layout, - AndroidView view) - { - _layout = layout; - _view = view; - } - - internal int Left => _layout.GetDecoratedLeft(_view); - internal int Top => _layout.GetDecoratedTop(_view); - internal int Bottom => _layout.GetDecoratedBottom(_view); - internal int Right => _layout.GetDecoratedRight(_view); - internal int Width => Right - Left; - internal int Height => Bottom - Top; - internal IntRectangle Rectangle => new IntRectangle(Left, Top, Width, Height); - - internal void Measure(int widthUsed, int heightUsed) - { - _layout.MeasureChild(_view, widthUsed, heightUsed); - } - internal void MeasureWithMargins(int widthUsed, int heightUsed) - { - _layout.MeasureChildWithMargins(_view, widthUsed, heightUsed); - } - internal void Layout(IntRectangle position) - { - var renderer = _view as IVisualElementRenderer; - renderer.Element.Layout(position.ToFormsRectangle(_layout._context)); - - // causes the private LAYOUT_REQUIRED flag to be set so we can be sure the Layout call will properly chain through to all children - Measure(position.Width, position.Height); - _layout.LayoutDecorated(_view, - left: position.Left, - top: position.Top, - right: position.Right, - bottom: position.Bottom - ); - } - internal void Add() - { - _layout.AddView(_view); - } - internal void DetachAndScrap(Recycler recycler) - { - _layout.DetachAndScrapView(_view, recycler); - } - } - internal abstract class VirtualLayoutManager - { - internal abstract Tuple<int, int> GetPositions( - int positionOrigin, - int itemCount, - IntRectangle viewport, - bool isPreLayout - ); - - internal abstract IntRectangle LayoutItem(int positionOrigin, int position); - internal abstract bool CanScrollHorizontally - { - get; - } - internal abstract bool CanScrollVertically - { - get; - } - - internal abstract void Layout(int positionOrigin, IntSize viewportSize, ref IntVector offset); - internal abstract IntRectangle GetBounds(int positionOrigin, State state); - } - - #region Private Defintions - enum AdapterChangeType - { - Removed = 1, - Added, - Moved, - Updated, - Changed - } - enum SnapPreference - { - None = 0, - Begin = 1, - End = -1 - } - sealed class SeekAndSnapScroller : LinearSmoothScroller - { - #region Fields - readonly SnapPreference _snapPreference; - readonly Func<int, IntVector> _vectorToPosition; - #endregion - - internal SeekAndSnapScroller( - Context context, - Func<int, IntVector> vectorToPosition, - SnapPreference snapPreference = SnapPreference.None) - : base(context) - { - _vectorToPosition = vectorToPosition; - _snapPreference = snapPreference; - } - - protected override int HorizontalSnapPreference => (int)_snapPreference; - protected override void OnStart() - { - OnBeginScroll?.Invoke(TargetPosition); - base.OnStart(); - } - protected override void OnStop() - { - // expected this to be triggered with the animation stops but it - // actually seems to be triggered when the target is found - OnEndScroll?.Invoke(TargetPosition); - base.OnStop(); - } - - public event Action<int> OnBeginScroll; - public event Action<int> OnEndScroll; - - public override PointF ComputeScrollVectorForPosition(int targetPosition) - { - var vector = _vectorToPosition(targetPosition); - return new PointF(vector.X, vector.Y); - } - - } - #endregion - - #region Static Fields - readonly static int s_samplesCount = 5; - #endregion - - #region Fields - readonly Context _context; - readonly VirtualLayoutManager _virtualLayout; - readonly Queue<Action<Recycler, State>> _deferredLayout; - readonly Dictionary<int, AndroidView> _viewByAdaptorPosition; - readonly HashSet<int> _visibleAdapterPosition; - readonly SeekAndSnapScroller _scroller; - - int _positionOrigin; // coordinates are relative to the upper left corner of this element - IntVector _locationOffset; // upper left corner of screen is positionOrigin + locationOffset - List<IntVector> _samples; - AdapterChangeType _adapterChangeType; - #endregion - - internal PhysicalLayoutManager(Context context, VirtualLayoutManager virtualLayout, int positionOrigin) - { - _positionOrigin = positionOrigin; - _context = context; - _virtualLayout = virtualLayout; - _viewByAdaptorPosition = new Dictionary<int, AndroidView>(); - _visibleAdapterPosition = new HashSet<int>(); - _samples = Enumerable.Repeat(IntVector.Origin, s_samplesCount).ToList(); - _deferredLayout = new Queue<Action<Recycler, State>>(); - _scroller = new SeekAndSnapScroller( - context: context, - vectorToPosition: adapterPosition => - { - var end = virtualLayout.LayoutItem(_positionOrigin, adapterPosition).Center(); - var begin = Viewport.Center(); - return end - begin; - } - ); - - _scroller.OnBeginScroll += adapterPosition => OnBeginScroll?.Invoke(adapterPosition); - _scroller.OnEndScroll += adapterPosition => OnEndScroll?.Invoke(adapterPosition); - } - - #region Private Members - // helpers to deal with locations as IntRectangles and IntVectors - IntRectangle Rectangle => new IntRectangle(0, 0, Width, Height); - void OffsetChildren(IntVector delta) - { - OffsetChildrenHorizontal(-delta.X); - OffsetChildrenVertical(-delta.Y); - } - void ScrollBy(ref IntVector delta, Recycler recycler, State state) - { - _adapterChangeType = default(AdapterChangeType); - - delta = Viewport.BoundTranslation( - delta: delta, - bound: _virtualLayout.GetBounds(_positionOrigin, state) - ); - - _locationOffset += delta; - _samples.Insert(0, delta); - _samples.RemoveAt(_samples.Count - 1); - - OffsetChildren(delta); - OnLayoutChildren(recycler, state); - } - void OnAppearingOrDisappearing(int position, bool isAppearing) - { - if (isAppearing) - { - if (!_visibleAdapterPosition.Contains(position)) - { - _visibleAdapterPosition.Add(position); - OnAppearing?.Invoke(position); - } - } - else - { - if (_visibleAdapterPosition.Contains(position)) - { - _visibleAdapterPosition.Remove(position); - OnDisappearing?.Invoke(position); - } - } - } - #endregion - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - } - - internal event Action<int> OnAppearing; - internal event Action<int> OnBeginScroll; - internal event Action<int> OnDisappearing; - internal event Action<int> OnEndScroll; - - internal IntVector Velocity => _samples.Aggregate((o, a) => o + a) / _samples.Count; - internal void Layout(int width, int height) - { - // e.g. when rotated the width and height are updated the virtual layout will - // need to resize and provide a new viewport offset given the current one. - _virtualLayout.Layout(_positionOrigin, new IntSize(width, height), ref _locationOffset); - } - internal IntRectangle Viewport => Rectangle + _locationOffset; - internal IEnumerable<int> VisiblePositions() - { - return _visibleAdapterPosition; - } - internal IEnumerable<AndroidView> Views() - { - return _viewByAdaptorPosition.Values; - } - - public override void OnAdapterChanged(Adapter oldAdapter, Adapter newAdapter) - { - RemoveAllViews(); - } - public override void OnItemsChanged(RecyclerView recyclerView) - { - _adapterChangeType = AdapterChangeType.Changed; - - // low-fidelity change event; assume everything has changed. If adapter reports it has "stable IDs" then - // RecyclerView will attempt to synthesize high-fidelity change events: added, removed, moved, updated. - base.OnItemsChanged(recyclerView); - } - public override void OnItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) - { - _adapterChangeType = AdapterChangeType.Added; - - _deferredLayout.Enqueue((recycler, state) => - { - - var viewByAdaptorPositionCopy = _viewByAdaptorPosition.ToArray(); - _viewByAdaptorPosition.Clear(); - foreach (KeyValuePair<int, AndroidView> pair in viewByAdaptorPositionCopy) - { - var view = pair.Value; - var position = pair.Key; - - // position unchanged - if (position < positionStart) - _viewByAdaptorPosition[position] = view; - - // position changed - else - _viewByAdaptorPosition[position + itemCount] = view; - } - - if (_positionOrigin >= positionStart) - _positionOrigin += itemCount; - }); - base.OnItemsAdded(recyclerView, positionStart, itemCount); - } - public override void OnItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) - { - Assert(itemCount == MaxItemsRemoved); - _adapterChangeType = AdapterChangeType.Removed; - - var positionEnd = positionStart + itemCount; - - _deferredLayout.Enqueue((recycler, state) => - { - if (state.ItemCount == 0) - throw new InvalidOperationException("Cannot delete all items."); - - // re-map views to their new positions - var viewByAdaptorPositionCopy = _viewByAdaptorPosition.ToArray(); - _viewByAdaptorPosition.Clear(); - foreach (var pair in viewByAdaptorPositionCopy) - { - var view = pair.Value; - var position = pair.Key; - - // position unchanged - if (position < positionStart) - _viewByAdaptorPosition[position] = view; - - // position changed - else if (position >= positionEnd) - _viewByAdaptorPosition[position - itemCount] = view; - - // removed - else - { - _viewByAdaptorPosition[-1] = view; - if (_visibleAdapterPosition.Contains(position)) - _visibleAdapterPosition.Remove(position); - } - } - - // if removed origin then shift origin to first removed position - if (_positionOrigin >= positionStart && _positionOrigin < positionEnd) - { - _positionOrigin = positionStart; - - // if no items to right of removed origin then set origin to item prior to removed set - if (_positionOrigin >= state.ItemCount) - { - _positionOrigin = state.ItemCount - 1; - - if (!_viewByAdaptorPosition.ContainsKey(_positionOrigin)) - throw new InvalidOperationException( - "VirtualLayoutManager must add items to the left and right of the origin" - ); - } - } - - // if removed before origin then shift origin left - else if (_positionOrigin >= positionEnd) - _positionOrigin -= itemCount; - }); - - base.OnItemsRemoved(recyclerView, positionStart, itemCount); - } - public override void OnItemsMoved(RecyclerView recyclerView, int from, int toValue, int itemCount) - { - _adapterChangeType = AdapterChangeType.Moved; - base.OnItemsMoved(recyclerView, from, toValue, itemCount); - } - public override void OnItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) - { - _adapterChangeType = AdapterChangeType.Updated; - - // rebind rendered updated elements - _deferredLayout.Enqueue((recycler, state) => - { - for (var i = 0; i < itemCount; i++) - { - var position = positionStart + i; - - AndroidView view; - if (!_viewByAdaptorPosition.TryGetValue(position, out view)) - continue; - - recycler.BindViewToPosition(view, position); - } - }); - - base.OnItemsUpdated(recyclerView, positionStart, itemCount); - } - - public override LayoutParams GenerateDefaultLayoutParams() - { - return new LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent); - } - public override AndroidView FindViewByPosition(int adapterPosition) - { - // Used by SmoothScrollToPosition to know when the view - // for the targeted adapterPosition has been attached. - - AndroidView view; - if (!_viewByAdaptorPosition.TryGetValue(adapterPosition, out view)) - return null; - return view; - } - - public override void ScrollToPosition(int adapterPosition) - { - if (adapterPosition < 0 || adapterPosition >= ItemCount) - throw new ArgumentException(nameof(adapterPosition)); - - _scroller.TargetPosition = adapterPosition; - StartSmoothScroll(_scroller); - } - public override void SmoothScrollToPosition(RecyclerView recyclerView, State state, int adapterPosition) - { - ScrollToPosition(adapterPosition); - } - public override bool CanScrollHorizontally() => _virtualLayout.CanScrollHorizontally; - public override bool CanScrollVertically() => _virtualLayout.CanScrollVertically; - - // entry points - public override bool SupportsPredictiveItemAnimations() => true; - public override void OnLayoutChildren(Recycler recycler, State state) - { - var adapterChangeType = _adapterChangeType; - if (state.IsPreLayout) - adapterChangeType = default(AdapterChangeType); - - // adapter updates - if (!state.IsPreLayout) - { - while (_deferredLayout.Count > 0) - _deferredLayout.Dequeue()(recycler, state); - } - - // get visible items - var positions = _virtualLayout.GetPositions( - positionOrigin: _positionOrigin, - itemCount: state.ItemCount, - viewport: Viewport, - // IsPreLayout => some type of data update of yet unknown type. Must assume update - // could be remove so virtualLayout must +1 off-screen left in case origin is - // removed and +n off-screen right to slide onscreen if a big item is removed - isPreLayout: state.IsPreLayout || adapterChangeType == AdapterChangeType.Removed - ).ToRange(); - - // disappearing - var disappearing = _viewByAdaptorPosition.Keys.Except(positions).ToList(); - - // defer cleanup of displaced items and lay them out off-screen so they animate off-screen - if (adapterChangeType == AdapterChangeType.Added) - { - positions = positions.Concat(disappearing).OrderBy(o => o).ToArray(); - disappearing.Clear(); - } - - // recycle - foreach (var position in disappearing) - { - var view = _viewByAdaptorPosition[position]; - - // remove - _viewByAdaptorPosition.Remove(position); - OnAppearingOrDisappearing(position, false); - - // scrap - new DecoratedView(this, view).DetachAndScrap(recycler); - } - - // TODO: Generalize - if (adapterChangeType == AdapterChangeType.Removed && _positionOrigin == state.ItemCount - 1) - { - var vlayout = _virtualLayout.LayoutItem(_positionOrigin, _positionOrigin); - _locationOffset = new IntVector(vlayout.Width - Width, _locationOffset.Y); - } - - var nextLocationOffset = new IntPoint(int.MaxValue, int.MaxValue); - var nextPositionOrigin = int.MaxValue; - foreach (var position in positions) - { - // attach - AndroidView view; - if (!_viewByAdaptorPosition.TryGetValue(position, out view)) - AddView(_viewByAdaptorPosition[position] = view = recycler.GetViewForPosition(position)); - - // layout - var decoratedView = new DecoratedView(this, view); - var layout = _virtualLayout.LayoutItem(_positionOrigin, position); - var physicalLayout = layout - _locationOffset; - decoratedView.Layout(physicalLayout); - - var isVisible = Viewport.IntersectsWith(layout); - if (isVisible) - OnAppearingOrDisappearing(position, true); - - // update offsets - if (isVisible && position < nextPositionOrigin) - { - nextLocationOffset = layout.Location; - nextPositionOrigin = position; - } - } - - // update origin - if (nextPositionOrigin != int.MaxValue) - { - _positionOrigin = nextPositionOrigin; - _locationOffset -= (IntVector)nextLocationOffset; - } - - // scrapped views not re-attached must be recycled (why isn't this done by Android, I dunno) - foreach (var viewHolder in recycler.ScrapList.ToArray()) - recycler.RecycleView(viewHolder.ItemView); - } - - public override int ScrollHorizontallyBy(int dx, Recycler recycler, State state) - { - var delta = new IntVector(dx, 0); - ScrollBy(ref delta, recycler, state); - return delta.X; - } - public override int ScrollVerticallyBy(int dy, Recycler recycler, State state) - { - var delta = new IntVector(0, dy); - ScrollBy(ref delta, recycler, state); - return delta.Y; - } - - public override string ToString() - { - return $"offset={_locationOffset}"; - } - } - - internal class ItemViewAdapter : Adapter - { - #region Private Definitions - class CarouselViewHolder : ViewHolder - { - #region Fields - readonly View _view; - readonly IVisualElementRenderer _visualElementRenderer; - #endregion - - public CarouselViewHolder(View view, IVisualElementRenderer renderer) - : base(renderer.ViewGroup) - { - _visualElementRenderer = renderer; - _view = view; - } - - public View View => _view; - public IVisualElementRenderer VisualElementRenderer => _visualElementRenderer; - } - #endregion - - #region Fields - readonly IVisualElementRenderer _renderer; - readonly Dictionary<int, object> _typeByTypeId; - readonly Dictionary<object, int> _typeIdByType; - int _nextItemTypeId; - #endregion - - public ItemViewAdapter(IVisualElementRenderer carouselRenderer) - { - _renderer = carouselRenderer; - _typeByTypeId = new Dictionary<int, object>(); - _typeIdByType = new Dictionary<object, int>(); - _nextItemTypeId = 0; - } - - #region Private Members - ItemsView Element - { - get - { - return (ItemsView)_renderer.Element; - } - } - IItemViewController Controller - { - get - { - return Element; - } - } - #endregion - - public override int ItemCount - { - get - { - return Controller.Count; - } - } - public override int GetItemViewType(int position) - { - // get item and type from ItemSource and ItemTemplate - object item = Controller.GetItem(position); - object type = Controller.GetItemType(item); - - // map type as DataTemplate to type as Id - int id = default(int); - if (!_typeIdByType.TryGetValue(type, out id)) - { - id = _nextItemTypeId++; - _typeByTypeId[id] = type; - _typeIdByType[type] = id; - } - return id; - } - public override ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) - { - // create view from type - var type = _typeByTypeId[viewType]; - var view = Controller.CreateView(type); - - // create renderer for view - var renderer = Platform.CreateRenderer(view); - Platform.SetRenderer(view, renderer); - - // package renderer + view - return new CarouselViewHolder(view, renderer); - } - public override void OnBindViewHolder(ViewHolder holder, int position) - { - var carouselHolder = (CarouselViewHolder)holder; - - var item = Controller.GetItem(position); - Controller.BindView(carouselHolder.View, item); - } - } -}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj b/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj index 44bac817..4c8351bd 100644 --- a/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj +++ b/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj @@ -150,7 +150,6 @@ <Compile Include="LayoutExtensions.cs" /> <Compile Include="Renderers\AHorizontalScrollView.cs" /> <Compile Include="Renderers\ButtonDrawable.cs" /> - <Compile Include="Renderers\CarouselViewRenderer.cs" /> <Compile Include="Renderers\AlignmentExtensions.cs" /> <Compile Include="IStartActivityForResult.cs" /> <Compile Include="Renderers\ConditionalFocusLayout.cs" /> diff --git a/Xamarin.Forms.Platform.UAP/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.UAP/Properties/AssemblyInfo.cs index 01702c9d..83c6dfb7 100644 --- a/Xamarin.Forms.Platform.UAP/Properties/AssemblyInfo.cs +++ b/Xamarin.Forms.Platform.UAP/Properties/AssemblyInfo.cs @@ -18,7 +18,6 @@ using Xamarin.Forms.Platform.UWP; [assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))] [assembly: ExportRenderer(typeof(Button), typeof(ButtonRenderer))] [assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))] -[assembly: ExportRenderer(typeof(CarouselView), typeof(CarouselViewRenderer))] [assembly: ExportRenderer(typeof(ScrollView), typeof(ScrollViewRenderer))] [assembly: ExportRenderer(typeof(ProgressBar), typeof(ProgressBarRenderer))] [assembly: ExportRenderer(typeof(Slider), typeof(SliderRenderer))] diff --git a/Xamarin.Forms.Platform.UAP/Resources.xaml b/Xamarin.Forms.Platform.UAP/Resources.xaml index b66958bf..7a11fe4f 100644 --- a/Xamarin.Forms.Platform.UAP/Resources.xaml +++ b/Xamarin.Forms.Platform.UAP/Resources.xaml @@ -37,30 +37,26 @@ </Style> <Style x:Key="ActionSheetStyle" TargetType="ContentDialog" /> - <Style x:Key="ActionSheetList" TargetType="ListView"> - <Setter Property="SelectionMode" Value="None" /> - <Setter Property="ItemContainerStyle"> - <Setter.Value> - <Style TargetType="ListViewItem"> - <Setter Property="Margin" Value="0" /> - <Setter Property="HorizontalContentAlignment" Value="Stretch" /> - </Style> - </Setter.Value> - </Setter> - <Setter Property="ItemTemplate"> - <Setter.Value> - <DataTemplate> - <TextBlock Text="{Binding}" HorizontalAlignment="Stretch" VerticalAlignment="Center" Style="{ThemeResource SubtitleTextBlockStyle}" /> - </DataTemplate> - </Setter.Value> - </Setter> - </Style> - - <DataTemplate x:Key="ItemTemplate"> - <uwp:ItemControl HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" /> - </DataTemplate> + <Style x:Key="ActionSheetList" TargetType="ListView"> + <Setter Property="SelectionMode" Value="None" /> + <Setter Property="ItemContainerStyle"> + <Setter.Value> + <Style TargetType="ListViewItem"> + <Setter Property="Margin" Value="0" /> + <Setter Property="HorizontalContentAlignment" Value="Stretch" /> + </Style> + </Setter.Value> + </Setter> + <Setter Property="ItemTemplate"> + <Setter.Value> + <DataTemplate> + <TextBlock Text="{Binding}" HorizontalAlignment="Stretch" VerticalAlignment="Center" Style="{ThemeResource SubtitleTextBlockStyle}" /> + </DataTemplate> + </Setter.Value> + </Setter> + </Style> - <ControlTemplate TargetType="ContentDialog" x:Key="MyContentDialogControlTemplate"> + <ControlTemplate TargetType="ContentDialog" x:Key="MyContentDialogControlTemplate"> <Border x:Name="Container"> <Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> diff --git a/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj b/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj index bccb702e..c992f826 100644 --- a/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj +++ b/Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj @@ -27,7 +27,8 @@ <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <NoWarn></NoWarn> + <NoWarn> + </NoWarn> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> @@ -38,7 +39,8 @@ <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <NoWarn></NoWarn> + <NoWarn> + </NoWarn> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'"> <PlatformTarget>ARM</PlatformTarget> @@ -129,9 +131,6 @@ <Compile Include="..\Xamarin.Forms.Platform.WinRT\AlignmentExtensions.cs"> <Link>AlignmentExtensions.cs</Link> </Compile> - <Compile Include="..\Xamarin.Forms.Platform.WinRT\CarouselViewRenderer.cs"> - <Link>CarouselViewRenderer.cs</Link> - </Compile> <Compile Include="..\Xamarin.Forms.Platform.WinRT\FormsTextBox.cs"> <Link>FormsTextBox.cs</Link> </Compile> diff --git a/Xamarin.Forms.Platform.WP8/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.WP8/CarouselViewRenderer.cs deleted file mode 100644 index 6050d07d..00000000 --- a/Xamarin.Forms.Platform.WP8/CarouselViewRenderer.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.Phone.Controls; -using SLButton = System.Windows.Controls.Button; -using SLBinding = System.Windows.Data.Binding; - -namespace Xamarin.Forms.Platform.WinPhone -{ - public class CarouselViewRenderer : ViewRenderer<CarouselView, LongListSelector> - { - protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e) - { - } - } -}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.WP8/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.WP8/Properties/AssemblyInfo.cs index f1be5df1..e081584b 100644 --- a/Xamarin.Forms.Platform.WP8/Properties/AssemblyInfo.cs +++ b/Xamarin.Forms.Platform.WP8/Properties/AssemblyInfo.cs @@ -48,7 +48,6 @@ using TableView = Xamarin.Forms.TableView; [assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))] [assembly: ExportRenderer(typeof(NavigationMenu), typeof(NavigationMenuRenderer))] [assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))] -[assembly: ExportRenderer(typeof(CarouselView), typeof(CarouselViewRenderer))] [assembly: ExportRenderer(typeof(TableView), typeof(TableViewRenderer))] [assembly: ExportRenderer(typeof(NativeViewWrapper), typeof(NativeViewWrapperRenderer))] diff --git a/Xamarin.Forms.Platform.WP8/Xamarin.Forms.Platform.WP8.csproj b/Xamarin.Forms.Platform.WP8/Xamarin.Forms.Platform.WP8.csproj index 62c4918f..eda2cdce 100644 --- a/Xamarin.Forms.Platform.WP8/Xamarin.Forms.Platform.WP8.csproj +++ b/Xamarin.Forms.Platform.WP8/Xamarin.Forms.Platform.WP8.csproj @@ -158,7 +158,6 @@ <Compile Include="FormsPhoneTextBox.cs" /> <Compile Include="FrameworkElementExtensions.cs" /> <Compile Include="LayoutExtensions.cs" /> - <Compile Include="CarouselViewRenderer.cs" /> <Compile Include="MD5.cs" /> <Compile Include="MD5CryptoServiceProvider.cs" /> <Compile Include="NativeViewWrapper.cs" /> diff --git a/Xamarin.Forms.Platform.WinRT/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.WinRT/CarouselViewRenderer.cs deleted file mode 100644 index f29909df..00000000 --- a/Xamarin.Forms.Platform.WinRT/CarouselViewRenderer.cs +++ /dev/null @@ -1,227 +0,0 @@ -using System; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Linq; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; -using WFlipView = Windows.UI.Xaml.Controls.FlipView; -using WBinding = Windows.UI.Xaml.Data.Binding; -using WApp = Windows.UI.Xaml.Application; -using WSize = Windows.Foundation.Size; -using WDataTemplate = Windows.UI.Xaml.DataTemplate; - -#if WINDOWS_UWP - -namespace Xamarin.Forms.Platform.UWP -#else - -namespace Xamarin.Forms.Platform.WinRT -#endif -{ - public class CarouselViewRenderer : ViewRenderer<CarouselView, FrameworkElement> - { - WFlipView _flipView; - - bool _leftAdd; - - ICarouselViewController Controller - { - get - { - return Element; - } - } - - protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e) - { - base.OnElementChanged(e); - - if (e.OldElement != null) - { - _flipView.SelectionChanged -= SelectionChanged; - _flipView.ItemsSource = null; - Element.CollectionChanged -= CollectionChanged; - } - - if (e.NewElement != null) - { - if (_flipView == null) - { - _flipView = new FlipView - { - IsSynchronizedWithCurrentItem = false, - ItemTemplate = (WDataTemplate)WApp.Current.Resources["ItemTemplate"] - }; - } - - _flipView.ItemsSource = Element.ItemsSource; - _flipView.SelectedIndex = Element.Position; - _flipView.SelectionChanged += SelectionChanged; - Element.CollectionChanged += CollectionChanged; - } - - if (_flipView != Control) - SetNativeControl(_flipView); - } - - protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "Position" && _flipView.SelectedIndex != Element.Position) - { - if (!_leftAdd) - _flipView.SelectedIndex = Element.Position; - _leftAdd = false; - } - - base.OnElementPropertyChanged(sender, e); - } - - void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - Controller.SendSelectedPositionChanged(_flipView.SelectedIndex); - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - if (e.NewStartingIndex <= Element.Position) - { - _leftAdd = true; - int position = Element.Position + e.NewItems.Count; - PositionChanged(position); - } - break; - - case NotifyCollectionChangedAction.Move: - break; - - case NotifyCollectionChangedAction.Remove: - if (Controller.Count == 0) - throw new InvalidOperationException("CarouselView must retain a least one item."); - - if (e.OldStartingIndex < Element.Position) - PositionChanged(Element.Position - e.OldItems.Count); - break; - - case NotifyCollectionChangedAction.Replace: - break; - - case NotifyCollectionChangedAction.Reset: - break; - - default: - throw new Exception($"Enum value '{(int)e.Action}' is not a member of NotifyCollectionChangedAction enumeration."); - } - } - - void PositionChanged(int position) - { - if (!_leftAdd) - _flipView.SelectedIndex = position; - Element.Position = position; - Controller.SendSelectedPositionChanged(position); - } - - void SelectionChanged(object sender, SelectionChangedEventArgs e) - { - object[] addedItems = e.AddedItems.ToArray(); - object[] removedItems = e.RemovedItems.ToArray(); - - object addedItem = addedItems.SingleOrDefault(); - if (addedItem != null) - { - PositionChanged(_flipView.SelectedIndex); - Controller.SendSelectedItemChanged(addedItems.Single()); - } - } - } - - public class ItemControl : ContentControl - { - CarouselView _carouselView; - object _item; - View _view; - - public ItemControl() - { - DataContextChanged += OnDataContextChanged; - } - - CarouselView CarouselView => LoadCarouselView(); - - IItemViewController Controller => CarouselView; - - protected override WSize ArrangeOverride(WSize finalSize) - { - _view.Layout(new Rectangle(0, 0, CarouselView.Width, CarouselView.Height)); - return base.ArrangeOverride(finalSize); - } - - protected override WSize MeasureOverride(WSize availableSize) - { - LoadCarouselView(); - - if (_item != null) - { - SetDataContext(_item); - _item = null; - } - - return base.MeasureOverride(availableSize); - } - - CarouselView LoadCarouselView() - { - if (_carouselView != null) - return _carouselView; - - DependencyObject parent = VisualTreeHelper.GetParent(this); - CarouselViewRenderer renderer = default(CarouselViewRenderer); - - do - { - if (parent == null) - return null; - - renderer = parent as CarouselViewRenderer; - if (renderer != null) - break; - - parent = VisualTreeHelper.GetParent(parent); - } while (true); - - _carouselView = renderer.Element; - return _carouselView; - } - - void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) - { - object item = args.NewValue; - - if (_carouselView != null) - SetDataContext(item); - - else if (item != null) - _item = item; - } - - void SetDataContext(object item) - { - // type item - object type = Controller.GetItemType(item); - - // activate item - _view = Controller.CreateView(type); - _view.Parent = CarouselView; - _view.Layout(new Rectangle(0, 0, CarouselView.Width, CarouselView.Height)); - - // render item - IVisualElementRenderer renderer = Platform.CreateRenderer(_view); - Platform.SetRenderer(_view, renderer); - Content = renderer; - - // bind item - Controller.BindView(_view, item); - } - } -}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.WinRT/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.WinRT/Properties/AssemblyInfo.cs index f24feecf..d1975076 100644 --- a/Xamarin.Forms.Platform.WinRT/Properties/AssemblyInfo.cs +++ b/Xamarin.Forms.Platform.WinRT/Properties/AssemblyInfo.cs @@ -18,7 +18,6 @@ using Xamarin.Forms.Platform.WinRT; [assembly: ExportRenderer(typeof(Label), typeof(LabelRenderer))] [assembly: ExportRenderer(typeof(Button), typeof(ButtonRenderer))] [assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))] -[assembly: ExportRenderer(typeof(CarouselView), typeof(CarouselViewRenderer))] [assembly: ExportRenderer(typeof(ScrollView), typeof(ScrollViewRenderer))] [assembly: ExportRenderer(typeof(ProgressBar), typeof(ProgressBarRenderer))] [assembly: ExportRenderer(typeof(Slider), typeof(SliderRenderer))] diff --git a/Xamarin.Forms.Platform.WinRT/Resources.xaml b/Xamarin.Forms.Platform.WinRT/Resources.xaml index c1edeeaf..3982521f 100644 --- a/Xamarin.Forms.Platform.WinRT/Resources.xaml +++ b/Xamarin.Forms.Platform.WinRT/Resources.xaml @@ -20,28 +20,24 @@ <Setter Property="Background" Value="{ThemeResource ApplicationPageBackgroundThemeBrush}" /> </Style> - <Style x:Key="ActionSheetList" TargetType="ListView"> - <Setter Property="SelectionMode" Value="None" /> - <Setter Property="ItemContainerStyle"> - <Setter.Value> - <Style TargetType="ListViewItem"> - <Setter Property="Margin" Value="0" /> - <Setter Property="HorizontalContentAlignment" Value="Stretch" /> - </Style> - </Setter.Value> - </Setter> - <Setter Property="ItemTemplate"> - <Setter.Value> - <DataTemplate> - <TextBlock Text="{Binding}" HorizontalAlignment="Stretch" VerticalAlignment="Center" Style="{ThemeResource SubheaderTextBlockStyle}" /> - </DataTemplate> - </Setter.Value> - </Setter> - </Style> - - <DataTemplate x:Key="ItemTemplate"> - <forms:ItemControl HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" /> - </DataTemplate> + <Style x:Key="ActionSheetList" TargetType="ListView"> + <Setter Property="SelectionMode" Value="None" /> + <Setter Property="ItemContainerStyle"> + <Setter.Value> + <Style TargetType="ListViewItem"> + <Setter Property="Margin" Value="0" /> + <Setter Property="HorizontalContentAlignment" Value="Stretch" /> + </Style> + </Setter.Value> + </Setter> + <Setter Property="ItemTemplate"> + <Setter.Value> + <DataTemplate> + <TextBlock Text="{Binding}" HorizontalAlignment="Stretch" VerticalAlignment="Center" Style="{ThemeResource SubheaderTextBlockStyle}" /> + </DataTemplate> + </Setter.Value> + </Setter> + </Style> <DataTemplate x:Key="CellTemplate"> <forms:CellControl HorizontalContentAlignment="Stretch" Height="{Binding Cell.RenderHeight,RelativeSource={RelativeSource Mode=Self},Converter={StaticResource HeightConverter}}" /> diff --git a/Xamarin.Forms.Platform.WinRT/Xamarin.Forms.Platform.WinRT.csproj b/Xamarin.Forms.Platform.WinRT/Xamarin.Forms.Platform.WinRT.csproj index 7f41e8ba..6d28b900 100644 --- a/Xamarin.Forms.Platform.WinRT/Xamarin.Forms.Platform.WinRT.csproj +++ b/Xamarin.Forms.Platform.WinRT/Xamarin.Forms.Platform.WinRT.csproj @@ -37,7 +37,8 @@ <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <NoWarn></NoWarn> + <NoWarn> + </NoWarn> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> @@ -47,7 +48,8 @@ <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> - <NoWarn></NoWarn> + <NoWarn> + </NoWarn> </PropertyGroup> <ItemGroup> <!-- A reference to the entire .NET Framework is automatically included --> @@ -66,7 +68,6 @@ </Compile> </ItemGroup> <ItemGroup Condition=" '$(OS)' != 'Unix' "> - <Compile Include="CarouselViewRenderer.cs" /> <Compile Include="NativeViewWrapper.cs" /> <Compile Include="NativeViewWrapperRenderer.cs" /> <Compile Include="ViewExtensions.cs" /> diff --git a/Xamarin.Forms.Platform.iOS/Properties/AssemblyInfo.cs b/Xamarin.Forms.Platform.iOS/Properties/AssemblyInfo.cs index f1593568..822e143f 100644 --- a/Xamarin.Forms.Platform.iOS/Properties/AssemblyInfo.cs +++ b/Xamarin.Forms.Platform.iOS/Properties/AssemblyInfo.cs @@ -44,7 +44,6 @@ using MonoTouch.UIKit; [assembly: ExportRenderer(typeof(Frame), typeof(FrameRenderer))] [assembly: ExportRenderer(typeof(NavigationMenu), typeof(NavigationMenuRenderer))] [assembly: ExportRenderer(typeof(ListView), typeof(ListViewRenderer))] -[assembly: ExportRenderer(typeof(CarouselView), typeof(CarouselViewRenderer))] [assembly: ExportRenderer(typeof(OpenGLView), typeof(OpenGLViewRenderer))] [assembly: ExportRenderer(typeof(NativeViewWrapper), typeof(NativeViewWrapperRenderer))] [assembly: ExportRenderer(typeof(TabbedPage), typeof(TabbedRenderer))] diff --git a/Xamarin.Forms.Platform.iOS/Renderers/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/CarouselViewRenderer.cs deleted file mode 100644 index 39cda393..00000000 --- a/Xamarin.Forms.Platform.iOS/Renderers/CarouselViewRenderer.cs +++ /dev/null @@ -1,424 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Drawing; -using System.Linq; -#if __UNIFIED__ -using UIKit; -using Foundation; -#else -using MonoTouch.UIKit; -using MonoTouch.Foundation; -#endif -#if __UNIFIED__ -using RectangleF = CoreGraphics.CGRect; -using SizeF = CoreGraphics.CGSize; -using PointF = CoreGraphics.CGPoint; - -#else -using nfloat=System.Single; -using nint=System.Int32; -using nuint=System.UInt32; -#endif - -namespace Xamarin.Forms.Platform.iOS -{ - /// <summary> - /// UICollectionView visualizes a collection of data. UICollectionViews are created indirectly by first creating a - /// CarouselViewController from which the CollectionView is accessed via the CollectionView property. - /// The CarouselViewController functionality is exposed through a set of interfaces (aka "conforms to" in the Apple - /// docs). - /// When Xamarin exposed CarouselViewRenderer the following interfaces where implemented as virtual methods: - /// UICollectionViewSource - /// UIScrollViewDelegate - /// UICollectionViewDelegate Allow you to manage the selection and highlighting of items in a collection view - /// UICollectionViewDataSource Creation and configuration of cells and supplementary views used to display data - /// The interfaces only implement required method while the UICollectionView exposes optional methods via - /// ExportAttribute. - /// The C# method name may be aliased. For example, C# "GetCell" maps to obj-C "CellForItemAtIndexPath". -#pragma warning disable 1584 - /// <seealso cref="https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/" /> -#pragma warning restore 1584 - /// </summary> - public class CarouselViewRenderer : ViewRenderer<CarouselView, UICollectionView> - { - #region Static Fields - const int DefaultMinimumDimension = 44; - #endregion - - #region Fields - // As on Android, ScrollToPostion from 0 to 2 should not raise OnPositionChanged for 1 - // Tracking the _targetPosition allows for skipping events for intermediate positions - int? _targetPosition; - - int _position; - CarouselViewController _controller; - RectangleF _lastBounds; - #endregion - - ICarouselViewController Controller - { - get - { - return Element; - } - } - void Initialize() - { - // cache hit? - var carouselView = base.Control; - if (carouselView != null) - return; - - _lastBounds = Bounds; - _controller = new CarouselViewController( - renderer: this, - initialPosition: Element.Position - ); - - // hook up on position changed event - // not ideal; the event is raised upon releasing the swipe instead of animation completion - _controller.OnWillDisplayCell += o => OnPositionChange(o); - - // populate cache - SetNativeControl(_controller.CollectionView); - } - - void OnItemChange(int position) - { - var item = Controller.GetItem(position); - Controller.SendSelectedItemChanged(item); - } - void OnPositionChange(int position) - { - if (position == _position) - return; - - if (_targetPosition != null && position != _targetPosition) - return; - - _targetPosition = null; - _position = position; - Element.Position = _position; - - Controller.SendSelectedPositionChanged(position); - OnItemChange(position); - return; - } - void ScrollToPosition(int position, bool animated = true) - { - if (position == _position) - return; - - _targetPosition = position; - _controller.ScrollToPosition(position, animated); - } - void OnCollectionChanged(object source, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - _controller.ReloadData(); - - if (e.NewStartingIndex <= _position) - ShiftPosition(e.NewItems.Count); - - break; - - case NotifyCollectionChangedAction.Move: - for (var i = 0; i < e.NewItems.Count; i++) - { - _controller.MoveItem( - oldPosition: e.OldStartingIndex + i, - newPosition: e.NewStartingIndex + i - ); - } - break; - - case NotifyCollectionChangedAction.Remove: - if (Controller.Count == 0) - throw new InvalidOperationException("CarouselView must retain a least one item."); - - if (e.OldStartingIndex == _position) - { - _controller.DeleteItems( - Enumerable.Range(e.OldStartingIndex, e.OldItems.Count) - ); - if (_position == Controller.Count) - _position--; - OnItemChange(_position); - } - - else - { - _controller.ReloadData(); - - if (e.OldStartingIndex < _position) - ShiftPosition(-e.OldItems.Count); - } - - break; - - case NotifyCollectionChangedAction.Replace: - _controller.ReloadItems( - Enumerable.Range(e.OldStartingIndex, e.OldItems.Count) - ); - break; - - case NotifyCollectionChangedAction.Reset: - _controller.ReloadData(); - break; - - default: - throw new Exception(); - } - } - void ShiftPosition(int offset) - { - // By default the position remains the same which causes an animation in the case - // of the added/removed position preceding the current position. I prefer the constructed - // Android behavior whereby the item remains the same and the position changes. - ScrollToPosition(_position + offset, false); - } - - protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "Position" && _position != Element.Position) - // not ideal; the event is raised before the animation to move completes (or even starts) - ScrollToPosition(Element.Position); - - base.OnElementPropertyChanged(sender, e); - } - protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e) - { - base.OnElementChanged(e); - - CarouselView oldElement = e.OldElement; - CarouselView newElement = e.NewElement; - if (oldElement != null) - { - e.OldElement.CollectionChanged -= OnCollectionChanged; - } - - if (newElement != null) - { - if (Control == null) - { - Initialize(); - } - - // initialize properties - _position = Element.Position; - - // hook up crud events - Element.CollectionChanged += OnCollectionChanged; - } - } - - public override void LayoutSubviews() - { - base.LayoutSubviews(); - - if (_lastBounds == Bounds) - return; - - base.Control.ReloadData(); - _lastBounds = Bounds; - _controller.ScrollToPosition(_position, false); - } - public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) - { - return Control.GetSizeRequest(widthConstraint, heightConstraint, DefaultMinimumDimension, DefaultMinimumDimension); - } - } - - internal sealed class CarouselViewController : UICollectionViewController - { - new sealed class Layout : UICollectionViewFlowLayout - { - static readonly nfloat ZeroMinimumInteritemSpacing = 0; - static readonly nfloat ZeroMinimumLineSpacing = 0; - - public Layout(UICollectionViewScrollDirection scrollDirection) - { - ScrollDirection = scrollDirection; - MinimumInteritemSpacing = ZeroMinimumInteritemSpacing; - MinimumLineSpacing = ZeroMinimumLineSpacing; - } - } - sealed class Cell : UICollectionViewCell - { - IItemViewController _controller; - int _position; - IVisualElementRenderer _renderer; - View _view; - - void Bind(object item, int position) - { - //if (position != this._position) - // controller.SendPositionDisappearing (this._position); - - _position = position; - OnBind?.Invoke(_position); - - _controller.BindView(_view, item); - } - - [Export("initWithFrame:")] - internal Cell(RectangleF frame) : base(frame) - { - _position = -1; - } - internal void Initialize(IItemViewController controller, object itemType, object item, int position) - { - _position = position; - - if (_controller == null) - { - _controller = controller; - - // create view - _view = controller.CreateView(itemType); - - // bind view - Bind(item, _position); - - // render view - _renderer = Platform.CreateRenderer(_view); - Platform.SetRenderer(_view, _renderer); - - // attach view - var uiView = _renderer.NativeView; - ContentView.AddSubview(uiView); - } - else - Bind(item, _position); - } - - public Action<int> OnBind; - public override void LayoutSubviews() - { - base.LayoutSubviews(); - - _renderer.Element.Layout(new Rectangle(0, 0, ContentView.Frame.Width, ContentView.Frame.Height)); - } - } - - readonly Dictionary<object, int> _typeIdByType; - CarouselViewRenderer _renderer; - int _nextItemTypeId; - int _initialPosition; - - internal CarouselViewController( - CarouselViewRenderer renderer, - int initialPosition) - : base(new Layout(UICollectionViewScrollDirection.Horizontal)) - { - _renderer = renderer; - _typeIdByType = new Dictionary<object, int>(); - _nextItemTypeId = 0; - _initialPosition = initialPosition; - } - - CarouselViewRenderer Renderer => _renderer; - CarouselView Element => _renderer.Element; - ICarouselViewController Controller => Element; - - [Export("collectionView:layout:sizeForItemAtIndexPath:")] - SizeF GetSizeForItem( - UICollectionView collectionView, - UICollectionViewLayout layout, - NSIndexPath indexPath) - { - return collectionView.Frame.Size; - } - - internal Action<int> OnBind; - internal Action<int> OnWillDisplayCell; - - public override void WillDisplayCell(UICollectionView collectionView, UICollectionViewCell cell, NSIndexPath indexPath) - { - if (_initialPosition != 0) - { - ScrollToPosition(_initialPosition, false); - _initialPosition = 0; - return; - } - - var index = indexPath.Row; - OnWillDisplayCell?.Invoke(index); - } - public override nint NumberOfSections(UICollectionView collectionView) - { - return 1; - } - public override void ViewDidLoad() - { - base.ViewDidLoad(); - - CollectionView.PagingEnabled = true; - CollectionView.BackgroundColor = UIColor.Clear; - } - public override nint GetItemsCount(UICollectionView collectionView, nint section) - { - var result = Controller.Count; - return result; - } - public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath) - { - var index = indexPath.Row; - - if (_initialPosition != 0) - index = _initialPosition; - - var item = Controller.GetItem(index); - var itemType = Controller.GetItemType(item); - - var itemTypeId = default(int); - if (!_typeIdByType.TryGetValue(itemType, out itemTypeId)) - { - _typeIdByType[itemType] = itemTypeId = _nextItemTypeId++; - CollectionView.RegisterClassForCell(typeof(Cell), itemTypeId.ToString()); - } - - var cell = (Cell)CollectionView.DequeueReusableCell(itemTypeId.ToString(), indexPath); - cell.Initialize(Element, itemType, item, index); - - // a semantically weak approach to OnAppearing; decided not to expose as such - if (cell.OnBind == null) - cell.OnBind += o => OnBind?.Invoke(o); - - return cell; - } - - internal void ReloadData() => CollectionView.ReloadData(); - internal void ReloadItems(IEnumerable<int> positions) - { - var indices = positions.Select(o => NSIndexPath.FromRowSection(o, 0)).ToArray(); - CollectionView.ReloadItems(indices); - } - internal void DeleteItems(IEnumerable<int> positions) - { - var indices = positions.Select(o => NSIndexPath.FromRowSection(o, 0)).ToArray(); - CollectionView.DeleteItems(indices); - } - internal void MoveItem(int oldPosition, int newPosition) - { - base.MoveItem( - CollectionView, - NSIndexPath.FromRowSection(oldPosition, 0), - NSIndexPath.FromRowSection(newPosition, 0) - ); - } - internal void ScrollToPosition(int position, bool animated = true) - { - CollectionView.ScrollToItem( - indexPath: NSIndexPath.FromRowSection(position, 0), - scrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, - animated: animated - ); - } - } -}
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.Classic.csproj b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.Classic.csproj index a3ba13c1..285660e6 100644 --- a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.Classic.csproj +++ b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.Classic.csproj @@ -78,7 +78,6 @@ <Compile Include="LayoutExtensions.cs" /> <Compile Include="Renderers\AlignmentExtensions.cs" /> <Compile Include="PageExtensions.cs" /> - <Compile Include="Renderers\CarouselViewRenderer.cs" /> <Compile Include="Renderers\ExportCellAttribute.cs" /> <Compile Include="Renderers\ExportImageSourceHandlerAttribute.cs" /> <Compile Include="Renderers\ExportRendererAttribute.cs" /> diff --git a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj index 1f9a92e2..3ed36e7f 100644 --- a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj +++ b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj @@ -140,7 +140,6 @@ <Compile Include="Renderers\AlignmentExtensions.cs" /> <Compile Include="Forms.cs" /> <Compile Include="PageExtensions.cs" /> - <Compile Include="Renderers\CarouselViewRenderer.cs" /> <Compile Include="Resources\StringResources.Designer.cs"> <AutoGen>True</AutoGen> <DesignTime>True</DesignTime> diff --git a/docs/APIDocs.projitems b/docs/APIDocs.projitems index b26e0daa..1cec1f07 100644 --- a/docs/APIDocs.projitems +++ b/docs/APIDocs.projitems @@ -9,6 +9,6 @@ <Import_RootNamespace>APIDocs</Import_RootNamespace> </PropertyGroup> <ItemGroup> - <Content Include="$(MSBuildThisFileDirectory)**\*.xml" /> + <Folder Include="$(MSBuildThisFileDirectory)Xamarin.Forms.Core\Xamarin.Forms\" /> </ItemGroup> </Project>
\ No newline at end of file diff --git a/docs/Xamarin.Forms.Core/Xamarin.Forms/CarouselView.xml b/docs/Xamarin.Forms.Core/Xamarin.Forms/CarouselView.xml deleted file mode 100644 index 688fd68e..00000000 --- a/docs/Xamarin.Forms.Core/Xamarin.Forms/CarouselView.xml +++ /dev/null @@ -1,230 +0,0 @@ -<Type Name="CarouselView" FullName="Xamarin.Forms.CarouselView"> - <TypeSignature Language="C#" Value="public class CarouselView : Xamarin.Forms.ItemsView, Xamarin.Forms.ICarouselViewController" /> - <TypeSignature Language="ILAsm" Value=".class public auto ansi beforefieldinit CarouselView extends Xamarin.Forms.ItemsView implements class Xamarin.Forms.ICarouselViewController, class Xamarin.Forms.IItemViewController" /> - <AssemblyInfo> - <AssemblyName>Xamarin.Forms.Core</AssemblyName> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <Base> - <BaseTypeName>Xamarin.Forms.ItemsView</BaseTypeName> - </Base> - <Interfaces> - <Interface> - <InterfaceName>Xamarin.Forms.ICarouselViewController</InterfaceName> - </Interface> - </Interfaces> - <Attributes> - <Attribute> - <AttributeName>Xamarin.Forms.RenderWith(typeof(Xamarin.Forms.Platform._CarouselViewRenderer))</AttributeName> - </Attribute> - </Attributes> - <Docs> - <summary>To be added.</summary> - <remarks>To be added.</remarks> - </Docs> - <Members> - <Member MemberName=".ctor"> - <MemberSignature Language="C#" Value="public CarouselView ();" /> - <MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor() cil managed" /> - <MemberType>Constructor</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <Parameters /> - <Docs> - <summary>To be added.</summary> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="Item"> - <MemberSignature Language="C#" Value="public int Item { get; }" /> - <MemberSignature Language="ILAsm" Value=".property instance int32 Item" /> - <MemberType>Property</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>System.Int32</ReturnType> - </ReturnValue> - <Docs> - <summary>To be added.</summary> - <value>To be added.</value> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="ItemProperty"> - <MemberSignature Language="C#" Value="public static readonly Xamarin.Forms.BindableProperty ItemProperty;" /> - <MemberSignature Language="ILAsm" Value=".field public static initonly class Xamarin.Forms.BindableProperty ItemProperty" /> - <MemberType>Field</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>Xamarin.Forms.BindableProperty</ReturnType> - </ReturnValue> - <Docs> - <summary>To be added.</summary> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="ItemSelected"> - <MemberSignature Language="C#" Value="public event EventHandler<Xamarin.Forms.SelectedItemChangedEventArgs> ItemSelected;" /> - <MemberSignature Language="ILAsm" Value=".event class System.EventHandler`1<class Xamarin.Forms.SelectedItemChangedEventArgs> ItemSelected" /> - <MemberType>Event</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>System.EventHandler<Xamarin.Forms.SelectedItemChangedEventArgs></ReturnType> - </ReturnValue> - <Docs> - <summary>To be added.</summary> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="OnMeasure"> - <MemberSignature Language="C#" Value="protected override Xamarin.Forms.SizeRequest OnMeasure (double widthConstraint, double heightConstraint);" /> - <MemberSignature Language="ILAsm" Value=".method familyhidebysig virtual instance valuetype Xamarin.Forms.SizeRequest OnMeasure(float64 widthConstraint, float64 heightConstraint) cil managed" /> - <MemberType>Method</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>Xamarin.Forms.SizeRequest</ReturnType> - </ReturnValue> - <Parameters> - <Parameter Name="widthConstraint" Type="System.Double" /> - <Parameter Name="heightConstraint" Type="System.Double" /> - </Parameters> - <Docs> - <param name="widthConstraint">To be added.</param> - <param name="heightConstraint">To be added.</param> - <summary>To be added.</summary> - <returns>To be added.</returns> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="Position"> - <MemberSignature Language="C#" Value="public int Position { get; set; }" /> - <MemberSignature Language="ILAsm" Value=".property instance int32 Position" /> - <MemberType>Property</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>System.Int32</ReturnType> - </ReturnValue> - <Docs> - <summary>To be added.</summary> - <value>To be added.</value> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="PositionProperty"> - <MemberSignature Language="C#" Value="public static readonly Xamarin.Forms.BindableProperty PositionProperty;" /> - <MemberSignature Language="ILAsm" Value=".field public static initonly class Xamarin.Forms.BindableProperty PositionProperty" /> - <MemberType>Field</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>Xamarin.Forms.BindableProperty</ReturnType> - </ReturnValue> - <Docs> - <summary>To be added.</summary> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="PositionSelected"> - <MemberSignature Language="C#" Value="public event EventHandler<Xamarin.Forms.SelectedPositionChangedEventArgs> PositionSelected;" /> - <MemberSignature Language="ILAsm" Value=".event class System.EventHandler`1<class Xamarin.Forms.SelectedPositionChangedEventArgs> PositionSelected" /> - <MemberType>Event</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>System.EventHandler<Xamarin.Forms.SelectedPositionChangedEventArgs></ReturnType> - </ReturnValue> - <Docs> - <summary>To be added.</summary> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="Xamarin.Forms.ICarouselViewController.SendPositionAppearing"> - <MemberSignature Language="C#" Value="void ICarouselViewController.SendPositionAppearing (int position);" /> - <MemberSignature Language="ILAsm" Value=".method hidebysig newslot virtual instance void Xamarin.Forms.ICarouselViewController.SendPositionAppearing(int32 position) cil managed" /> - <MemberType>Method</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>System.Void</ReturnType> - </ReturnValue> - <Parameters> - <Parameter Name="position" Type="System.Int32" /> - </Parameters> - <Docs> - <param name="position">To be added.</param> - <summary>To be added.</summary> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="Xamarin.Forms.ICarouselViewController.SendPositionDisappearing"> - <MemberSignature Language="C#" Value="void ICarouselViewController.SendPositionDisappearing (int position);" /> - <MemberSignature Language="ILAsm" Value=".method hidebysig newslot virtual instance void Xamarin.Forms.ICarouselViewController.SendPositionDisappearing(int32 position) cil managed" /> - <MemberType>Method</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>System.Void</ReturnType> - </ReturnValue> - <Parameters> - <Parameter Name="position" Type="System.Int32" /> - </Parameters> - <Docs> - <param name="position">To be added.</param> - <summary>To be added.</summary> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="Xamarin.Forms.ICarouselViewController.SendSelectedItemChanged"> - <MemberSignature Language="C#" Value="void ICarouselViewController.SendSelectedItemChanged (object item);" /> - <MemberSignature Language="ILAsm" Value=".method hidebysig newslot virtual instance void Xamarin.Forms.ICarouselViewController.SendSelectedItemChanged(object item) cil managed" /> - <MemberType>Method</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>System.Void</ReturnType> - </ReturnValue> - <Parameters> - <Parameter Name="item" Type="System.Object" /> - </Parameters> - <Docs> - <param name="item">To be added.</param> - <summary>To be added.</summary> - <remarks>To be added.</remarks> - </Docs> - </Member> - <Member MemberName="Xamarin.Forms.ICarouselViewController.SendSelectedPositionChanged"> - <MemberSignature Language="C#" Value="void ICarouselViewController.SendSelectedPositionChanged (int position);" /> - <MemberSignature Language="ILAsm" Value=".method hidebysig newslot virtual instance void Xamarin.Forms.ICarouselViewController.SendSelectedPositionChanged(int32 position) cil managed" /> - <MemberType>Method</MemberType> - <AssemblyInfo> - <AssemblyVersion>2.0.0.0</AssemblyVersion> - </AssemblyInfo> - <ReturnValue> - <ReturnType>System.Void</ReturnType> - </ReturnValue> - <Parameters> - <Parameter Name="position" Type="System.Int32" /> - </Parameters> - <Docs> - <param name="position">To be added.</param> - <summary>To be added.</summary> - <remarks>To be added.</remarks> - </Docs> - </Member> - </Members> -</Type> |