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

Commit

Permalink
Change bindable layout to handle collectionchanged on main thread.
Browse files Browse the repository at this point in the history
  • Loading branch information
GalaxiaGuy committed Oct 26, 2021
1 parent 70e311b commit 9e6991f
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 8 deletions.
139 changes: 139 additions & 0 deletions Xamarin.Forms.Core.UnitTests/BindableLayoutTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;

namespace Xamarin.Forms.Core.UnitTests
Expand Down Expand Up @@ -139,6 +140,144 @@ public void TracksClear()
Assert.IsTrue(IsLayoutWithItemsSource(itemsSource, layout));
}

[Test]
public async Task TracksAddOnBackgroundThread()
{
var invokeOnMainThreadWasCalled = false;
Device.PlatformServices = new MockPlatformServices(x => invokeOnMainThreadWasCalled = true, isInvokeRequired: true);
var layout = new StackLayout
{
IsPlatformEnabled = true,
};

var itemsSource = new ObservableCollection<int>();
BindableLayout.SetItemsSource(layout, itemsSource);

invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource.Add(1));
Assert.IsTrue(invokeOnMainThreadWasCalled);
}

[Test]
public async Task TracksInsertOnBackgroundThread()
{
var invokeOnMainThreadWasCalled = false;
Device.PlatformServices = new MockPlatformServices(x => invokeOnMainThreadWasCalled = true, isInvokeRequired: true);
var layout = new StackLayout
{
IsPlatformEnabled = true,
};

var itemsSource = new ObservableCollection<int>() { 0, 1, 2, 3, 4 };
BindableLayout.SetItemsSource(layout, itemsSource);

invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource.Insert(2, 5));
Assert.IsTrue(invokeOnMainThreadWasCalled);
}

[Test]
public async Task TracksRemoveOnBackgroundThread()
{
var invokeOnMainThreadWasCalled = false;
Device.PlatformServices = new MockPlatformServices(x => invokeOnMainThreadWasCalled = true, isInvokeRequired: true);
var layout = new StackLayout
{
IsPlatformEnabled = true,
};

var itemsSource = new ObservableCollection<int>() { 0, 1 };
BindableLayout.SetItemsSource(layout, itemsSource);

invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource.RemoveAt(0));
Assert.IsTrue(invokeOnMainThreadWasCalled);
invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource.Remove(1));
Assert.IsTrue(invokeOnMainThreadWasCalled);
}

[Test]
public async Task TracksRemoveAllOnBackgroundThread()
{
var invokeOnMainThreadWasCalled = false;
Device.PlatformServices = new MockPlatformServices(x => invokeOnMainThreadWasCalled = true, isInvokeRequired: true);
var layout = new StackLayout
{
IsPlatformEnabled = true,
};

var itemsSource = new ObservableRangeCollection<int>(Enumerable.Range(0, 10));
BindableLayout.SetItemsSource(layout, itemsSource);

invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource.RemoveAll());
Assert.IsTrue(invokeOnMainThreadWasCalled);
}

[Test]
public async Task TracksReplaceOnBackgroundThread()
{
var invokeOnMainThreadWasCalled = false;
Device.PlatformServices = new MockPlatformServices(x => invokeOnMainThreadWasCalled = true, isInvokeRequired: true);
var layout = new StackLayout
{
IsPlatformEnabled = true,
};

var itemsSource = new ObservableCollection<int>() { 0, 1, 2 };
BindableLayout.SetItemsSource(layout, itemsSource);

invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource[0] = 3);
Assert.IsTrue(invokeOnMainThreadWasCalled);
invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource[1] = 4);
Assert.IsTrue(invokeOnMainThreadWasCalled);
invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource[2] = 5);
Assert.IsTrue(invokeOnMainThreadWasCalled);
}

[Test]
public async Task TracksMoveOnBackgroundThread()
{
var invokeOnMainThreadWasCalled = false;
Device.PlatformServices = new MockPlatformServices(x => invokeOnMainThreadWasCalled = true, isInvokeRequired: true);
var layout = new StackLayout
{
IsPlatformEnabled = true,
};

var itemsSource = new ObservableCollection<int>() { 0, 1 };
BindableLayout.SetItemsSource(layout, itemsSource);

invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource.Move(0, 1));
Assert.IsTrue(invokeOnMainThreadWasCalled);
invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource.Move(1, 0));
Assert.IsTrue(invokeOnMainThreadWasCalled);
}

[Test]
public async Task TracksClearOnBackgroundThread()
{
var invokeOnMainThreadWasCalled = false;
Device.PlatformServices = new MockPlatformServices(x => invokeOnMainThreadWasCalled = true, isInvokeRequired: true);
var layout = new StackLayout
{
IsPlatformEnabled = true,
};

var itemsSource = new ObservableCollection<int>() { 0, 1 };
BindableLayout.SetItemsSource(layout, itemsSource);

invokeOnMainThreadWasCalled = false;
await Task.Run(() => itemsSource.Clear());
Assert.IsTrue(invokeOnMainThreadWasCalled);
}

[Test]
public void TracksNull()
{
Expand Down
19 changes: 11 additions & 8 deletions Xamarin.Forms.Core/BindableLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,14 +297,17 @@ void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArg
return;
}

e.Apply(
insert: (item, index, _) => layout.Children.Insert(index, CreateItemView(item, layout)),
removeAt: (item, index) => layout.Children.RemoveAt(index),
reset: CreateChildren);

// UpdateEmptyView is called from within CreateChildren, therefor skip it for Reset
if (e.Action != NotifyCollectionChangedAction.Reset)
UpdateEmptyView(layout);
Device.BeginInvokeOnMainThread(() =>
{
e.Apply(
insert: (item, index, _) => layout.Children.Insert(index, CreateItemView(item, layout)),
removeAt: (item, index) => layout.Children.RemoveAt(index),
reset: CreateChildren);
// UpdateEmptyView is called from within CreateChildren, therefor skip it for Reset
if (e.Action != NotifyCollectionChangedAction.Reset)
UpdateEmptyView(layout);
});
}
}
}

0 comments on commit 9e6991f

Please sign in to comment.