From d27205716aec29f93ceb280fcad3d4749ca4cfcb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 2 May 2015 20:23:01 +0200 Subject: [PATCH] Handle deselecting removed items... in SelectingItemsControl. --- Perspex.Controls/ItemsControl.cs | 2 +- .../Primitives/SelectingItemsControl.cs | 38 +++++++++++++ Perspex.Controls/Utils/IEnumerableUtils.cs | 5 ++ .../Primitives/SelectingItemsControlTests.cs | 55 +++++++++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/Perspex.Controls/ItemsControl.cs b/Perspex.Controls/ItemsControl.cs index 311804b1bb..f379adaa7b 100644 --- a/Perspex.Controls/ItemsControl.cs +++ b/Perspex.Controls/ItemsControl.cs @@ -126,7 +126,7 @@ namespace Perspex.Controls if (incc != null) { - incc.CollectionChanged -= this.ItemsCollectionChanged; + incc.CollectionChanged += this.ItemsCollectionChanged; } } diff --git a/Perspex.Controls/Primitives/SelectingItemsControl.cs b/Perspex.Controls/Primitives/SelectingItemsControl.cs index de011299c0..b90c5b1e89 100644 --- a/Perspex.Controls/Primitives/SelectingItemsControl.cs +++ b/Perspex.Controls/Primitives/SelectingItemsControl.cs @@ -11,6 +11,8 @@ namespace Perspex.Controls.Primitives using Perspex.VisualTree; using System; using System.Linq; + using System.Collections; + using System.Collections.Specialized; public abstract class SelectingItemsControl : ItemsControl { @@ -66,6 +68,42 @@ namespace Perspex.Controls.Primitives set { this.SetValue(SelectedItemProperty, value); } } + protected override void ItemsChanged(IEnumerable oldValue, IEnumerable newValue) + { + base.ItemsChanged(oldValue, newValue); + + var selected = this.SelectedItem; + + if (selected != null) + { + if (newValue == null || !newValue.Contains(selected)) + { + this.SelectedItem = null; + } + } + } + + protected override void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + base.ItemsCollectionChanged(sender, e); + + var selected = this.SelectedItem; + + switch (e.Action) + { + case NotifyCollectionChangedAction.Remove: + case NotifyCollectionChangedAction.Reset: + if (e.OldItems.Contains(selected)) + { + this.SelectedItem = null; + } + break; + case NotifyCollectionChangedAction.Move: + this.SelectedItem = this.Items.IndexOf(selected); + break; + } + } + protected virtual void MoveSelection(FocusNavigationDirection direction) { var panel = this.Presenter?.Panel as INavigablePanel; diff --git a/Perspex.Controls/Utils/IEnumerableUtils.cs b/Perspex.Controls/Utils/IEnumerableUtils.cs index 247dcf34d7..26bf953db9 100644 --- a/Perspex.Controls/Utils/IEnumerableUtils.cs +++ b/Perspex.Controls/Utils/IEnumerableUtils.cs @@ -13,6 +13,11 @@ namespace Perspex.Controls.Utils internal static class IEnumerableUtils { + public static bool Contains(this IEnumerable items, object item) + { + return items.IndexOf(item) != -1; + } + public static int Count(this IEnumerable items) { Contract.Requires(items != null); diff --git a/Tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/Tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 01286445c6..1eecf79b62 100644 --- a/Tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/Tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -6,6 +6,7 @@ namespace Perspex.Controls.Primitives.UnitTests { + using Perspex.Collections; using Perspex.Controls.Presenters; using Perspex.Controls.Primitives; using Perspex.Input; @@ -173,6 +174,60 @@ namespace Perspex.Controls.Primitives.UnitTests target.SelectedItem = new Item(); } + [Fact] + public void Clearing_Items_Should_Clear_Selection() + { + var items = new PerspexList + { + new Item(), + new Item(), + }; + + var target = new Target + { + Items = items, + Template = this.Template(), + }; + + target.ApplyTemplate(); + target.SelectedIndex = 1; + + Assert.Equal(items[1], target.SelectedItem); + Assert.Equal(1, target.SelectedIndex); + + target.Items = null; + + Assert.Equal(null, target.SelectedItem); + Assert.Equal(-1, target.SelectedIndex); + } + + [Fact] + public void Removing_Selected_Item_Should_Clear_Selection() + { + var items = new PerspexList + { + new Item(), + new Item(), + }; + + var target = new Target + { + Items = items, + Template = this.Template(), + }; + + target.ApplyTemplate(); + target.SelectedIndex = 1; + + Assert.Equal(items[1], target.SelectedItem); + Assert.Equal(1, target.SelectedIndex); + + items.RemoveAt(1); + + Assert.Equal(null, target.SelectedItem); + Assert.Equal(-1, target.SelectedIndex); + } + [Fact] public void PointerPressed_Event_Should_Be_Handled() {