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

Commit

Permalink
[iOS] Allow observable source update while CollectionView is not visi…
Browse files Browse the repository at this point in the history
…ble (#13678) fixes #13126

* Allow item source updates while CollectionView is hidden; prevent
CollectionView updates while CollectionView is hidden;
fixes #13126

* Fix bug where measurement cell content is applied to the wrong cell
Only force layout on transition from invisible to visible
Move cell size cache to layout and clear cache on size change (e.g., rotation)
Use ItemsView size for constraints when possible;

* Add test for Reset situation; fix bug with Reset before CollectionView is visible;
  • Loading branch information
hartez authored Feb 9, 2021
1 parent 7ea5159 commit a10c2c6
Show file tree
Hide file tree
Showing 9 changed files with 377 additions and 60 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;

#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif

namespace Xamarin.Forms.Controls.Issues
{
[Issue(IssueTracker.Github, 13126, "[Bug] Regression: 5.0.0-pre5 often fails to draw dynamically loaded collection view content", PlatformAffected.iOS)]
#if UITEST
[NUnit.Framework.Category(UITestCategories.CollectionView)]
#endif
public class Issue13126 : TestContentPage
{
_13126VM _vm;
const string Success = "Success";

protected override void Init()
{
var collectionView = BindingWithConverter();

var grid = new Grid
{
RowDefinitions = new RowDefinitionCollection
{
new RowDefinition() { Height = GridLength.Star },
}
};

grid.Children.Add(collectionView);

Content = grid;

_vm = new _13126VM();
BindingContext = _vm;
}

protected async override void OnParentSet()
{
base.OnParentSet();
_vm.IsBusy = true;

await Task.Delay(1000);

_vm.Data.Add(Success);

_vm.IsBusy = false;
}

internal static CollectionView BindingWithConverter()
{
var cv = new CollectionView
{
IsVisible = true,

ItemTemplate = new DataTemplate(() =>
{
var label = new Label();
label.SetBinding(Label.TextProperty, new Binding("."));
return label;
})
};

cv.EmptyView = new Label { Text = "Should not see me" };

cv.SetBinding(CollectionView.ItemsSourceProperty, new Binding("Data"));
cv.SetBinding(VisualElement.IsVisibleProperty, new Binding("IsBusy", converter: new BoolInverter()));

return cv;
}

class BoolInverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !((bool)value);
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

internal class _13126VM : INotifyPropertyChanged
{
private bool _isBusy;

public bool IsBusy
{
get
{
return _isBusy;
}

set
{
_isBusy = value;
OnPropertyChanged(nameof(IsBusy));
}
}

public OptimizedObservableCollection<string> Data { get; } = new OptimizedObservableCollection<string>();

public event PropertyChangedEventHandler PropertyChanged;

void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}

internal class OptimizedObservableCollection<T> : ObservableCollection<T>
{
bool _shouldRaiseNotifications = true;

public OptimizedObservableCollection()
{
}

public OptimizedObservableCollection(IEnumerable<T> collection)
: base(collection)
{
}

public IDisposable BeginMassUpdate()
{
return new MassUpdater(this);
}

protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (_shouldRaiseNotifications)
base.OnCollectionChanged(e);
}

protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (_shouldRaiseNotifications)
base.OnPropertyChanged(e);
}

class MassUpdater : IDisposable
{
readonly OptimizedObservableCollection<T> parent;
public MassUpdater(OptimizedObservableCollection<T> parent)
{
this.parent = parent;
parent._shouldRaiseNotifications = false;
}

public void Dispose()
{
parent._shouldRaiseNotifications = true;
parent.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
parent.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
parent.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}

#if UITEST
[Test]
public void CollectionViewShouldSourceShouldUpdateWhileInvisible()
{
RunningApp.WaitForElement(Success);
}
#endif
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using static Xamarin.Forms.Controls.Issues.Issue13126;

#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif

namespace Xamarin.Forms.Controls.Issues
{
[Issue(IssueTracker.Github, 13126, "[Bug] Regression: 5.0.0-pre5 often fails to draw dynamically loaded collection view content",
PlatformAffected.iOS, issueTestNumber:1)]
#if UITEST
[NUnit.Framework.Category(UITestCategories.CollectionView)]
#endif
public class Issue13126_2 : TestContentPage
{
_13126VM _vm;
const string Success = "Success";

protected override void Init()
{
var collectionView = BindingWithConverter();

var grid = new Grid
{
RowDefinitions = new RowDefinitionCollection
{
new RowDefinition() { Height = GridLength.Star },
}
};

grid.Children.Add(collectionView);

Content = grid;

_vm = new _13126VM();
BindingContext = _vm;
}

protected async override void OnParentSet()
{
base.OnParentSet();
_vm.IsBusy = true;

await Task.Delay(1000);

using (_vm.Data.BeginMassUpdate())
{
_vm.Data.Add(Success);
}

_vm.IsBusy = false;
}

#if UITEST
[Test]
public void CollectionViewShouldSourceShouldResetWhileInvisible()
{
RunningApp.WaitForElement(Success);
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
<Compile Include="$(MSBuildThisFileDirectory)CollectionViewGroupTypeIssue.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue11214.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue13109.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue13126.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue13126_2.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue13551.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RadioButtonTemplateFromStyle.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellWithCustomRendererDisabledAnimations.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,18 @@ void UpdateVisualStates()

_oldViews = newViews;
}

protected internal override void UpdateVisibility()
{
if (ItemsView.IsVisible)
{
CollectionView.Hidden = false;
}
else
{
CollectionView.Hidden = true;
}
}
}

class CarouselViewLoopManager : IDisposable
Expand Down
Loading

0 comments on commit a10c2c6

Please sign in to comment.