Skip to content

Commit

Permalink
feat(itemscontrol): Sync Items with ItemsSource
Browse files Browse the repository at this point in the history
Ensure Items collection is updated to reflect ItemsSource.
  • Loading branch information
davidjohnoliver committed Nov 26, 2020
1 parent 0604e46 commit 26cf7ab
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ internal static async Task WaitForIdle()
/// <summary>
/// Waits for <paramref name="element"/> to be loaded and measured in the visual tree.
/// </summary>
/// <remarks>On UWP, <see cref="WaitForIdle"/> may not always wait long enough for the control to be properly measured.</remarks>
/// <remarks>
/// On UWP, <see cref="WaitForIdle"/> may not always wait long enough for the control to be properly measured.
///
/// This method assumes that the control will have a non-zero size once loaded, so it's not appropriate for elements that are
/// collapsed, empty, etc.
/// </remarks>
internal static async Task WaitForLoaded(FrameworkElement element)
{
await WaitFor(IsLoaded, message: $"{element} loaded");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Private.Infrastructure;
using Windows.Foundation.Collections;
#if NETFX_CORE
using Uno.UI.Extensions;
#elif __IOS__
Expand All @@ -18,12 +19,13 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using static Private.Infrastructure.TestServices;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls
{
[TestClass]
[Ignore("Items-ItemsSource sync not implemented yet")]
[RunsOnUIThread]
public partial class Given_ListViewBase_Items
{
[TestMethod]
Expand Down Expand Up @@ -173,7 +175,7 @@ public async Task When_ItemsSource_Resets_ItemsCollection_Reference_Does_Not_Cha

[TestMethod]
[RunsOnUIThread]
public async Task When_ItemsSource_ObservableCollection_Modified_VectorChange_Not_Triggered()
public async Task When_ItemsSource_ObservableCollection_Modified_VectorChange_Triggered()
{
var listView = new ListView();
var items = new ObservableCollection<int>() { 1 };
Expand Down Expand Up @@ -285,5 +287,194 @@ public async Task When_ItemsSource_ICollection_Items_Do_Not_Sync()
items.Add(2, "World");
Assert.AreEqual(1, listView.Items.Count);
}

[TestMethod]
public void When_ItemsSource_Is_CollectionViewSource_Ungrouped_Observable()
{
var listView = new ListView();
var source = new ObservableCollection<int>(Enumerable.Range(0, 10));

var cvs = new CollectionViewSource { Source = source };
listView.ItemsSource = cvs.View;

Assert.AreEqual(10, listView.Items.Count);

var timesRaised = 0;
CollectionChange change = default;
var index = -1;
listView.Items.VectorChanged += (o, e) =>
{
timesRaised++; //1
change = e.CollectionChange; //insert
index = (int)e.Index;
};

source.Add(10);

#if NETFX_CORE // CollectionView doesn't implement VectorChanged
Assert.AreEqual(11, listView.Items.Count);
Assert.AreEqual(1, timesRaised);
Assert.AreEqual(CollectionChange.ItemInserted, change);
Assert.AreEqual(10, index);
#endif
}

[TestMethod]
public void When_ItemsSource_Grouped_Simple()
{
var listView = new ListView();
var source = CountriesABC.GroupBy(s => s[0].ToString()).ToArray();

var cvs = new CollectionViewSource
{
Source = source,
IsSourceGrouped = true
};
listView.ItemsSource = cvs.View;

Assert.AreEqual(CountriesABC.Length, listView.Items.Count);

var sourceFlattened = source.SelectMany(g => g).ToArray();
CollectionAssert.AreEqual(sourceFlattened, listView.Items.ToArray());
}

[TestMethod]
public void When_ItemsSource_Grouped_Observables_Inner_Groups_Modified()
{
var listView = new ListView();
var source = GetGroupedObservable(CountriesABC, s => s[0].ToString());

var cvs = new CollectionViewSource
{
Source = source,
IsSourceGrouped = true
};
listView.ItemsSource = cvs.View;

Assert.AreEqual(CountriesABC.Length, listView.Items.Count);

var timesRaised = 0;
CollectionChange change = default;
var index = -1;
listView.Items.VectorChanged += (o, e) =>
{
timesRaised++; //1
change = e.CollectionChange; //insert
index = (int)e.Index; //11
};

var a = source.Single(g => g.Key == "A");
a.Add("Arendelle");

#if NETFX_CORE // CollectionView doesn't implement VectorChanged
Assert.AreEqual(CountriesABC.Length + 1, listView.Items.Count);
Assert.AreEqual(1, timesRaised);
Assert.AreEqual(CollectionChange.ItemInserted, change);
Assert.AreEqual(11, index);
#endif
}

[TestMethod]
public void When_ItemsSource_Grouped_Observables_Outer_Modified()
{
var listView = new ListView();
var source = GetGroupedObservable(CountriesABC, s => s[0].ToString());

var cvs = new CollectionViewSource
{
Source = source,
IsSourceGrouped = true
};
listView.ItemsSource = cvs.View;

Assert.AreEqual(CountriesABC.Length, listView.Items.Count);

var timesRaised = 0;
var changeWasExpected = true;
var expectedIndex = 49;
var indexWasExpected = true;
listView.Items.VectorChanged += (o, e) =>
{
timesRaised++;
changeWasExpected &= e.CollectionChange == CollectionChange.ItemInserted;
var index = (int)e.Index; //49, 48, 47, 46
indexWasExpected &= index == expectedIndex;
expectedIndex--;
};

var d = new GroupingObservableCollection<string, string>("D", CountriesD);
source.Add(d);

#if NETFX_CORE // CollectionView doesn't implement VectorChanged
Assert.AreEqual(CountriesABC.Length + CountriesD.Length, listView.Items.Count);
Assert.AreEqual(4, timesRaised);
Assert.IsTrue(changeWasExpected);
Assert.IsTrue(indexWasExpected);
#endif
}

private static ObservableCollection<GroupingObservableCollection<TKey, TElement>> GetGroupedObservable<TKey, TElement>(IEnumerable<TElement> source, Func<TElement, TKey> keySelector)
{
var observables = source.GroupBy(keySelector).Select(g => new GroupingObservableCollection<TKey, TElement>(g.Key, g));
return new ObservableCollection<GroupingObservableCollection<TKey, TElement>>(observables);
}

private readonly string[] CountriesABC = new[]
{
"ALGERIA",
"ANDORRA",
"AZERBAIJAN",
"BRUNEI",
"CENTRAL AFRICAN REPUBLIC",
"CHINA",
"BELIZE",
"COLOMBIA",
"BRAZIL",
"CONGO, REPUBLIC OF THE",
"BARBADOS",
"BELGIUM",
"ARGENTINA",
"BURUNDI",
"AUSTRALIA",
"BANGLADESH",
"BOTSWANA",
"CUBA",
"AFGHANISTAN",
"CONGO, DEMOCRATIC REPUBLIC OF THE",
"ANGOLA",
"BAHRAIN",
"BELARUS",
"COMOROS",
"BAHAMAS, THE",
"CHAD",
"CYPRUS",
"CANADA",
"BURKINA FASO",
"CAMBODIA",
"BENIN",
"CZECH REPUBLIC",
"CABO VERDE",
"ANTIGUA AND BARBUDA",
"COSTA RICA",
"CHILE",
"BOSNIA AND HERZEGOVINA",
"BULGARIA",
"BOLIVIA",
"BHUTAN",
"ALBANIA",
"AUSTRIA",
"CÔTE D'IVOIRE ",
"CAMEROON",
"ARMENIA",
"CROATIA",
};

private readonly string[] CountriesD = new[]
{
"DENMARK",
"DJIBOUTI",
"DOMINICA",
"DOMINICAN REPUBLIC"
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,9 @@ public async Task When_CollectionViewSource_In_Xaml()

await WindowHelper.WaitForIdle();

//Assert.AreEqual(3, page.SubjectListView.Items.Count); // TODO
#if NETFX_CORE // TODO: subscribe to changes to Source property
Assert.AreEqual(3, page.SubjectListView.Items.Count);
#endif
ListViewItem lvi = null;
await WindowHelper.WaitFor(() => (lvi = page.SubjectListView.ContainerFromItem("One") as ListViewItem) != null);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls
{
internal class GroupingObservableCollection<TKey, TElement> : ObservableCollection<TElement>, IGrouping<TKey, TElement>
{
public TKey Key { get; }

public GroupingObservableCollection(TKey key) : base()
{
Key = key;
}

public GroupingObservableCollection(TKey key, IEnumerable<TElement> collection) : base(collection)
{
Key = key;
}
}
}
Loading

0 comments on commit 26cf7ab

Please sign in to comment.