Browse Source

Added SelectingItemsControl.SelectionChanged event.

pull/390/head
Steven Kirk 11 years ago
parent
commit
c49201b8cb
  1. 61
      src/Perspex.Controls/Primitives/SelectingItemsControl.cs
  2. 38
      src/Perspex.Controls/SelectionChangedEventArgs.cs
  3. 62
      tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  4. 79
      tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

61
src/Perspex.Controls/Primitives/SelectingItemsControl.cs

@ -76,7 +76,19 @@ namespace Perspex.Controls.Primitives
/// has changed.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> IsSelectedChangedEvent =
RoutedEvent.Register<SelectingItemsControl, RoutedEventArgs>("IsSelectedChanged", RoutingStrategies.Bubble);
RoutedEvent.Register<SelectingItemsControl, RoutedEventArgs>(
"IsSelectedChanged",
RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="SelectionChanged"/> event.
/// </summary>
public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
RoutedEvent.Register<SelectingItemsControl, SelectionChangedEventArgs>(
"SelectionChanged",
RoutingStrategies.Bubble);
private static readonly IList Empty = new object[0];
private int _selectedIndex = -1;
private object _selectedItem;
@ -100,6 +112,15 @@ namespace Perspex.Controls.Primitives
{
}
/// <summary>
/// Occurs when the control's selection changes.
/// </summary>
public event EventHandler<SelectionChangedEventArgs> SelectionChanged
{
add { AddHandler(SelectionChangedEvent, value); }
remove { RemoveHandler(SelectionChangedEvent, value); }
}
/// <summary>
/// Gets or sets the index of the selected item.
/// </summary>
@ -532,22 +553,28 @@ namespace Perspex.Controls.Primitives
/// </summary>
/// <param name="container">The container.</param>
/// <param name="selected">Whether the control is selected</param>
private void MarkContainerSelected(IControl container, bool selected)
/// <returns>The previous selection state.</returns>
private bool MarkContainerSelected(IControl container, bool selected)
{
try
{
var selectable = container as ISelectable;
bool result;
_ignoreContainerSelectionChanged = true;
if (selectable != null)
{
result = selectable.IsSelected;
selectable.IsSelected = selected;
}
else
{
result = container.Classes.Contains(":selected");
((IPseudoClasses)container.Classes).Set(":selected", selected);
}
return result;
}
finally
{
@ -593,11 +620,14 @@ namespace Perspex.Controls.Primitives
private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var generator = ItemContainerGenerator;
IList added = null;
IList removed = null;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
SelectedItemsAdded(e.NewItems.Cast<object>().ToList());
added = e.NewItems;
break;
case NotifyCollectionChangedAction.Remove:
@ -616,26 +646,36 @@ namespace Perspex.Controls.Primitives
}
}
removed = e.OldItems;
break;
case NotifyCollectionChangedAction.Reset:
if (generator != null)
{
removed = new List<object>();
foreach (var item in generator.Containers)
{
if (item != null)
{
MarkContainerSelected(item.ContainerControl, false);
if (MarkContainerSelected(item.ContainerControl, false))
{
removed.Add(item.Item);
}
}
}
}
if (!_syncingSelectedItems)
if (SelectedItems.Count > 0)
{
SelectedItemsAdded(SelectedItems);
added = SelectedItems;
}
else if (!_syncingSelectedItems)
{
SelectedIndex = -1;
}
SelectedItemsAdded(SelectedItems);
break;
case NotifyCollectionChangedAction.Replace:
@ -661,8 +701,19 @@ namespace Perspex.Controls.Primitives
RaisePropertyChanged(SelectedItemProperty, oldItem, item, BindingPriority.LocalValue);
}
added = e.OldItems;
removed = e.NewItems;
break;
}
if (added?.Count > 0 || removed?.Count > 0)
{
var changed = new SelectionChangedEventArgs(
SelectionChangedEvent,
added ?? Empty,
removed ?? Empty);
RaiseEvent(changed);
}
}
/// <summary>

38
src/Perspex.Controls/SelectionChangedEventArgs.cs

