|
|
@@ -23,9 +23,9 @@ namespace Perspex.Controls.Primitives
|
|
|
/// <see cref="SelectingItemsControl"/> provides a base class for <see cref="ItemsControl"/>s
|
|
|
/// that maintain a selection (single or multiple). By default only its
|
|
|
/// <see cref="SelectedIndex"/> and <see cref="SelectedItem"/> properties are visible; the
|
|
|
- /// multiple selection properties <see cref="SelectedIndexes"/> and <see cref="SelectedItems"/>
|
|
|
- /// together with the <see cref="SelectionMode"/> properties are protected, however a derived
|
|
|
- /// class can expose these if it wishes to support multiple selection.
|
|
|
+ /// current multiple selection <see cref="SelectedItems"/> together with the
|
|
|
+ /// <see cref="SelectionMode"/> properties are protected, however a derived class can expose
|
|
|
+ /// these if it wishes to support multiple selection.
|
|
|
/// </para>
|
|
|
/// <para>
|
|
|
/// <see cref="SelectingItemsControl"/> maintains a selection respecting the current
|
|
|
@@ -54,21 +54,14 @@ namespace Perspex.Controls.Primitives
|
|
|
o => o.SelectedItem,
|
|
|
(o, v) => o.SelectedItem = v);
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Defines the <see cref="SelectedIndexes"/> property.
|
|
|
- /// </summary>
|
|
|
- protected static readonly PerspexProperty<IPerspexList<int>> SelectedIndexesProperty =
|
|
|
- PerspexProperty.RegisterDirect<SelectingItemsControl, IPerspexList<int>>(
|
|
|
- nameof(SelectedIndexes),
|
|
|
- o => o.SelectedIndexes);
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Defines the <see cref="SelectedItems"/> property.
|
|
|
/// </summary>
|
|
|
- protected static readonly PerspexProperty<IPerspexList<object>> SelectedItemsProperty =
|
|
|
- PerspexProperty.RegisterDirect<SelectingItemsControl, IPerspexList<object>>(
|
|
|
+ protected static readonly PerspexProperty<IList<object>> SelectedItemsProperty =
|
|
|
+ PerspexProperty.RegisterDirect<SelectingItemsControl, IList<object>>(
|
|
|
nameof(SelectedItems),
|
|
|
- o => o.SelectedItems);
|
|
|
+ o => o.SelectedItems,
|
|
|
+ (o, v) => o.SelectedItems = v);
|
|
|
|
|
|
/// <summary>
|
|
|
/// Defines the <see cref="SelectionMode"/> property.
|
|
|
@@ -85,8 +78,9 @@ namespace Perspex.Controls.Primitives
|
|
|
public static readonly RoutedEvent<RoutedEventArgs> IsSelectedChangedEvent =
|
|
|
RoutedEvent.Register<SelectingItemsControl, RoutedEventArgs>("IsSelectedChanged", RoutingStrategies.Bubble);
|
|
|
|
|
|
- private PerspexList<int> _selectedIndexes = new PerspexList<int>();
|
|
|
- private PerspexList<object> _selectedItems = new PerspexList<object>();
|
|
|
+ private int _selectedIndex = -1;
|
|
|
+ private object _selectedItem;
|
|
|
+ private IList<object> _selectedItems;
|
|
|
private bool _ignoreContainerSelectionChanged;
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -103,9 +97,6 @@ namespace Perspex.Controls.Primitives
|
|
|
public SelectingItemsControl()
|
|
|
{
|
|
|
ItemContainerGenerator.ContainersInitialized.Subscribe(ContainersInitialized);
|
|
|
- _selectedIndexes.Validate = ValidateIndex;
|
|
|
- _selectedIndexes.ForEachItem(SelectedIndexesAdded, SelectedIndexesRemoved, SelectionReset);
|
|
|
- _selectedItems.ForEachItem(SelectedItemsAdded, SelectedItemsRemoved, SelectionReset);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -115,7 +106,7 @@ namespace Perspex.Controls.Primitives
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
- return _selectedIndexes.Count > 0 ? _selectedIndexes[0]: -1;
|
|
|
+ return _selectedIndex;
|
|
|
}
|
|
|
|
|
|
set
|
|
|
@@ -125,14 +116,9 @@ namespace Perspex.Controls.Primitives
|
|
|
|
|
|
if (old != effective)
|
|
|
{
|
|
|
- _selectedIndexes.Clear();
|
|
|
-
|
|
|
- if (effective != -1)
|
|
|
- {
|
|
|
- _selectedIndexes.Add(effective);
|
|
|
- }
|
|
|
-
|
|
|
+ _selectedIndex = effective;
|
|
|
RaisePropertyChanged(SelectedIndexProperty, old, effective, BindingPriority.LocalValue);
|
|
|
+ SelectedItem = ElementAt(Items, effective);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -144,42 +130,62 @@ namespace Perspex.Controls.Primitives
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
- return _selectedItems.FirstOrDefault();
|
|
|
+ return _selectedItem;
|
|
|
}
|
|
|
|
|
|
set
|
|
|
{
|
|
|
var old = SelectedItem;
|
|
|
- var effective = Items?.Cast<object>().Contains(value) == true ? value : null;
|
|
|
+ var index = IndexOf(Items, value);
|
|
|
+ var effective = index != -1 ? value : null;
|
|
|
|
|
|
if (effective != old)
|
|
|
{
|
|
|
- _selectedItems.Clear();
|
|
|
+ _selectedItem = effective;
|
|
|
+ RaisePropertyChanged(SelectedItemProperty, old, effective, BindingPriority.LocalValue);
|
|
|
+ SelectedIndex = index;
|
|
|
|
|
|
if (effective != null)
|
|
|
{
|
|
|
- _selectedItems.Add(effective);
|
|
|
+ if (SelectedItems.Count != 1 || SelectedItems[0] != effective)
|
|
|
+ {
|
|
|
+ SelectedItems.Clear();
|
|
|
+ SelectedItems.Add(effective);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ SelectedItems.Clear();
|
|
|
}
|
|
|
-
|
|
|
- RaisePropertyChanged(SelectedItemProperty, old, effective, BindingPriority.LocalValue);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Gets the selected indexes.
|
|
|
- /// </summary>
|
|
|
- protected IPerspexList<int> SelectedIndexes
|
|
|
- {
|
|
|
- get { return _selectedIndexes; }
|
|
|
- }
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Gets the selected items.
|
|
|
/// </summary>
|
|
|
- protected IPerspexList<object> SelectedItems
|
|
|
+ protected IList<object> SelectedItems
|
|
|
{
|
|
|
- get { return _selectedItems; }
|
|
|
+ get
|
|
|
+ {
|
|
|
+ if (_selectedItems == null)
|
|
|
+ {
|
|
|
+ _selectedItems = new PerspexList<object>();
|
|
|
+ SubscribeToSelectedItems();
|
|
|
+ }
|
|
|
+
|
|
|
+ return _selectedItems;
|
|
|
+ }
|
|
|
+
|
|
|
+ set
|
|
|
+ {
|
|
|
+ if (value != null)
|
|
|
+ {
|
|
|
+ UnsubscribeFromSelectedItems();
|
|
|
+ _selectedItems = value;
|
|
|
+ SubscribeToSelectedItems();
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -285,7 +291,7 @@ namespace Perspex.Controls.Primitives
|
|
|
var mode = SelectionMode;
|
|
|
var toggle = toggleModifier || (mode & SelectionMode.Toggle) != 0;
|
|
|
var multi = (mode & SelectionMode.Multiple) != 0;
|
|
|
- var range = multi && SelectedIndexes.Count > 0 ? rangeModifier : false;
|
|
|
+ var range = multi && SelectedIndex != -1 ? rangeModifier : false;
|
|
|
|
|
|
if (!toggle && !range)
|
|
|
{
|
|
|
@@ -293,21 +299,24 @@ namespace Perspex.Controls.Primitives
|
|
|
}
|
|
|
else if (multi && range)
|
|
|
{
|
|
|
- SynchronizeIndexes(SelectedIndexes, SelectedIndexes[0], index);
|
|
|
+ SynchronizeItems(
|
|
|
+ SelectedItems,
|
|
|
+ GetRange(Items, SelectedIndex, index));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- var i = SelectedIndexes.IndexOf(index);
|
|
|
+ var item = ElementAt(Items, index);
|
|
|
+ var i = SelectedItems.IndexOf(item);
|
|
|
|
|
|
if (i != -1 && (!AlwaysSelected || SelectedItems.Count > 1))
|
|
|
{
|
|
|
- SelectedIndexes.RemoveAt(i);
|
|
|
+ SelectedItems.Remove(item);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (multi)
|
|
|
{
|
|
|
- SelectedIndexes.Add(index);
|
|
|
+ SelectedItems.Add(item);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -315,6 +324,14 @@ namespace Perspex.Controls.Primitives
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ if (Presenter?.Panel != null)
|
|
|
+ {
|
|
|
+ var container = ItemContainerGenerator.ContainerFromIndex(index);
|
|
|
+ KeyboardNavigation.SetTabOnceActiveElement(
|
|
|
+ (InputElement)Presenter.Panel,
|
|
|
+ container);
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -373,6 +390,26 @@ namespace Perspex.Controls.Primitives
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the item at the specified index in a collection.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="items">The collection.</param>
|
|
|
+ /// <param name="index">The index.</param>
|
|
|
+ /// <returns>The index of the item or -1 if the item was not found.</returns>
|
|
|
+ private static object ElementAt(IEnumerable items, int index)
|
|
|
+ {
|
|
|
+ var typedItems = items?.Cast<object>();
|
|
|
+
|
|
|
+ if (index != -1 && typedItems != null && index < typedItems.Count())
|
|
|
+ {
|
|
|
+ return typedItems.ElementAt(index) ?? null;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Gets the index of an item in a collection.
|
|
|
/// </summary>
|
|
|
@@ -409,85 +446,54 @@ namespace Perspex.Controls.Primitives
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Generates a range of integers between the first and last inclusive.
|
|
|
+ /// Gets a range of items from an IEnumerable.
|
|
|
/// </summary>
|
|
|
- /// <param name="first">The first integer.</param>
|
|
|
- /// <param name="last">The last integer.</param>
|
|
|
- /// <returns>The range.</returns>
|
|
|
- private static IEnumerable<int> Range(int first, int last)
|
|
|
+ /// <param name="items">The items.</param>
|
|
|
+ /// <param name="first">The index of the first item.</param>
|
|
|
+ /// <param name="last">The index of the last item.</param>
|
|
|
+ /// <returns>The items.</returns>
|
|
|
+ private static IEnumerable<object> GetRange(IEnumerable items, int first, int last)
|
|
|
{
|
|
|
+ var list = (items as IList) ?? items.Cast<object>().ToList();
|
|
|
int step = first > last ? -1 : 1;
|
|
|
|
|
|
for (int i = first; i != last; i += step)
|
|
|
{
|
|
|
- yield return i;
|
|
|
+ yield return list[i];
|
|
|
}
|
|
|
|
|
|
- yield return last;
|
|
|
+ yield return list[last];
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Makes a list of integers equal the range first...last.
|
|
|
+ /// Makes a list of objects equal another.
|
|
|
/// </summary>
|
|
|
- /// <param name="indexes">The list of indexes.</param>
|
|
|
- /// <param name="first">The first in the range.</param>
|
|
|
- /// <param name="last">The last in the range.</param>
|
|
|
- private static void SynchronizeIndexes(IPerspexList<int> indexes, int first, int last)
|
|
|
+ /// <param name="items">The items collection.</param>
|
|
|
+ /// <param name="desired">The desired items.</param>
|
|
|
+ private static void SynchronizeItems(IList<object> items, IEnumerable<object> desired)
|
|
|
{
|
|
|
- var i = 0;
|
|
|
- var next = first;
|
|
|
- int step = first > last ? -1 : 1;
|
|
|
-
|
|
|
- while (i < indexes.Count && indexes[i] == next && next != last)
|
|
|
- {
|
|
|
- ++i;
|
|
|
- next += step;
|
|
|
- }
|
|
|
-
|
|
|
- if (next != last || i != indexes.Count - 1)
|
|
|
- {
|
|
|
- if (i < indexes.Count - 1)
|
|
|
- {
|
|
|
- indexes.RemoveRange(i, indexes.Count - i);
|
|
|
- }
|
|
|
+ int index = 0;
|
|
|
|
|
|
- indexes.AddRange(Range(next, last));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Sets a container's 'selected' class or <see cref="ISelectable.IsSelected"/>.
|
|
|
- /// </summary>
|
|
|
- /// <param name="container">The container.</param>
|
|
|
- /// <param name="selected">Whether the control is selected</param>
|
|
|
- private void MarkContainerSelected(IControl container, bool selected)
|
|
|
- {
|
|
|
- try
|
|
|
+ foreach (var i in desired)
|
|
|
{
|
|
|
- var selectable = container as ISelectable;
|
|
|
- var styleable = container as IStyleable;
|
|
|
-
|
|
|
- _ignoreContainerSelectionChanged = true;
|
|
|
-
|
|
|
- if (selectable != null)
|
|
|
+ if (index < items.Count)
|
|
|
{
|
|
|
- selectable.IsSelected = selected;
|
|
|
- }
|
|
|
- else if (styleable != null)
|
|
|
- {
|
|
|
- if (selected)
|
|
|
+ if (items[index] != i)
|
|
|
{
|
|
|
- styleable.Classes.Add(":selected");
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- styleable.Classes.Remove(":selected");
|
|
|
+ items[index] = i;
|
|
|
}
|
|
|
}
|
|
|
+ else
|
|
|
+ {
|
|
|
+ items.Add(i);
|
|
|
+ }
|
|
|
+
|
|
|
+ ++index;
|
|
|
}
|
|
|
- finally
|
|
|
+
|
|
|
+ while (index < items.Count)
|
|
|
{
|
|
|
- _ignoreContainerSelectionChanged = false;
|
|
|
+ items.RemoveAt(items.Count - 1);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -530,165 +536,214 @@ namespace Perspex.Controls.Primitives
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Sets an item container's 'selected' class or <see cref="ISelectable.IsSelected"/>.
|
|
|
+ /// Called when the currently selected item is lost and the selection must be changed
|
|
|
+ /// depending on the <see cref="SelectionMode"/> property.
|
|
|
/// </summary>
|
|
|
- /// <param name="index">The index of the item.</param>
|
|
|
- /// <param name="selected">Whether the control is selected</param>
|
|
|
- /// <returns>The container.</returns>
|
|
|
- private IControl MarkIndexSelected(int index, bool selected)
|
|
|
+ private void LostSelection()
|
|
|
{
|
|
|
- var container = ItemContainerGenerator.ContainerFromIndex(index);
|
|
|
+ var items = Items?.Cast<object>();
|
|
|
|
|
|
- if (container != null)
|
|
|
+ if (items != null && AlwaysSelected)
|
|
|
{
|
|
|
- MarkContainerSelected(container, selected);
|
|
|
+ var index = Math.Min(SelectedIndex, items.Count() - 1);
|
|
|
+
|
|
|
+ if (index > -1)
|
|
|
+ {
|
|
|
+ SelectedItem = items.ElementAt(index);
|
|
|
+ return;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- return container;
|
|
|
+ SelectedIndex = -1;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Called when an index is added to the <see cref="SelectedIndexes"/> collection.
|
|
|
+ /// Sets a container's 'selected' class or <see cref="ISelectable.IsSelected"/>.
|
|
|
/// </summary>
|
|
|
- /// <param name="listIndex">The index in the SelectedIndexes collection.</param>
|
|
|
- /// <param name="itemIndexes">The item indexes.</param>
|
|
|
- private void SelectedIndexesAdded(int listIndex, IEnumerable<int> itemIndexes)
|
|
|
+ /// <param name="container">The container.</param>
|
|
|
+ /// <param name="selected">Whether the control is selected</param>
|
|
|
+ private void MarkContainerSelected(IControl container, bool selected)
|
|
|
{
|
|
|
- var indexes = (itemIndexes as IList<int>) ?? itemIndexes.ToList();
|
|
|
- IControl container = null;
|
|
|
-
|
|
|
- if (SelectedItems.Count != SelectedIndexes.Count)
|
|
|
+ try
|
|
|
{
|
|
|
- var items = indexes.Select(x => Items.Cast<object>().ElementAt(x));
|
|
|
- SelectedItems.AddRange(items);
|
|
|
- }
|
|
|
+ var selectable = container as ISelectable;
|
|
|
+ var styleable = container as IStyleable;
|
|
|
|
|
|
- foreach (var itemIndex in indexes)
|
|
|
- {
|
|
|
- container = MarkIndexSelected(itemIndex, true);
|
|
|
- }
|
|
|
+ _ignoreContainerSelectionChanged = true;
|
|
|
|
|
|
- if (SelectedIndexes.Count == 1)
|
|
|
- {
|
|
|
- RaisePropertyChanged(SelectedIndexProperty, -1, SelectedIndexes[0], BindingPriority.LocalValue);
|
|
|
+ if (selectable != null)
|
|
|
+ {
|
|
|
+ selectable.IsSelected = selected;
|
|
|
+ }
|
|
|
+ else if (styleable != null)
|
|
|
+ {
|
|
|
+ if (selected)
|
|
|
+ {
|
|
|
+ styleable.Classes.Add(":selected");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ styleable.Classes.Remove(":selected");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- if (container != null && Presenter?.Panel != null)
|
|
|
+ finally
|
|
|
{
|
|
|
- KeyboardNavigation.SetTabOnceActiveElement((InputElement)Presenter.Panel, container);
|
|
|
+ _ignoreContainerSelectionChanged = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Called when an index is removed from the <see cref="SelectedIndexes"/> collection.
|
|
|
+ /// Sets an item container's 'selected' class or <see cref="ISelectable.IsSelected"/>.
|
|
|
/// </summary>
|
|
|
- /// <param name="listIndex">The index in the SelectedIndexes collection.</param>
|
|
|
- /// <param name="itemIndexes">The item indexes.</param>
|
|
|
- private void SelectedIndexesRemoved(int listIndex, IEnumerable<int> itemIndexes)
|
|
|
+ /// <param name="index">The index of the item.</param>
|
|
|
+ /// <param name="selected">Whether the item should be selected or deselected.</param>
|
|
|
+ private void MarkItemSelected(int index, bool selected)
|
|
|
{
|
|
|
- var sync = SelectedIndexes.Count != SelectedItems.Count;
|
|
|
-
|
|
|
- SelectedItems.RemoveRange(listIndex, itemIndexes.Count());
|
|
|
-
|
|
|
- foreach (var itemIndex in itemIndexes)
|
|
|
- {
|
|
|
- MarkIndexSelected(itemIndex, false);
|
|
|
- }
|
|
|
+ var container = ItemContainerGenerator.ContainerFromIndex(index);
|
|
|
|
|
|
- if (SelectedIndexes.Count == 0)
|
|
|
+ if (container != null)
|
|
|
{
|
|
|
- RaisePropertyChanged(
|
|
|
- SelectedIndexProperty,
|
|
|
- itemIndexes.First(),
|
|
|
- -1,
|
|
|
- BindingPriority.LocalValue);
|
|
|
+ MarkContainerSelected(container, selected);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Called when an item is added to the <see cref="SelectedItems"/> collection.
|
|
|
+ /// Sets an item container's 'selected' class or <see cref="ISelectable.IsSelected"/>.
|
|
|
/// </summary>
|
|
|
- /// <param name="index">The index in the SelectedItems collection.</param>
|
|
|
/// <param name="item">The item.</param>
|
|
|
- private void SelectedItemsAdded(int index, object item)
|
|
|
+ /// <param name="selected">Whether the item should be selected or deselected.</param>
|
|
|
+ private void MarkItemSelected(object item, bool selected)
|
|
|
{
|
|
|
- if (SelectedIndexes.Count != SelectedItems.Count)
|
|
|
- {
|
|
|
- SelectedIndexes.Insert(index, IndexOf(Items, item));
|
|
|
- }
|
|
|
+ var index = IndexOf(Items, item);
|
|
|
|
|
|
- if (SelectedItems.Count == 1)
|
|
|
+ if (index != -1)
|
|
|
{
|
|
|
- RaisePropertyChanged(SelectedItemProperty, null, item, BindingPriority.LocalValue);
|
|
|
+ MarkItemSelected(index, selected);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Called when an item is removed from the <see cref="SelectedItems"/> collection.
|
|
|
+ /// Called when the <see cref="SelectedItems"/> CollectionChanged event is raised.
|
|
|
/// </summary>
|
|
|
- /// <param name="index">The index in the SelectedItems collection.</param>
|
|
|
- /// <param name="item">The item.</param>
|
|
|
- private void SelectedItemsRemoved(int index, object item)
|
|
|
+ /// <param name="sender">The event sender.</param>
|
|
|
+ /// <param name="e">The event args.</param>
|
|
|
+ private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
|
{
|
|
|
- if (SelectedIndexes.Count != SelectedItems.Count)
|
|
|
+ switch (e.Action)
|
|
|
{
|
|
|
- SelectedIndexes.RemoveAt(index);
|
|
|
+ case NotifyCollectionChangedAction.Add:
|
|
|
+ SelectedItemsAdded(e.NewItems.Cast<object>().ToList());
|
|
|
+ break;
|
|
|
+
|
|
|
+ case NotifyCollectionChangedAction.Remove:
|
|
|
+ if (SelectedItems.Count == 0)
|
|
|
+ {
|
|
|
+ SelectedIndex = -1;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ foreach (var item in e.OldItems)
|
|
|
+ {
|
|
|
+ MarkItemSelected(item, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case NotifyCollectionChangedAction.Reset:
|
|
|
+ foreach (var item in ItemContainerGenerator.Containers)
|
|
|
+ {
|
|
|
+ MarkContainerSelected(item, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ SelectedIndex = -1;
|
|
|
+ SelectedItemsAdded(SelectedItems);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case NotifyCollectionChangedAction.Replace:
|
|
|
+ foreach (var item in e.OldItems)
|
|
|
+ {
|
|
|
+ MarkItemSelected(item, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var item in e.NewItems)
|
|
|
+ {
|
|
|
+ MarkItemSelected(item, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (SelectedItem != SelectedItems[0])
|
|
|
+ {
|
|
|
+ var oldItem = SelectedItem;
|
|
|
+ var oldIndex = SelectedIndex;
|
|
|
+ var item = SelectedItems[0];
|
|
|
+ var index = IndexOf(Items, item);
|
|
|
+ _selectedIndex = index;
|
|
|
+ _selectedItem = item;
|
|
|
+ RaisePropertyChanged(SelectedIndexProperty, oldIndex, index, BindingPriority.LocalValue);
|
|
|
+ RaisePropertyChanged(SelectedItemProperty, oldItem, item, BindingPriority.LocalValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Called when the <see cref="SelectedItems"/> collection is reset.
|
|
|
+ /// Called when items are added to the <see cref="SelectedItems"/> collection.
|
|
|
/// </summary>
|
|
|
- private void SelectionReset()
|
|
|
+ /// <param name="items">The added items.</param>
|
|
|
+ private void SelectedItemsAdded(IList<object> items)
|
|
|
{
|
|
|
- if (SelectedIndexes.Count > 0)
|
|
|
+ if (items.Count > 0)
|
|
|
{
|
|
|
- SelectedIndexes.Clear();
|
|
|
- }
|
|
|
+ foreach (var item in items)
|
|
|
+ {
|
|
|
+ MarkItemSelected(item, true);
|
|
|
+ }
|
|
|
|
|
|
- if (SelectedItems.Count > 0)
|
|
|
- {
|
|
|
- SelectedItems.Clear();
|
|
|
- }
|
|
|
+ if (SelectedItem == null)
|
|
|
+ {
|
|
|
+ var index = IndexOf(Items, items[0]);
|
|
|
|
|
|
- foreach (var container in ItemContainerGenerator.Containers)
|
|
|
- {
|
|
|
- MarkContainerSelected(container, false);
|
|
|
+ if (index != -1)
|
|
|
+ {
|
|
|
+ _selectedItem = items[0];
|
|
|
+ _selectedIndex = index;
|
|
|
+ RaisePropertyChanged(SelectedIndexProperty, -1, index, BindingPriority.LocalValue);
|
|
|
+ RaisePropertyChanged(SelectedItemProperty, null, items[0], BindingPriority.LocalValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Validates items added to the <see cref="SelectedIndexes"/> collection.
|
|
|
+ /// Subscribes to the <see cref="SelectedItems"/> CollectionChanged event, if any.
|
|
|
/// </summary>
|
|
|
- /// <param name="index">The index to be added.</param>
|
|
|
- private void ValidateIndex(int index)
|
|
|
+ private void SubscribeToSelectedItems()
|
|
|
{
|
|
|
- if (index < 0 || index >= Items?.Cast<object>().Count())
|
|
|
+ var incc = _selectedItems as INotifyCollectionChanged;
|
|
|
+
|
|
|
+ if (incc != null)
|
|
|
{
|
|
|
- throw new IndexOutOfRangeException();
|
|
|
+ incc.CollectionChanged += SelectedItemsCollectionChanged;
|
|
|
}
|
|
|
+
|
|
|
+ SelectedItemsCollectionChanged(
|
|
|
+ _selectedItems,
|
|
|
+ new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Called when the currently selected item is lost and the selection must be changed
|
|
|
- /// depending on the <see cref="SelectionMode"/> property.
|
|
|
+ /// Unsubscribes from the <see cref="SelectedItems"/> CollectionChanged event, if any.
|
|
|
/// </summary>
|
|
|
- private void LostSelection()
|
|
|
+ private void UnsubscribeFromSelectedItems()
|
|
|
{
|
|
|
- var items = Items?.Cast<object>();
|
|
|
+ var incc = _selectedItems as INotifyCollectionChanged;
|
|
|
|
|
|
- if (items != null && AlwaysSelected)
|
|
|
+ if (incc != null)
|
|
|
{
|
|
|
- var index = Math.Min(SelectedIndex, items.Count() - 1);
|
|
|
-
|
|
|
- if (index > -1)
|
|
|
- {
|
|
|
- SelectedItem = items.ElementAt(index);
|
|
|
- return;
|
|
|
- }
|
|
|
+ incc.CollectionChanged -= SelectedItemsCollectionChanged;
|
|
|
}
|
|
|
-
|
|
|
- SelectedIndex = -1;
|
|
|
}
|
|
|
}
|
|
|
}
|