summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core.UnitTests/ListViewTests.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Core.UnitTests/ListViewTests.cs')
-rw-r--r--Xamarin.Forms.Core.UnitTests/ListViewTests.cs1490
1 files changed, 1490 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core.UnitTests/ListViewTests.cs b/Xamarin.Forms.Core.UnitTests/ListViewTests.cs
new file mode 100644
index 00000000..d5672f77
--- /dev/null
+++ b/Xamarin.Forms.Core.UnitTests/ListViewTests.cs
@@ -0,0 +1,1490 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using NUnit.Framework;
+
+namespace Xamarin.Forms.Core.UnitTests
+{
+ [TestFixture]
+ public class ListViewTests : BaseTestFixture
+ {
+ [TearDown]
+ public override void TearDown()
+ {
+ base.TearDown ();
+ Device.PlatformServices = null;
+ Device.Info = null;
+ }
+
+ [SetUp]
+ public override void Setup ()
+ {
+ base.Setup ();
+ Device.PlatformServices = new MockPlatformServices ();
+ Device.Info = new TestDeviceInfo ();
+ }
+
+ [Test]
+ public void TestConstructor ()
+ {
+ var listView = new ListView ();
+
+ Assert.Null (listView.ItemsSource);
+ Assert.Null (listView.ItemTemplate);
+ Assert.AreEqual (LayoutOptions.FillAndExpand, listView.HorizontalOptions);
+ Assert.AreEqual (LayoutOptions.FillAndExpand, listView.VerticalOptions);
+ }
+
+ internal class ListItem
+ {
+ public string Name { get; set; }
+ public string Description { get; set; }
+ }
+
+ [Test]
+ public void TestTemplating ()
+ {
+ var cellTemplate = new DataTemplate (typeof (TextCell));
+ cellTemplate.SetBinding (TextCell.TextProperty, new Binding ("Name"));
+ cellTemplate.SetBinding (TextCell.DetailProperty, new Binding ("Description"));
+
+ var listView = new ListView {
+ ItemsSource = new[] {
+ new ListItem {Name = "Foo", Description = "Bar"},
+ new ListItem {Name = "Baz", Description = "Raz"}
+ },
+ ItemTemplate = cellTemplate
+ };
+
+ var cell = (Cell)listView.ItemTemplate.CreateContent ();
+
+ var textCell = (TextCell)cell;
+ cell.BindingContext = listView.ItemsSource.OfType<ListItem> ().First ();
+
+ Assert.AreEqual ("Foo", textCell.Text);
+ Assert.AreEqual ("Bar", textCell.Detail);
+ }
+
+ [Test]
+ public void TemplateNullObject()
+ {
+ var listView = new ListView {
+ ItemsSource = new object[] {
+ null
+ }
+ };
+
+ Cell cell = listView.TemplatedItems[0];
+
+ Assert.That (cell, Is.Not.Null);
+ Assert.That (cell, Is.InstanceOf<TextCell>());
+ Assert.That (((TextCell) cell).Text, Is.Null);
+ }
+
+ [Test]
+ [Description ("Setting GroupDisplayBinding or GroupHeaderTemplate when the other is set should set the other one to null.")]
+ public void SettingGroupHeaderTemplateSetsDisplayBindingToNull()
+ {
+ var listView = new ListView {
+ GroupDisplayBinding = new Binding ("Path")
+ };
+
+ listView.GroupHeaderTemplate = new DataTemplate (typeof (TextCell));
+
+ Assert.That (listView.GroupDisplayBinding, Is.Null);
+ }
+
+ [Test]
+ [Description ("Setting GroupDisplayBinding or GroupHeaderTemplate when the other is set should set the other one to null.")]
+ public void SettingGroupDisplayBindingSetsHeaderTemplateToNull()
+ {
+ var listView = new ListView {
+ GroupHeaderTemplate = new DataTemplate (typeof (TextCell))
+ };
+
+ listView.GroupDisplayBinding = new Binding ("Path");
+
+ Assert.That (listView.GroupHeaderTemplate, Is.Null);
+ }
+
+ [Test]
+ [Description ("You should be able to set ItemsSource without having set the other properties first without issue")]
+ public void SettingItemsSourceWithoutBindingsOrItemsSource()
+ {
+ var listView = new ListView {
+ IsGroupingEnabled = true
+ };
+
+ Assert.That (() => listView.ItemsSource = new[] { new[] { new object() } }, Throws.Nothing);
+ }
+
+ [Test]
+ public void DefaultGroupHeaderTemplates()
+ {
+ var items = new[] { new[] { new object() } };
+
+ var listView = new ListView {
+ IsGroupingEnabled = true,
+ ItemsSource = items
+ };
+
+ var til = (TemplatedItemsList<ItemsView<Cell>, Cell>)((IList)listView.TemplatedItems)[0];
+ Cell cell = til.HeaderContent;
+
+ Assert.That (cell, Is.Not.Null);
+ Assert.That (cell, Is.InstanceOf<TextCell>());
+ Assert.That (((TextCell) cell).Text, Is.EqualTo (items[0].ToString()));
+ }
+
+ [Test]
+ [Description ("Tapping a different item (row) that is equal to the current item selection should still raise ItemSelected")]
+ public void NotifyRowTappedDifferentIndex()
+ {
+ string item = "item";
+
+ var listView = new ListView {
+ ItemsSource = new[] {
+ item,
+ item
+ }
+ };
+
+ listView.NotifyRowTapped (0);
+
+ bool raised = false;
+ listView.ItemSelected += (sender, arg) => raised = true;
+
+ listView.NotifyRowTapped (1);
+ Assert.That (raised, Is.True, "ItemSelected was not raised");
+ }
+
+ [Test]
+ public void DoesNotCrashWhenAddingToSource ()
+ {
+ var items = new ObservableCollection<string> {
+ "Foo",
+ "Bar",
+ "Baz"
+ };
+
+ var listView = new ListView {
+ ItemsSource = items,
+ ItemTemplate = new DataTemplate(typeof(TextCell))
+ };
+
+ Assert.DoesNotThrow (() => items.Add ("Blah"));
+ }
+
+ [Test]
+ public void DoesNotThrowWhenMovingInSource ()
+ {
+ var items = new ObservableCollection<string> {
+ "Foo",
+ "Bar",
+ "Baz"
+ };
+
+ var listView = new ListView {
+ ItemsSource = items,
+ ItemTemplate = new DataTemplate (typeof (TextCell))
+ };
+
+ Assert.DoesNotThrow (() => items.Move (0, 1));
+ }
+
+ [Test]
+ [Description ("A cell being tapped from the UI should raise both tapped events, but not change ItemSelected")]
+ public void NotifyTappedSameItem()
+ {
+ int cellTapped = 0;
+ int itemTapped = 0;
+ int itemSelected = 0;
+
+ var listView = new ListView {
+ ItemsSource = new[] { "item" },
+ ItemTemplate = new DataTemplate (() => {
+ var cell = new TextCell();
+ cell.Tapped += (s, e) => {
+ cellTapped++;
+ };
+ return cell;
+ })
+ };
+
+ listView.ItemTapped += (sender, arg) => itemTapped++;
+ listView.ItemSelected += (sender, arg) => itemSelected++;
+
+ listView.NotifyRowTapped (0);
+
+ Assert.That (cellTapped, Is.EqualTo (1), "Cell.Tapped was not raised");
+ Assert.That (itemTapped, Is.EqualTo (1), "ListView.ItemTapped was not raised");
+ Assert.That (itemSelected, Is.EqualTo (1), "ListView.ItemSelected was not raised");
+
+ listView.NotifyRowTapped (0);
+
+ Assert.That (cellTapped, Is.EqualTo (2), "Cell.Tapped was not raised a second time");
+ Assert.That (itemTapped, Is.EqualTo (2), "ListView.ItemTapped was not raised a second time");
+ Assert.That (itemSelected, Is.EqualTo (1), "ListView.ItemSelected was raised a second time");
+ }
+
+ [Test]
+ public void ScrollTo()
+ {
+ var listView = new ListView {
+ IsPlatformEnabled = true,
+ Platform = new UnitPlatform()
+ };
+
+ object item = new object();
+
+ bool requested = false;
+ listView.ScrollToRequested += (sender, args) => {
+ requested = true;
+
+ Assert.That (args.Item, Is.SameAs (item));
+ Assert.That (args.Group, Is.Null);
+ Assert.That (args.Position, Is.EqualTo (ScrollToPosition.Center));
+ Assert.That (args.ShouldAnimate, Is.EqualTo (true));
+ };
+
+ listView.ScrollTo (item, ScrollToPosition.Center, animated: true);
+ Assert.That (requested, Is.True);
+ }
+
+ [Test]
+ public void ScrollToDelayed()
+ {
+ var listView = new ListView();
+
+ object item = new object();
+
+ bool requested = false;
+ listView.ScrollToRequested += (sender, args) => {
+ requested = true;
+
+ Assert.That (args.Item, Is.SameAs (item));
+ Assert.That (args.Group, Is.Null);
+ Assert.That (args.Position, Is.EqualTo (ScrollToPosition.Center));
+ Assert.That (args.ShouldAnimate, Is.EqualTo (true));
+ };
+
+ listView.ScrollTo (item, ScrollToPosition.Center, animated: true);
+ Assert.That (requested, Is.False);
+
+ listView.IsPlatformEnabled = true;
+ listView.Platform = new UnitPlatform();
+
+ Assert.That (requested, Is.True);
+ }
+
+ [Test]
+ public void ScrollToGroup()
+ {
+ // Fake a renderer so we pass along messages right away
+ var listView = new ListView {
+ IsPlatformEnabled = true,
+ Platform = new UnitPlatform(),
+ IsGroupingEnabled = true
+ };
+
+ object item = new object();
+ object group = new object();
+
+ bool requested = false;
+ listView.ScrollToRequested += (sender, args) => {
+ requested = true;
+
+ Assert.That (args.Item, Is.SameAs (item));
+ Assert.That (args.Group, Is.SameAs (group));
+ Assert.That (args.Position, Is.EqualTo (ScrollToPosition.Center));
+ Assert.That (args.ShouldAnimate, Is.EqualTo (true));
+ };
+
+ listView.ScrollTo (item, group, ScrollToPosition.Center, animated: true);
+ Assert.That (requested, Is.True);
+ }
+
+ [Test]
+ public void ScrollToInvalid()
+ {
+ var listView = new ListView {
+ IsPlatformEnabled = true,
+ Platform = new UnitPlatform()
+ };
+
+ Assert.That (() => listView.ScrollTo (new object(), (ScrollToPosition) 500, true), Throws.ArgumentException);
+ Assert.That (() => listView.ScrollTo (new object(), new object(), ScrollToPosition.Start, true), Throws.InvalidOperationException);
+
+ listView.IsGroupingEnabled = true;
+ Assert.That (() => listView.ScrollTo (new object(), new object(), (ScrollToPosition) 500, true), Throws.ArgumentException);
+ }
+
+ [Test]
+ public void GetSizeRequest ()
+ {
+ var listView = new ListView {
+ IsPlatformEnabled = true,
+ Platform = new UnitPlatform (),
+ HasUnevenRows = false,
+ RowHeight = 50,
+ ItemsSource = Enumerable.Range (0, 20).ToList ()
+ };
+
+
+ var sizeRequest = listView.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity);
+ Assert.AreEqual (40, sizeRequest.Minimum.Width);
+ Assert.AreEqual (40, sizeRequest.Minimum.Height);
+ Assert.AreEqual (50, sizeRequest.Request.Width);
+ Assert.AreEqual (50 * 20, sizeRequest.Request.Height);
+ }
+
+ [Test]
+ public void GetSizeRequestUneven ()
+ {
+ var listView = new ListView {
+ IsPlatformEnabled = true,
+ Platform = new UnitPlatform (),
+ HasUnevenRows = true,
+ RowHeight = 50,
+ ItemsSource = Enumerable.Range (0, 20).ToList ()
+ };
+
+
+ var sizeRequest = listView.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity);
+ Assert.AreEqual (40, sizeRequest.Minimum.Width);
+ Assert.AreEqual (40, sizeRequest.Minimum.Height);
+ Assert.AreEqual (50, sizeRequest.Request.Width);
+ Assert.AreEqual (100, sizeRequest.Request.Height);
+ }
+
+ public class ListItemValue : IComparable<ListItemValue>
+ {
+ public string Name { get; private set; }
+
+ public ListItemValue (string name)
+ {
+ Name = name;
+ }
+
+ int IComparable<ListItemValue>.CompareTo (ListItemValue value)
+ {
+ return Name.CompareTo (value.Name);
+ }
+
+ public string Label
+ {
+ get { return Name[0].ToString (); }
+ }
+ }
+
+ public class ListItemCollection : ObservableCollection<ListItemValue>
+ {
+ public string Title { get; private set; }
+
+ public ListItemCollection (string title)
+ {
+ Title = title;
+ }
+
+ public static List<ListItemValue> GetSortedData ()
+ {
+ var items = ListItems;
+ items.Sort ();
+ return items;
+ }
+
+ // Data used to populate our list.
+ static readonly List<ListItemValue> ListItems = new List<ListItemValue> () {
+ new ListItemValue ("Babbage"),
+ new ListItemValue ("Boole"),
+ new ListItemValue ("Berners-Lee"),
+ new ListItemValue ("Atanasoff"),
+ new ListItemValue ("Allen"),
+ new ListItemValue ("Cormack"),
+ new ListItemValue ("Cray"),
+ new ListItemValue ("Dijkstra"),
+ new ListItemValue ("Dix"),
+ new ListItemValue ("Dewey"),
+ new ListItemValue ("Erdos"),
+ };
+ }
+
+ public class TestCell : TextCell
+ {
+ public static int NumberOfCells = 0;
+
+ public TestCell ()
+ {
+ Interlocked.Increment (ref NumberOfCells);
+ }
+
+ ~TestCell ()
+ {
+ Interlocked.Decrement (ref NumberOfCells);
+ }
+ }
+
+ ObservableCollection<ListItemCollection> SetupList ()
+ {
+ var allListItemGroups = new ObservableCollection<ListItemCollection> ();
+
+ foreach (var item in ListItemCollection.GetSortedData ()) {
+ // Attempt to find any existing groups where theg group title matches the first char of our ListItem's name.
+ var listItemGroup = allListItemGroups.FirstOrDefault (g => g.Title == item.Label);
+
+ // If the list group does not exist, we create it.
+ if (listItemGroup == null) {
+ listItemGroup = new ListItemCollection (item.Label) { item };
+ allListItemGroups.Add (listItemGroup);
+ } else {
+ // If the group does exist, we simply add the demo to the existing group.
+ listItemGroup.Add (item);
+ }
+ }
+ return allListItemGroups;
+ }
+
+ [Test]
+ public void UncollectableHeaderReferences ()
+ {
+ var list = new ListView {
+ Platform = new UnitPlatform (),
+ IsPlatformEnabled = true,
+ ItemTemplate = new DataTemplate (typeof (TextCell)) {
+ Bindings = {
+ {TextCell.TextProperty, new Binding ("Name")}
+ }
+ },
+ GroupHeaderTemplate = new DataTemplate (typeof (TestCell)) {
+ Bindings = {
+ {TextCell.TextProperty, new Binding ("Title")}
+ }
+ },
+ IsGroupingEnabled = true,
+ ItemsSource = SetupList (),
+ };
+
+ Assert.AreEqual (5, TestCell.NumberOfCells);
+
+ var newList1 = SetupList ();
+ var newList2 = SetupList ();
+
+ for (var i = 0; i < 400; i++) {
+ list.ItemsSource = i % 2 > 0 ? newList1 : newList2;
+
+ // grab a header just so we can be sure its reailized
+ var header = list.TemplatedItems.GetGroup (0).HeaderContent;
+ }
+
+ GC.Collect ();
+ GC.WaitForPendingFinalizers ();
+
+ // use less or equal because mono will keep the last header var alive no matter what
+ Assert.True (TestCell.NumberOfCells <= 6);
+
+ var keepAlive = list.ToString ();
+ }
+
+ [Test]
+ public void CollectionChangedMultipleFires ()
+ {
+ var source = new ObservableCollection<string> {
+ "Foo",
+ "Bar"
+ };
+
+ var list = new ListView {
+ Platform = new UnitPlatform (),
+ IsPlatformEnabled = true,
+ ItemsSource = source,
+ ItemTemplate = new DataTemplate (typeof (TextCell))
+ };
+
+ int fireCount = 0;
+ list.TemplatedItems.CollectionChanged += (sender, args) => {
+ fireCount++;
+ };
+
+ source.Add ("Baz");
+
+ Assert.AreEqual (1, fireCount);
+ }
+
+ [Test]
+ public void GroupedCollectionChangedMultipleFires ()
+ {
+ var source = new ObservableCollection<ObservableCollection <string>> {
+ new ObservableCollection<string> {"Foo"},
+ new ObservableCollection<string> {"Bar"}
+ };
+
+ var list = new ListView {
+ Platform = new UnitPlatform (),
+ IsPlatformEnabled = true,
+ IsGroupingEnabled = true,
+ ItemsSource = source,
+ ItemTemplate = new DataTemplate (typeof (TextCell)) {
+ Bindings = {
+ {TextCell.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ int fireCount = 0;
+ list.TemplatedItems.GroupedCollectionChanged += (sender, args) => {
+ fireCount++;
+ };
+
+ source[0].Add ("Baz");
+
+ Assert.AreEqual (1, fireCount);
+ }
+
+ [Test]
+ public void HeaderAsView()
+ {
+ var label = new Label { Text = "header" };
+ var lv = new ListView {
+ Header = label
+ };
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.SameAs (label));
+ }
+
+ [Test]
+ public void HeaderTemplated()
+ {
+ var lv = new ListView {
+ Header = "header",
+ HeaderTemplate = new DataTemplate(typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.Not.Null);
+ Assert.That (controller.HeaderElement, Is.InstanceOf<Label>());
+ Assert.That (((Label) controller.HeaderElement).Text, Is.EqualTo (lv.Header));
+ }
+
+ [Test]
+ public void HeaderTemplateThrowsIfCell ()
+ {
+ var lv = new ListView ();
+
+ Assert.Throws<ArgumentException> (() => lv.HeaderTemplate = new DataTemplate (typeof (TextCell)));
+ }
+
+ [Test]
+ public void FooterTemplateThrowsIfCell ()
+ {
+ var lv = new ListView ();
+
+ Assert.Throws<ArgumentException> (() => lv.FooterTemplate = new DataTemplate (typeof (TextCell)));
+ }
+
+ [Test]
+ public void HeaderObjectTemplatedChanged()
+ {
+ var lv = new ListView {
+ Header = "header",
+ HeaderTemplate = new DataTemplate (typeof (Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changed = true;
+ };
+
+ lv.Header = "newheader";
+
+ Assert.That (changing, Is.False);
+ Assert.That (changed, Is.False);
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.Not.Null);
+ Assert.That (controller.HeaderElement, Is.InstanceOf<Label>());
+ Assert.That (((Label) controller.HeaderElement).Text, Is.EqualTo (lv.Header));
+ }
+
+ [Test]
+ public void HeaderViewChanged()
+ {
+ var lv = new ListView {
+ Header = new Label { Text = "header" }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changed = true;
+ };
+
+ Label label = new Label { Text = "header" };
+ lv.Header = label;
+
+ Assert.That (changing, Is.True);
+ Assert.That (changed, Is.True);
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.SameAs (label));
+ }
+
+
+ [Test]
+ public void HeaderTemplateChanged()
+ {
+ var lv = new ListView {
+ Header = "header",
+ HeaderTemplate = new DataTemplate(typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changed = true;
+ };
+
+ lv.HeaderTemplate = new DataTemplate (typeof (Entry)) {
+ Bindings = {
+ { Entry.TextProperty, new Binding (".") }
+ }
+ };
+
+ Assert.That (changing, Is.True);
+ Assert.That (changed, Is.True);
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.Not.Null);
+ Assert.That (controller.HeaderElement, Is.InstanceOf<Entry>());
+ Assert.That (((Entry) controller.HeaderElement).Text, Is.EqualTo (lv.Header));
+ }
+
+ [Test]
+ public void HeaderTemplateChangedNoObject()
+ {
+ var lv = new ListView {
+ HeaderTemplate = new DataTemplate(typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changed = true;
+ };
+
+ lv.HeaderTemplate = new DataTemplate (typeof (Entry)) {
+ Bindings = {
+ { Entry.TextProperty, new Binding (".") }
+ }
+ };
+
+ Assert.That (changing, Is.False);
+ Assert.That (changed, Is.False);
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.Null);
+ }
+
+ [Test]
+ public void HeaderNoTemplate()
+ {
+ var lv = new ListView {
+ Header = "foo"
+ };
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.Not.Null);
+ Assert.That (controller.HeaderElement, Is.InstanceOf<Label>());
+ Assert.That (((Label) controller.HeaderElement).Text, Is.EqualTo (lv.Header));
+ }
+
+ [Test]
+ public void HeaderChangedNoTemplate()
+ {
+ var lv = new ListView {
+ Header = "foo"
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changed = true;
+ };
+
+ lv.Header = "bar";
+
+ Assert.That (changing, Is.True);
+ Assert.That (changed, Is.True);
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.Not.Null);
+ Assert.That (controller.HeaderElement, Is.InstanceOf<Label>());
+ Assert.That (((Label) controller.HeaderElement).Text, Is.EqualTo (lv.Header));
+ }
+
+ [Test]
+ public void HeaderViewButTemplated()
+ {
+ var lv = new ListView {
+ Header = new Entry { Text = "foo" },
+ HeaderTemplate = new DataTemplate (typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding ("Text") }
+ }
+ }
+ };
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.Not.Null);
+ Assert.That (controller.HeaderElement, Is.InstanceOf<Label>());
+ Assert.That (((Label) controller.HeaderElement).Text, Is.EqualTo (((Entry)lv.Header).Text));
+ }
+
+ [Test]
+ public void HeaderTemplatedChangedToView()
+ {
+ var lv = new ListView {
+ Header = new Entry { Text = "foo" },
+ HeaderTemplate = new DataTemplate (typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding ("Text") }
+ }
+ }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changed = true;
+ };
+
+ lv.HeaderTemplate = null;
+
+ Assert.That (changing, Is.True);
+ Assert.That (changed, Is.True);
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.Not.Null);
+ Assert.That (controller.HeaderElement, Is.InstanceOf<Entry>());
+ Assert.That (((Entry) controller.HeaderElement).Text, Is.EqualTo (((Entry)lv.Header).Text));
+ }
+
+ [Test]
+ public void HeaderTemplatedSetToNull()
+ {
+ var lv = new ListView {
+ Header = "header",
+ HeaderTemplate = new DataTemplate(typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "HeaderElement")
+ changed = true;
+ };
+
+ lv.Header = null;
+
+ Assert.That (changing, Is.True);
+ Assert.That (changed, Is.True);
+
+ IListViewController controller = lv;
+ Assert.That (controller.HeaderElement, Is.Null);
+ }
+
+ [Test]
+ public void FooterAsView()
+ {
+ var label = new Label { Text = "footer" };
+ var lv = new ListView {
+ Footer = label
+ };
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.SameAs (label));
+ }
+
+ [Test]
+ public void FooterTemplated()
+ {
+ var lv = new ListView {
+ Footer = "footer",
+ FooterTemplate = new DataTemplate(typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.Not.Null);
+ Assert.That (controller.FooterElement, Is.InstanceOf<Label>());
+ Assert.That (((Label) controller.FooterElement).Text, Is.EqualTo (lv.Footer));
+ }
+
+ [Test]
+ public void FooterObjectTemplatedChanged()
+ {
+ var lv = new ListView {
+ Footer = "footer",
+ FooterTemplate = new DataTemplate (typeof (Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changed = true;
+ };
+
+ lv.Footer = "newfooter";
+
+ Assert.That (changing, Is.False);
+ Assert.That (changed, Is.False);
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.Not.Null);
+ Assert.That (controller.FooterElement, Is.InstanceOf<Label>());
+ Assert.That (((Label) controller.FooterElement).Text, Is.EqualTo (lv.Footer));
+ }
+
+ [Test]
+ public void FooterViewChanged()
+ {
+ var lv = new ListView {
+ Footer = new Label { Text = "footer" }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changed = true;
+ };
+
+ Label label = new Label { Text = "footer" };
+ lv.Footer = label;
+
+ Assert.That (changing, Is.True);
+ Assert.That (changed, Is.True);
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.SameAs (label));
+ }
+
+
+ [Test]
+ public void FooterTemplateChanged()
+ {
+ var lv = new ListView {
+ Footer = "footer",
+ FooterTemplate = new DataTemplate(typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changed = true;
+ };
+
+ lv.FooterTemplate = new DataTemplate (typeof (Entry)) {
+ Bindings = {
+ { Entry.TextProperty, new Binding (".") }
+ }
+ };
+
+ Assert.That (changing, Is.True);
+ Assert.That (changed, Is.True);
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.Not.Null);
+ Assert.That (controller.FooterElement, Is.InstanceOf<Entry>());
+ Assert.That (((Entry) controller.FooterElement).Text, Is.EqualTo (lv.Footer));
+ }
+
+ [Test]
+ public void FooterTemplateChangedNoObject()
+ {
+ var lv = new ListView {
+ FooterTemplate = new DataTemplate(typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changed = true;
+ };
+
+ lv.FooterTemplate = new DataTemplate (typeof (Entry)) {
+ Bindings = {
+ { Entry.TextProperty, new Binding (".") }
+ }
+ };
+
+ Assert.That (changing, Is.False);
+ Assert.That (changed, Is.False);
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.Null);
+ }
+
+ [Test]
+ public void FooterNoTemplate()
+ {
+ var lv = new ListView {
+ Footer = "foo"
+ };
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.Not.Null);
+ Assert.That (controller.FooterElement, Is.InstanceOf<Label>());
+ Assert.That (((Label) controller.FooterElement).Text, Is.EqualTo (lv.Footer));
+ }
+
+ [Test]
+ public void FooterChangedNoTemplate()
+ {
+ var lv = new ListView {
+ Footer = "foo"
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changed = true;
+ };
+
+ lv.Footer = "bar";
+
+ Assert.That (changing, Is.True);
+ Assert.That (changed, Is.True);
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.Not.Null);
+ Assert.That (controller.FooterElement, Is.InstanceOf<Label>());
+ Assert.That (((Label) controller.FooterElement).Text, Is.EqualTo (lv.Footer));
+ }
+
+ [Test]
+ public void FooterViewButTemplated()
+ {
+ var lv = new ListView {
+ Footer = new Entry { Text = "foo" },
+ FooterTemplate = new DataTemplate (typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding ("Text") }
+ }
+ }
+ };
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.Not.Null);
+ Assert.That (controller.FooterElement, Is.InstanceOf<Label>());
+ Assert.That (((Label) controller.FooterElement).Text, Is.EqualTo (((Entry)lv.Footer).Text));
+ }
+
+ [Test]
+ public void FooterTemplatedChangedToView()
+ {
+ var lv = new ListView {
+ Footer = new Entry { Text = "foo" },
+ FooterTemplate = new DataTemplate (typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding ("Text") }
+ }
+ }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changed = true;
+ };
+
+ lv.FooterTemplate = null;
+
+ Assert.That (changing, Is.True);
+ Assert.That (changed, Is.True);
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.Not.Null);
+ Assert.That (controller.FooterElement, Is.InstanceOf<Entry>());
+ Assert.That (((Entry) controller.FooterElement).Text, Is.EqualTo (((Entry)lv.Footer).Text));
+ }
+
+ [Test]
+ public void FooterTemplatedSetToNull()
+ {
+ var lv = new ListView {
+ Footer = "footer",
+ FooterTemplate = new DataTemplate(typeof(Label)) {
+ Bindings = {
+ { Label.TextProperty, new Binding (".") }
+ }
+ }
+ };
+
+ bool changed = false, changing = false;
+ lv.PropertyChanging += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changing = true;
+ };
+ lv.PropertyChanged += (sender, args) => {
+ if (args.PropertyName == "FooterElement")
+ changed = true;
+ };
+
+ lv.Footer = null;
+
+ Assert.That (changing, Is.True);
+ Assert.That (changed, Is.True);
+
+ IListViewController controller = lv;
+ Assert.That (controller.FooterElement, Is.Null);
+ }
+
+ [Test]
+ public void BeginRefresh()
+ {
+ var lv = new ListView();
+
+ bool refreshing = false;
+ lv.Refreshing += (sender, args) => {
+ refreshing = true;
+ };
+
+ lv.BeginRefresh();
+
+ Assert.That (refreshing, Is.True);
+ Assert.That (lv.IsRefreshing, Is.True);
+ }
+
+ [Test]
+ public void SendRefreshing()
+ {
+ var lv = new ListView();
+
+ bool refreshing = false;
+ lv.Refreshing += (sender, args) => {
+ refreshing = true;
+ };
+
+ IListViewController controller = lv;
+ controller.SendRefreshing();
+
+ Assert.That (refreshing, Is.True);
+ Assert.That (lv.IsRefreshing, Is.True);
+ }
+
+ [Test]
+ public void RefreshCommand()
+ {
+ var lv = new ListView();
+
+ bool commandExecuted = false;
+
+ Command refresh = new Command (() => commandExecuted = true);
+
+ lv.RefreshCommand = refresh;
+
+ IListViewController controller = lv;
+ controller.SendRefreshing();
+
+ Assert.That (commandExecuted, Is.True);
+ }
+
+ [TestCase (true)]
+ [TestCase (false)]
+ public void RefreshCommandCanExecute (bool initial)
+ {
+ var lv = new ListView { IsPullToRefreshEnabled = initial };
+
+ bool commandExecuted = false;
+
+ Command refresh = new Command (() => commandExecuted = true,
+ () => !initial);
+
+ lv.RefreshCommand = refresh;
+
+ Assert.That ((lv as IListViewController).RefreshAllowed, Is.EqualTo (!initial));
+ }
+
+ [TestCase (true)]
+ [TestCase (false)]
+ public void RefreshCommandCanExecuteChanges (bool initial)
+ {
+ var lv = new ListView { IsPullToRefreshEnabled = initial };
+
+ bool commandExecuted = false;
+
+ Command refresh = new Command (() => commandExecuted = true,
+ () => initial);
+
+ lv.RefreshCommand = refresh;
+
+ Assert.That ((lv as IListViewController).RefreshAllowed, Is.EqualTo (initial));
+
+ initial = !initial;
+ refresh.ChangeCanExecute();
+
+ Assert.That ((lv as IListViewController).RefreshAllowed, Is.EqualTo (initial));
+ }
+
+ [Test]
+ public void BeginRefreshDoesNothingWhenCannotExecute()
+ {
+ var lv = new ListView();
+
+ bool commandExecuted = false, eventFired = false;
+
+ lv.Refreshing += (sender, args) => eventFired = true;
+
+ Command refresh = new Command (() => commandExecuted = true,
+ () => false);
+
+ lv.RefreshCommand = refresh;
+ lv.BeginRefresh();
+
+ Assert.That (lv.IsRefreshing, Is.False);
+ Assert.That (eventFired, Is.False);
+ Assert.That (commandExecuted, Is.False);
+ }
+
+ [Test]
+ public void SendRefreshingDoesNothingWhenCannotExecute()
+ {
+ var lv = new ListView();
+
+ bool commandExecuted = false, eventFired = false;
+
+ lv.Refreshing += (sender, args) => eventFired = true;
+
+ Command refresh = new Command (() => commandExecuted = true,
+ () => false);
+
+ lv.RefreshCommand = refresh;
+
+ ((IListViewController)lv).SendRefreshing();
+
+ Assert.That (lv.IsRefreshing, Is.False);
+ Assert.That (eventFired, Is.False);
+ Assert.That (commandExecuted, Is.False);
+ }
+
+ [Test]
+ public void SettingIsRefreshingDoesntFireEvent()
+ {
+ var lv = new ListView();
+
+ bool refreshing = false;
+ lv.Refreshing += (sender, args) => {
+ refreshing = true;
+ };
+
+ lv.IsRefreshing = true;
+
+ Assert.That (refreshing, Is.False);
+ }
+
+ [Test]
+ public void EndRefresh()
+ {
+ var lv = new ListView { IsRefreshing = true };
+
+ Assert.That (lv.IsRefreshing, Is.True);
+
+ lv.EndRefresh();
+
+ Assert.That (lv.IsRefreshing, Is.False);
+ }
+
+ [Test]
+ public void CanRefreshAfterCantExecuteCommand()
+ {
+ var lv = new ListView();
+
+ bool commandExecuted = false, eventFired = false;
+
+ lv.Refreshing += (sender, args) => eventFired = true;
+
+ Command refresh = new Command (() => commandExecuted = true,
+ () => false);
+
+ lv.RefreshCommand = refresh;
+ lv.RefreshCommand = null;
+
+ ((IListViewController)lv).SendRefreshing();
+
+ Assert.That (lv.IsRefreshing, Is.True);
+ Assert.That (eventFired, Is.True);
+ Assert.That (commandExecuted, Is.False);
+ }
+
+ [Test]
+ public void StopsListeningToCommandAfterCleared()
+ {
+ var lv = new ListView();
+
+ bool commandExecuted = false, canExecuteRequested = false;
+
+ Command refresh = new Command (() => commandExecuted = true,
+ () => canExecuteRequested = true);
+
+ lv.RefreshCommand = refresh;
+ canExecuteRequested = false;
+
+ lv.RefreshCommand = null;
+
+ Assert.That (() => refresh.ChangeCanExecute(), Throws.Nothing);
+ Assert.That (canExecuteRequested, Is.False);
+
+ lv.BeginRefresh();
+
+ Assert.That (commandExecuted, Is.False);
+ }
+
+ [Test]
+ [Description ("We should be able to set selected item when using ReadOnlyList")]
+ public void SetItemSelectedOnReadOnlyList()
+ {
+ var source = new ReadOnlySource ();
+ var listView = new ListView {
+ ItemsSource = source
+ };
+
+ bool raised = false;
+ listView.ItemSelected += (sender, arg) => raised = true;
+
+ listView.SelectedItem = source [0];
+ Assert.That (raised, Is.True, "ItemSelected was raised on ReadOnlySource");
+ }
+
+ internal class ReadOnlySource : IReadOnlyList<ListItem>
+ {
+ List<ListItem> items;
+ public ReadOnlySource ()
+ {
+ items = new List<ListItem> ();
+
+ for (int i = 0; i < 100; i++) {
+ items.Add (new ListItem { Name="person " + i } );
+ }
+
+ }
+ #region IEnumerable implementation
+ public IEnumerator<ListItem> GetEnumerator ()
+ {
+ return items.GetEnumerator ();
+ }
+ #endregion
+ #region IEnumerable implementation
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
+ {
+ return items.GetEnumerator ();
+ }
+ #endregion
+ #region IReadOnlyList implementation
+ public ListItem this [int index] {
+ get {
+ return items [index];
+ }
+ }
+ #endregion
+ #region IReadOnlyCollection implementation
+ public int Count {
+ get {
+ return items.Count;
+ }
+ }
+ #endregion
+
+ }
+
+ [Test]
+ public void ChildElementsParentIsNulledWhenReset()
+ {
+ var list = new ListView();
+ list.ItemsSource = new[] { "Hi", "Bye" };
+
+ var cell = list.TemplatedItems[0];
+ Assume.That (cell.Parent, Is.SameAs (list));
+
+ list.ItemsSource = null;
+ Assert.That (cell.Parent, Is.Null);
+ }
+
+ [Test]
+ public void ChildElementsParentIsNulledWhenRemoved()
+ {
+ var collection = new ObservableCollection<string> {
+ "Hi", "Bye"
+ };
+
+ var list = new ListView();
+ list.ItemsSource = collection;
+
+ var cell = list.TemplatedItems[0];
+ Assume.That (cell.Parent, Is.SameAs (list));
+
+ collection.Remove (collection[0]);
+ Assert.That (cell.Parent, Is.Null);
+ }
+
+ [Test]
+ public void ChildElementsParentIsNulledWhenCleared()
+ {
+ var collection = new ObservableCollection<string> {
+ "Hi", "Bye"
+ };
+
+ var list = new ListView();
+ list.ItemsSource = collection;
+
+ var cell = list.TemplatedItems[0];
+ Assume.That (cell.Parent, Is.SameAs (list));
+
+ collection.Clear();
+ Assert.That (cell.Parent, Is.Null);
+ }
+
+ [TestCase (TargetPlatform.Android, ListViewCachingStrategy.RecycleElement)]
+ [TestCase (TargetPlatform.iOS, ListViewCachingStrategy.RecycleElement)]
+ [TestCase (TargetPlatform.Windows, ListViewCachingStrategy.RetainElement)]
+ [TestCase (TargetPlatform.Other, ListViewCachingStrategy.RetainElement)]
+ [TestCase (TargetPlatform.WinPhone, ListViewCachingStrategy.RetainElement)]
+ public void EnforcesCachingStrategy (TargetPlatform platform, ListViewCachingStrategy expected)
+ {
+ var oldOS = Device.OS;
+ // we need to do this because otherwise we cant set the caching strategy
+ Device.OS = platform;
+ var listView = new ListView (ListViewCachingStrategy.RecycleElement);
+
+ Assert.AreEqual (expected, listView.CachingStrategy);
+ Device.OS = oldOS;
+ }
+
+ [Test]
+ public void DefaultCacheStrategy ()
+ {
+ var listView = new ListView ();
+
+ Assert.AreEqual (ListViewCachingStrategy.RetainElement, listView.CachingStrategy);
+ }
+
+ [Test]
+ public void DoesNotRetainInRecycleMode ()
+ {
+ var items = new ObservableCollection<string> {
+ "Foo",
+ "Bar"
+ };
+
+ var oldOS = Device.OS;
+ // we need to do this because otherwise we cant set the caching strategy
+ Device.OS = TargetPlatform.Android;
+
+ var bindable = new ListView (ListViewCachingStrategy.RecycleElement);
+ bindable.ItemTemplate = new DataTemplate (typeof (TextCell)) {
+ Bindings = {
+ { TextCell.TextProperty, new Binding (".") }
+ }
+ };
+
+ bindable.ItemsSource = items;
+ var item1 = bindable.TemplatedItems[0];
+ var item2 = bindable.TemplatedItems[0];
+
+ Assert.False(ReferenceEquals (item1, item2));
+
+ Device.OS = oldOS;
+ }
+ }
+}