diff options
Diffstat (limited to 'Xamarin.Forms.Controls.Issues')
2 files changed, 0 insertions, 1037 deletions
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla52487.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla52487.cs deleted file mode 100644 index 4f1d0971..00000000 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla52487.cs +++ /dev/null @@ -1,1036 +0,0 @@ -using Xamarin.Forms.CustomAttributes; -using Xamarin.Forms.Internals; -using System.Linq; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System; -using System.Diagnostics; -using System.Collections.Generic; -using System.Collections; -using System.Collections.Concurrent; -using System.Threading; - -#if UITEST -using Xamarin.UITest; -using NUnit.Framework; -#endif - -namespace Xamarin.Forms.Controls.Issues -{ - [Preserve(AllMembers = true)] - [Issue( - IssueTracker.Bugzilla, - 502487, - "ListView with Recycle + HasUnevenRows generates lots (and lots!) of content view", - // https://bugzilla.xamarin.com/show_bug.cgi?id=52487 - PlatformAffected.iOS - )] - public class Bugzilla52487 : TestContentPage - { -#if __IOS__ - const int MaxAskDelta = 0; - const int MaxViewDelta = 5; - const int MaxAttachDelta = 5; -#elif __ANDROID__ - const int MaxAskDelta = 0; - const int MaxViewDelta = 1; - const int MaxAttachDelta = 1; -#else - const int MaxAskDelta = 0; - const int MaxViewDelta = int.MaxValue; - const int MaxAttachDelta = int.MaxValue; -#endif - - const int CountFontSize = 12; - const int CellFontSize = 12; - const int MinScrollDelta = 2; - const int ItemsCount = 1000; - const int GroupCount = 100; - const int DefaultItemHeight = 300 / 4; - const int MinimumItemHeight = 40; - - // dis-enable item type when % item id is zero - const int DisableModulous = 11; - - // generate alternate item type when % item id is zero - const int ItemTypeModulous = 7; - - // render half height when % item id is zero (RecycleElement or RecycleElementAndDataTemplate) - const int HalfHeightModulous = 5; - - // select alternate cell type when % item id is zero (RecycleElement) - const int DataTemplateModulous = 3; - - static Tuple<int, int, int> Mix = new Tuple<int, int, int>(255, 255, 100); - static Tuple<int, int, int> AltMix = new Tuple<int, int, int>(100, 100, 255); - static IEnumerable<Color> ColorGenerator(Tuple<int, int, int> mix) - { - double colorDelta = 0; - while (true) - { - colorDelta += 2 * Math.PI / 100; - var r = (Math.Sin(colorDelta) + 1) / 2 * 255; - var g = (Math.Sin(colorDelta * 2) + 1) / 2 * 255; - var b = (Math.Sin(colorDelta * 3) + 1) / 2 * 255; - - if (mix != null) - { - r = (r + mix.Item1) / 2; - g = (g + mix.Item2) / 2; - b = (b + mix.Item3) / 2; - } - - yield return Color.FromRgb((int)r, (int)g, (int)b); - } - } - - [Preserve(AllMembers = true)] - class LazyReadOnlyList<V> : IReadOnlyList<V> - where V : class - { - int _count; - object _context; - List<WeakReference<V>> _items; - Action<int> _onAsk; - Func<LazyReadOnlyList<V>, int, object, V> _activate; - - internal LazyReadOnlyList( - int count, - object context, - Action<int> onAsk, - Func<LazyReadOnlyList<V>, int, object, V> activate) - { - _count = count; - _context = context; - _onAsk = onAsk; - _activate = activate; - _items = new List<WeakReference<V>>( - Enumerable.Range(0, count) - .Select(o => new WeakReference<V>(null)) - ); - } - - protected object Context - { - get { return _context; } - set { _context = value; } - } - protected IEnumerable<WeakReference<V>> WeakItems => - _items; - - public V this[int index] - { - get - { - _onAsk(index); - - var weakItem = _items[index]; - - V item; - if (!weakItem.TryGetTarget(out item)) - { - _items[index] = - new WeakReference<V>( - item = _activate(this, index, _context)); - } - - return item; - } - } - - public int Count - => _count; - - public IEnumerator<V> GetEnumerator() - { - for (var i = 0; i < Count; i++) - yield return this[i]; - } - - IEnumerator IEnumerable.GetEnumerator() - => GetEnumerator(); - } - - [Preserve(AllMembers = true)] - public abstract partial class ListViewSpy<T> : ListViewSpy - { - [Preserve(AllMembers = true)] - abstract class Selector : DataTemplateSelector - { - [Preserve(AllMembers = true)] - internal class SelectByData : Selector - { - public SelectByData() : base( - typeof(ItemViewCell.Selected.ByDataNormal), - typeof(ItemViewCell.Selected.ByDataAlternate) - ) - { } - - protected override DataTemplate OnSelectTemplate(object item, BindableObject container) - { - // RecycleElement previously placed no restraint on the type of view - // the resulting DataTemplate could return. So the resulting DataTemplate - // could randomly pick a view type to render items even between appearances - // on screen. - - // After this fix, the DataTempate will be required to return the same - // type of view although the type need not be a function of only the item type. - // So the type of view a DataTemplates chooses to return can be a function of - // the item _data_ and not only an items type. - - __counter.OnSelectTemplate++; - - // item could be either Item.Full type or Item.Half type... - if (!(item is Item.Normal) && !(item is Item.Alternate)) - throw new ArgumentException(); - - // ... but selector chooses DataTemplate strictly via item _data_. - return ((Item)item).Id % DataTemplateModulous == 0 ? - _dataTemplateAlt : _dataTemplate; - } - } - - [Preserve(AllMembers = true)] - internal class SelectByType : Selector - { - public SelectByType() : base( - typeof(ItemViewCell.Selected.ByTypeNormal), - typeof(ItemViewCell.Selected.ByTypeAlternate) - ) - { } - - protected override DataTemplate OnSelectTemplate(object item, BindableObject container) - { - // RecycleElementAndDataTemplate requires that - // DataTempalte be a function of the item _type_ - - __counter.OnSelectTemplate++; - - if (item is Item.Normal) - return _dataTemplate; - - if (item is Item.Alternate) - return _dataTemplateAlt; - - throw new ArgumentException(); - } - } - - DataTemplate _dataTemplate; - DataTemplate _dataTemplateAlt; - - public Selector(Type nomral, Type alternate) - { - // RecycleElementAndDataTemplate requires - // that the DataTemplate use the .ctor that takes a type - _dataTemplate = new DataTemplate(nomral); - _dataTemplateAlt = new DataTemplate(alternate); - } - } - - [Preserve(AllMembers = true)] - abstract class ItemViewCell : ViewCell - { - [Preserve(AllMembers = true)] - internal abstract class Selected : ItemViewCell - { - [Preserve(AllMembers = true)] - internal class ByTypeNormal : ByType - { - static Color NextColor() { Colors.MoveNext(); return Colors.Current; } - static readonly IEnumerator<Color> Colors = ColorGenerator(Mix).GetEnumerator(); - - public ByTypeNormal() : base(NextColor()) { } - } - [Preserve(AllMembers = true)] - internal class ByTypeAlternate : ByType - { - static Color NextColor() { Colors.MoveNext(); return Colors.Current; } - static readonly IEnumerator<Color> Colors = ColorGenerator(AltMix).GetEnumerator(); - - public ByTypeAlternate() : base(NextColor()) { } - - protected override bool IsAlternate => true; - } - [Preserve(AllMembers = true)] - internal abstract class ByType : Selected - { - internal ByType(Color color) - : base(color) { } - - protected override void OnBindingContextChanged() - { - base.OnBindingContextChanged(); - - if (BindingContext == null || !(BindingContext is ItemViewCell)) - return; - - // check that template is a function of the item type - var itemType = BindingContext.GetType(); - var itemIsNormalType = itemType == typeof(Item.Normal); - var expectedTemplateType = itemIsNormalType ? typeof(ByTypeNormal) : typeof(ByTypeAlternate); - - var templateType = GetType(); - if (templateType != expectedTemplateType) - throw new ArgumentException( - $"BindingContext.GetType() = {itemType.Name}, " + - $"TemplateType {templateType.Name}!={expectedTemplateType.Name}"); - } - } - - [Preserve(AllMembers = true)] - internal class ByDataNormal : ByData - { - static Color NextColor() { Colors.MoveNext(); return Colors.Current; } - static readonly IEnumerator<Color> Colors = ColorGenerator(Mix).GetEnumerator(); - - public ByDataNormal() : base(NextColor()) { } - } - [Preserve(AllMembers = true)] - internal class ByDataAlternate : ByData - { - static Color NextColor() { Colors.MoveNext(); return Colors.Current; } - static readonly IEnumerator<Color> Colors = ColorGenerator(AltMix).GetEnumerator(); - - public ByDataAlternate() : base(NextColor()) { } - - protected override bool IsAlternate => true; - } - [Preserve(AllMembers = true)] - internal abstract class ByData : Selected - { - internal ByData(Color color) - : base(color) { } - - protected override void OnBindingContextChanged() - { - base.OnBindingContextChanged(); - - if (BindingContext == null) - return; - - // check that template is a function of the item data - var isRemainderZero = BindingContext.Id % DataTemplateModulous == 0; - var expectedItemType = isRemainderZero ? typeof(Item.Alternate) : typeof(Item.Normal); - var expectedTemplateType = isRemainderZero ? typeof(ByDataAlternate) : typeof(ByDataNormal); - - var templateType = GetType(); - if (templateType != expectedTemplateType) - throw new ArgumentException( - $"Item.Id = {BindingContext?.Id}, " + - $"TemplateType {templateType.Name}!={expectedTemplateType.Name}"); - } - } - - internal Selected(Color color) - : base(color) { } - } - - [Preserve(AllMembers = true)] - internal class Constant : ItemViewCell - { - static Color NextColor() { Colors.MoveNext(); return Colors.Current; } - static readonly IEnumerator<Color> Colors = ColorGenerator(Mix).GetEnumerator(); - - public Constant() : base(NextColor()) { } - } - - readonly int _id; - readonly Label _label; - - ItemViewCell(Color color) - { - _id = __counter.CellAlloc++; - - View = _label = new Label - { - BackgroundColor = color, - VerticalTextAlignment = TextAlignment.Center, - HorizontalTextAlignment = TextAlignment.Center, - FontSize = CellFontSize - }; - - _label.SetBinding(HeightRequestProperty, nameof(Item.Value)); - } - - Item BindingContext - => (Item)base.BindingContext; - int ItemId - => BindingContext.Id; - int? ItemGroupId - => BindingContext.GroupId; - bool IsAlternateItem - => BindingContext is Item.Alternate; - - protected virtual bool IsAlternate => false; - - protected override void OnBindingContextChanged() - { - base.OnBindingContextChanged(); - - __counter.CellBind++; - - if (BindingContext == null) - return; - - // double check that item generator returned correct type of item - var isRemainderZero = BindingContext.Id % ItemTypeModulous == 0; - var expectedItemType = isRemainderZero ? typeof(Item.Alternate) : typeof(Item.Normal); - var itemType = BindingContext.GetType(); - if (itemType != expectedItemType) - throw new ArgumentException( - $"Item.Id = {BindingContext?.Id}, ItemType {GetType().Name}!={expectedItemType.Name}"); - - } - - protected override void OnAppearing() - { - IsEnabled = ItemId % DisableModulous == 0; - - _label.Text = ToString(); - _label.FontAttributes = IsEnabled ? FontAttributes.Italic : FontAttributes.None; - - __counter.AttachCell(_id); - } - - public new int Id - => _id; - - // cell type is (1) constant or a function of the the Item (2) data or (3) type - public override string ToString() - => $"{ItemId}" + - (ItemGroupId == null ? "" : "/" + ItemGroupId) + - $"{(IsAlternateItem ? "*" : "")} ->" + - $" {_id}{(IsAlternate ? "*" : "")}"; - - ~ItemViewCell() - { - int id; - __counter.CellFree++; - __counter.DetachCell(_id); - // update would be off UI thread - } - } - - [Preserve(AllMembers = true)] - abstract class Item : INotifyPropertyChanged - { - [Preserve(AllMembers = true)] - internal class Normal : Item - { - internal Normal(int id, int? groupId, int height) - : base(id, groupId, height) { } - }; - - [Preserve(AllMembers = true)] - internal class Alternate : Item - { - internal Alternate(int id, int? groupId, int height) - : base(id, groupId, height) { } - }; - - internal static Item Create(LazyItemList list, int index, int height) - { - - var id = list.ItemIdOffset + index; - - if (id % ItemTypeModulous == 0) - return new Alternate(id, list.Id, height); - - return new Normal(id, list.Id, height); - } - - int _allocId; - - int _id; - int _index; - int? _groupId; - int _height; - - private Item(int id, int? groupId, int height) - { - _allocId = Interlocked.Increment(ref __counter.ItemAlloc); - - if (id % HalfHeightModulous == 0) - height = height / 2; - - _groupId = groupId; - _height = height; - _id = id; - } - - public int Id - => _id; - public int? GroupId - => _groupId; - public int Value - { - get { return _height; } - set - { - _height = value; - OnPropertyChanged(); - } - } - - public event PropertyChangedEventHandler PropertyChanged; - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - - public override string ToString() - => _groupId == null ? - $"{Id}, value={Value}, _alloc={_allocId}" : - $"{Id}, group={GroupId}, value={Value}, _alloc={_allocId}"; - - ~Item() - { - Interlocked.Increment(ref __counter.ItemFree); - } - } - - interface IItemList : IEnumerable, IDisposable - { - void UpdateHeights(double multipule); - int Count { get; } - void Dispose(); - Item this[int index] { get; } - } - - [Preserve(AllMembers = true)] - class LazyItemList : LazyReadOnlyList<Item>, IItemList - { - int _itemIdOffset; - int? _id; - int _count; - - internal LazyItemList(int count) - : this(null /* grouping disabled */, 0, count) { } - internal LazyItemList(int? id, int itemIdOffset, int count) - : base(count, DefaultItemHeight, - onAsk: o => __counter.ViewModelAsk.Add(o), - activate: (self, subIndex, height) => - Item.Create( - list: (LazyItemList)self, - index: subIndex, - height: (int)height - ) - ) - { - _id = id; - _itemIdOffset = itemIdOffset; - _count = count; - } - - protected int Context - { - get { return (int)base.Context; } - set { base.Context = value; } - } - - public void UpdateHeights(double multipule) - { - if (multipule < 1 && Context < MinimumItemHeight) - return; - - Context = (int)(Context * multipule); - - foreach (var weakItem in WeakItems) - { - Item item; - if (!(weakItem.TryGetTarget(out item))) - continue; - - item.Value = Context; - } - } - public int ItemIdOffset - => _itemIdOffset; - public int? Id - => _id; - - public void Dispose() - { - foreach (var weakItem in WeakItems) - weakItem.SetTarget(null); - } - - public override string ToString() - => $"{_id}"; - } - - [Preserve(AllMembers = true)] - class LazyGroupedItemList : LazyReadOnlyList<LazyItemList>, IItemList - { - int _itemsPerGroup; - - internal LazyGroupedItemList(int numberOfGroups, int count) - : base(numberOfGroups, - context: null, - onAsk: o => __counter.ViewModelAsk.Add(o), - activate: (self, groupId, context) => - new LazyItemList( - id: groupId, - itemIdOffset: groupId * (count / numberOfGroups), - count: count / numberOfGroups - ) - ) - { _itemsPerGroup = count / numberOfGroups; } - - Item IItemList.this[int index] - { - get - { - var group = index / _itemsPerGroup; - index = index % _itemsPerGroup; - return this[group][index]; - } - } - - public void UpdateHeights(double multipule) - { - foreach (var weakItem in WeakItems) - { - LazyItemList group; - if (!(weakItem.TryGetTarget(out group))) - continue; - - group.UpdateHeights(multipule); - } - } - - public void Dispose() - { - foreach (var weakItem in WeakItems) - { - LazyItemList group; - if (!(weakItem.TryGetTarget(out group))) - continue; - - group.Dispose(); - } - } - } - - [Preserve(AllMembers = true)] - class SnapShot - { - Counter _counter; - - internal int Views; - internal int Attached; - internal int Binds; - internal int Items; - internal int Asks; - - public SnapShot(Counter counter) - { - _counter = counter; - - Views = _counter.Views; - Attached = _counter.AttachedCells; - Binds = _counter.CellBind; - Items = _counter.Items; - Asks = _counter.OnSelectTemplate; - } - - void Subtract() - { - Views = _counter.Views - Views; - Attached = _counter.AttachedCells - Attached; - Binds = _counter.CellBind - Binds; - Items = _counter.Items - Items; - Asks = _counter.OnSelectTemplate - Asks; - } - - public void Update() - => Subtract(); - } - - [Preserve(AllMembers = true)] - class Counter - { - internal int CellAlloc; - internal int CellFree; - internal int CellBind; - - internal int ItemAlloc; - internal int ItemFree; - internal int OnSelectTemplate; - internal HashSet<int> ViewModelAsk - = new HashSet<int>(); - - HashSet<int> CellAttached - = new HashSet<int>(); - - internal int AttachedCells - { - get - { - lock (this) - return CellAttached.Count; - } - } - internal void AttachCell(int id) - { - lock (this) - CellAttached.Add(id); - } - internal void DetachCell(int id) - { - lock (this) - CellAttached.Remove(id); - } - - internal int Views => CellAlloc - CellFree; - internal int Items => ItemAlloc - ItemFree; - } - - [Preserve(AllMembers = true)] - class CounterView : StackLayout - { - static Label CreateLabel() - => new Label() { FontSize = CountFontSize }; - - internal void Update() - { - ViewCountLabel.Text - = $"View={__counter.Views}"; - AttachedCountLabel.Text - = $"Atch={__counter.AttachedCells}"; - BindCountLabel.Text - = $"Bind={__counter.CellBind}"; - ItemCountLabel.Text - = $"Item={__counter.Items}"; - AskLabel.Text - = $"Ask={__counter.OnSelectTemplate}"; - } - - Label AttachedCountLabel = CreateLabel(); - Label ViewCountLabel = CreateLabel(); - Label BindCountLabel = CreateLabel(); - Label ItemCountLabel = CreateLabel(); - Label AskLabel = CreateLabel(); - - internal CounterView() - { - Children.Add(ViewCountLabel); - Children.Add(AttachedCountLabel); - Children.Add(BindCountLabel); - Children.Add(ItemCountLabel); - Children.Add(AskLabel); - Update(); - } - } - - // Cell is activated via DataTemplate using default ctor which - // makes it difficult to pass the counter to the cell. So we make - // it static to give cell access and create a different generic - // instantiation for each type of ListView to get different counters - static Counter __counter = new Counter(); - - ListView _listView; - int _appeared; - int _disappeared; - IItemList _itemsList; - - public ListViewSpy() - { - __listViewSpyAlloc++; - - var name = GetType().Name; - - var hasUnevenRows = name.Contains("UnevenRows"); - - var isGrouped = name.Contains("Grouped"); - - _itemsList = isGrouped ? (IItemList) - new LazyGroupedItemList(GroupCount, ItemsCount) : - new LazyItemList(ItemsCount); - - var strategy = - name.Contains("RecycleElementAndDataTemplate") ? ListViewCachingStrategy.RecycleElementAndDataTemplate : - name.Contains("RecycleElement") ? ListViewCachingStrategy.RecycleElement : - ListViewCachingStrategy.RetainElement; - - var dataTemplate = - strategy == ListViewCachingStrategy.RecycleElement ? new Selector.SelectByData() : - strategy == ListViewCachingStrategy.RecycleElementAndDataTemplate ? new Selector.SelectByType() : - new DataTemplate(typeof(ItemViewCell.Constant)); - - _listView = new ListView(strategy) - { - HasUnevenRows = hasUnevenRows, - // see https://github.com/xamarin/Xamarin.Forms/pull/994/files - //RowHeight = 50, - ItemsSource = _itemsList, - ItemTemplate = dataTemplate, - - IsGroupingEnabled = isGrouped, - GroupDisplayBinding = null, - GroupShortNameBinding = null, - GroupHeaderTemplate = null - }; - Children.Add(_listView); - - _listView.AutomationId = $"__ListView"; - - var counter = new CounterView(); - - _listView.ItemAppearing += (o, e) => - { - _appeared = (e.Item as Item)?.Id ?? -1; - counter.Update(); - }; - - _listView.ItemDisappearing += (o, e) => - { - _disappeared = (e.Item as Item)?.Id ?? -1; - counter.Update(); - }; - - Children.Add(counter); - } - - void Scroll(int target) - { - var snapShot = new SnapShot(__counter); - _listView.ScrollTo(_itemsList[target], ScrollToPosition.MakeVisible, animated: true); - snapShot.Update(); - - // TEST - if (!_listView.IsGroupingEnabled && - _listView.CachingStrategy == ListViewCachingStrategy.RecycleElementAndDataTemplate) - { - if (snapShot.Attached > MaxAttachDelta) - throw new Exception($"Attached Delta: {snapShot.Attached}"); - if (snapShot.Views > MaxViewDelta) - throw new Exception($"Views Delta: {snapShot.Views}"); - if (snapShot.Asks > MaxAskDelta) - throw new Exception($"Asks Delta: {snapShot.Asks}"); - } - } - - internal override void Down() - { - var target = Math.Max(_appeared, _disappeared); - target += Math.Abs(_appeared - _disappeared) + MinScrollDelta; - if (target >= _itemsList.Count) - target = _itemsList.Count - 1; - - Scroll(target); - } - internal override void Up() - { - var target = Math.Min(_appeared, _disappeared); - target -= Math.Abs(_appeared - _disappeared) + MinScrollDelta; - if (target < 0) - target = 0; - - Scroll(target); - } - internal override void UpdateHeights(double multipule) - => _itemsList.UpdateHeights(multipule); - - internal override void Dispose() - => _itemsList.Dispose(); - - ~ListViewSpy() - { - __listViewSpyFree++; - } - } - - [Preserve(AllMembers = true)] - public abstract partial class ListViewSpy : StackLayout - { - [Preserve(AllMembers = true)] - internal sealed class Retain : - ListViewSpy<Retain> - { } - - [Preserve(AllMembers = true)] - internal sealed class UnevenRowsRecycleElement : - ListViewSpy<UnevenRowsRecycleElement> - { } - - [Preserve(AllMembers = true)] - internal sealed class UnevenRowsRecycleElementAndDataTemplate : - ListViewSpy<UnevenRowsRecycleElementAndDataTemplate> - { } - - [Preserve(AllMembers = true)] - internal sealed class EvenRowsRecycleElement : - ListViewSpy<EvenRowsRecycleElement> - { } - - [Preserve(AllMembers = true)] - internal sealed class EvenRowsRecycleElementAndDataTemplate : - ListViewSpy<EvenRowsRecycleElementAndDataTemplate> - { } - - [Preserve(AllMembers = true)] - internal sealed class GroupedRetain : - ListViewSpy<GroupedRetain> - { } - - [Preserve(AllMembers = true)] - internal sealed class GroupedUnevenRowsRecycleElement : - ListViewSpy<GroupedUnevenRowsRecycleElement> - { } - - [Preserve(AllMembers = true)] - internal sealed class GroupedUnevenRowsRecycleElementAndDataTemplate : - ListViewSpy<GroupedUnevenRowsRecycleElementAndDataTemplate> - { } - - [Preserve(AllMembers = true)] - internal sealed class GroupedEvenRowsRecycleElement : - ListViewSpy<GroupedEvenRowsRecycleElement> - { } - - [Preserve(AllMembers = true)] - internal sealed class GroupedEvenRowsRecycleElementAndDataTemplate : - ListViewSpy<GroupedEvenRowsRecycleElementAndDataTemplate> - { } - - internal abstract void Down(); - internal abstract void Up(); - internal abstract void UpdateHeights(double difference); - internal abstract void Dispose(); - } - - static int __listViewSpyAlloc; - static int __listViewSpyFree; - ListViewSpy[] __listViews; - - IEnumerable<ListViewSpy> ListViews() - => __listViews ?? Enumerable.Empty<ListViewSpy>(); - - void Update() - => Title = $"ListViews={__listViewSpyAlloc - __listViewSpyFree}"; - - Grid RecycleListViews(bool group = false) - { - // reclaim - foreach (var o in ListViews()) - o.Dispose(); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - - __listViews = group ? - new ListViewSpy[] - { - new ListViewSpy.GroupedRetain(), - new ListViewSpy.GroupedEvenRowsRecycleElement(), - new ListViewSpy.GroupedEvenRowsRecycleElementAndDataTemplate(), - new ListViewSpy.GroupedUnevenRowsRecycleElement(), - new ListViewSpy.GroupedUnevenRowsRecycleElementAndDataTemplate(), - } : - new ListViewSpy[] - { - new ListViewSpy.Retain(), - new ListViewSpy.EvenRowsRecycleElement(), - new ListViewSpy.EvenRowsRecycleElementAndDataTemplate(), - new ListViewSpy.UnevenRowsRecycleElement(), - new ListViewSpy.UnevenRowsRecycleElementAndDataTemplate(), - }; - - var grid = new Grid(); - foreach (var o in __listViews) - grid.Children.AddHorizontal(o); - - Update(); - - return grid; - } - - private class ButtonGird : Grid - { - Bugzilla52487 _test; - - internal ButtonGird(Bugzilla52487 test) - { - _test = test; - } - - private void ForEachListView(Action<ListViewSpy> onClick) - => _test.ListViews().ForEach(o => onClick(o)); - - public void Add(View view) - => Children.AddHorizontal(view); - - public Switch AddSwitch(Action<bool> onToggle) - { - var toggle = new Switch(); - toggle.Toggled += (o, s) => onToggle(s.Value); - Add(toggle); - return toggle; - } - - public Button AddButton(string text, Action onClick) - { - var button = new Button() { Text = text }; - button.Clicked += (o, s) => onClick(); - Add(button); - return button; - } - - public Button AddButton(string text, Action<ListViewSpy> onClick) - => AddButton(text, () => ForEachListView(onClick)); - - public Entry AddEntry() - { - var entry = new Entry(); - Add(entry); - return entry; - } - } - - protected override void Init() - { - var listViewGrid = new ContentView(); - listViewGrid.Content = RecycleListViews(false); - - var buttonsGrid = new ButtonGird(this); - var more = buttonsGrid.AddButton("More", x => x.UpdateHeights(2)); - var less = buttonsGrid.AddButton($"Less", x => x.UpdateHeights(.5)); - var up = buttonsGrid.AddButton("Up", x => x.Up()); - var down = buttonsGrid.AddButton("Down", x => x.Down()); - var group = buttonsGrid.AddSwitch( - isGrouped => listViewGrid.Content = RecycleListViews(isGrouped)); - - Content = new StackLayout - { - Children = { - listViewGrid, - buttonsGrid, - } - }; - - Update(); - } - public static class Id - { - public static string Down = nameof(Down); - } -#if UITEST - [Test] - public void Bugzilla52487Test() - { - try - { - RunningApp.WaitForElement(Id.Down); - RunningApp.Screenshot("Down"); - - RunningApp.Tap(Id.Down); - } - - finally - { - RunningApp.Screenshot("Finally"); - } - } -#endif - } -}
\ No newline at end of file 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 a692c0d6..30aa67b4 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 @@ -212,7 +212,6 @@ <Compile Include="$(MSBuildThisFileDirectory)Bugzilla54649.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla56609.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla55912.cs" /> - <Compile Include="$(MSBuildThisFileDirectory)Bugzilla52487.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla57317.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla57114.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla57515.cs" /> |