@ -0,0 +1,38 @@
// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Collections;
using Perspex.Controls.Primitives;
using Perspex.Interactivity;
namespace Perspex.Controls
{
/// <summary>
/// Provides data for the <see cref="SelectingItemsControl.SelectionChanged"/> event.
/// </summary>
public class SelectionChangedEventArgs : RoutedEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="SelectionChangedEventArgs"/> class.
/// </summary>
/// <param name="routedEvent">The event being raised.</param>
/// <param name="addedItems">The items added to the selection.</param>
/// <param name="removedItems">The items removed from the selection.</param>
public SelectionChangedEventArgs(RoutedEvent routedEvent, IList addedItems, IList removedItems)
: base(routedEvent)
{
AddedItems = addedItems;
RemovedItems = removedItems;
}
/// <summary>
/// Gets the items that were added to the selection.
/// </summary>
public IList AddedItems { get; }
/// <summary>
/// Gets the items that were removed from the selection.
/// </summary>
public IList RemovedItems { get; }
}
}

62
tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Collections.ObjectModel;
using System.Linq;
using Perspex.Collections;
using Perspex.Controls.Presenters;
using Perspex.Controls.Primitives;
@ -425,6 +426,67 @@ namespace Perspex.Controls.UnitTests.Primitives
Assert.Equal(target.SelectedItem, items[1]);
}
[Fact]
public void Setting_SelectedIndex_Should_Raise_SelectionChanged_Event()
{
var items = new[]
{
new Item(),
new Item(),
};
var target = new SelectingItemsControl
{
Items = items,
Template = Template(),
};
var called = false;
target.SelectionChanged += (s, e) =>
{
Assert.Same(items[1], e.AddedItems.Cast<object>().Single());
Assert.Empty(e.RemovedItems);
called = true;
};
target.SelectedIndex = 1;
Assert.True(called);
}
[Fact]
public void Clearing_SelectedIndex_Should_Raise_SelectionChanged_Event()
{
var items = new[]
{
new Item(),
new Item(),
};
var target = new SelectingItemsControl
{
Items = items,
Template = Template(),
SelectedIndex = 1,
};
var called = false;
target.SelectionChanged += (s, e) =>
{
Assert.Same(items[1], e.RemovedItems.Cast<object>().Single());
Assert.Empty(e.AddedItems);
called = true;
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectedIndex = -1;
Assert.True(called);
}
private FuncControlTemplate Template()
{
return new FuncControlTemplate<SelectingItemsControl>(control =>

79
tests/Perspex.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@ -461,6 +461,85 @@ namespace Perspex.Controls.UnitTests.Primitives
Assert.Empty(target.SelectedItems);
}
[Fact]
public void Adding_To_SelectedItems_Should_Raise_SelectionChanged()
{
var items = new[] { "foo", "bar", "baz" };
var target = new TestSelector
{
DataContext = items,
Template = Template(),
};
var called = false;
target.SelectionChanged += (s, e) =>
{
Assert.Equal(new[] { "bar" }, e.AddedItems.Cast<object>().ToList());
Assert.Empty(e.RemovedItems);
called = true;
};
target.SelectedItems.Add("bar");
Assert.True(called);
}
[Fact]
public void Removing_From_SelectedItems_Should_Raise_SelectionChanged()
{
var items = new[] { "foo", "bar", "baz" };
var target = new TestSelector
{
Items = items,
Template = Template(),
SelectedItem = "bar",
};
var called = false;
target.SelectionChanged += (s, e) =>
{
Assert.Equal(new[] { "bar" }, e.RemovedItems.Cast<object>().ToList());
Assert.Empty(e.AddedItems);
called = true;
};
target.SelectedItems.Remove("bar");
Assert.True(called);
}
[Fact]
public void Assigning_SelectedItems_Should_Raise_SelectionChanged()
{
var items = new[] { "foo", "bar", "baz" };
var target = new TestSelector
{
Items = items,
Template = Template(),
SelectedItem = "bar",
};
var called = false;
target.SelectionChanged += (s, e) =>
{
Assert.Equal(new[] { "foo", "baz" }, e.AddedItems.Cast<object>());
Assert.Equal(new[] { "bar" }, e.RemovedItems.Cast<object>());
called = true;
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectedItems = new[] { "foo", "baz" };
Assert.True(called);
}
private FuncControlTemplate Template()
{
return new FuncControlTemplate<SelectingItemsControl>(control =>

Loading…
Cancel
Save