Browse Source

More work on updating selection...

...when source items change.
pull/2673/head
Steven Kirk 7 years ago
parent
commit
94d71fb12f
  1. 107
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  2. 26
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  3. 91
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

107
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -307,31 +307,23 @@ namespace Avalonia.Controls.Primitives
{
SelectedIndex = 0;
}
else if (e.NewStartingIndex <= SelectedIndex)
else
{
UpdateSelectedItem(SelectedIndex + e.NewItems.Count, false);
_selection.ItemsInserted(e.NewStartingIndex, e.NewItems.Count);
UpdateSelectedItem(_selection.First(), false);
}
break;
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Replace:
if (SelectedIndex >= e.OldStartingIndex && SelectedIndex < e.OldStartingIndex + e.OldItems.Count)
{
if (!AlwaysSelected)
{
SelectedIndex = -1;
}
else
{
LostSelection();
}
}
else if (e.OldStartingIndex <= SelectedIndex)
{
UpdateSelectedItem(SelectedIndex - e.OldItems.Count, false);
}
_selection.ItemsRemoved(e.OldStartingIndex, e.OldItems.Count);
UpdateSelectedItem(_selection.First(), false);
ResetSelectedItems();
break;
case NotifyCollectionChangedAction.Replace:
UpdateSelectedItem(SelectedIndex, false);
ResetSelectedItems();
break;
case NotifyCollectionChangedAction.Move:
@ -498,7 +490,7 @@ namespace Avalonia.Controls.Primitives
MarkItemSelected(container.Index, true);
}
ResetSelectedItems(GetRange(Items, 0, ItemCount - 1));
ResetSelectedItems();
});
}
@ -534,15 +526,19 @@ namespace Avalonia.Controls.Primitives
UpdateSelectedItems(() =>
{
var start = SelectedIndex != -1 ? SelectedIndex : 0;
var first = Math.Min(start, index);
var last = Math.Max(start, index);
var step = start < index ? 1 : -1;
for (var i = first; i < last; ++i)
_selection.Clear();
for (var i = start; i != index; i += step)
{
_selection.Add(i);
}
_selection.Add(last);
_selection.Add(index);
var first = Math.Min(start, index);
var last = Math.Max(start, index);
foreach (var container in ItemContainerGenerator.Containers)
{
@ -551,7 +547,7 @@ namespace Avalonia.Controls.Primitives
container.Index >= first && container.Index <= last);
}
ResetSelectedItems(GetRange(Items, start, index));
ResetSelectedItems();
});
}
else if (multi && toggle)
@ -805,14 +801,17 @@ namespace Avalonia.Controls.Primitives
return index;
}
private void ResetSelectedItems(IEnumerable<object> items)
private void ResetSelectedItems()
{
SelectedItems.Clear();
foreach (var i in items)
UpdateSelectedItems(() =>
{
SelectedItems.Add(i);
}
SelectedItems.Clear();
foreach (var i in _selection)
{
SelectedItems.Add(ElementAt(Items, i));
}
});
}
/// <summary>
@ -1135,6 +1134,54 @@ namespace Avalonia.Controls.Primitives
return result;
}
public void ItemsInserted(int index, int count)
{
_set = new HashSet<int>();
for (var i = 0; i < _list.Count; ++i)
{
var ix = _list[i];
if (ix >= index)
{
var newIndex = ix + count;
_list[i] = newIndex;
_set.Add(newIndex);
}
else
{
_set.Add(ix);
}
}
}
public void ItemsRemoved(int index, int count)
{
var last = (index + count) - 1;
_set = new HashSet<int>();
for (var i = 0; i < _list.Count; ++i)
{
var ix = _list[i];
if (ix >= index && ix <= last)
{
_list.RemoveAt(i--);
}
else if (ix > last)
{
var newIndex = ix - count;
_list[i] = newIndex;
_set.Add(newIndex);
}
else
{
_set.Add(ix);
}
}
}
public bool Contains(int index) => _set.Contains(index);
public int First() => HasItems ? _list[0] : -1;

26
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -868,6 +868,32 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Equal("Bar", target.SelectedItem);
}
[Fact]
public void Replacing_Selected_Item_Should_Update_SelectedItem()
{
var items = new ObservableCollection<string>
{
"Foo",
"Bar",
"Baz"
};
var target = new ListBox
{
Template = Template(),
Items = items,
SelectedIndex = 1,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
items[1] = "Qux";
Assert.Equal(1, target.SelectedIndex);
Assert.Equal("Qux", target.SelectedItem);
}
private FuncControlTemplate Template()
{
return new FuncControlTemplate<SelectingItemsControl>(control =>

91
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@ -841,7 +841,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
[Fact]
public void Adding_Item_Before_SelectedItems_Should_Update_Indexes()
public void Adding_Item_Before_SelectedItems_Should_Update_Selection()
{
var items = new ObservableCollection<string>
{
@ -865,8 +865,97 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Equal(1, target.SelectedIndex);
Assert.Equal("Foo", target.SelectedItem);
Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems);
Assert.Equal(new[] { 1, 2, 3 }, SelectedContainers(target));
}
[Fact]
public void Removing_Item_Before_SelectedItem_Should_Update_Selection()
{
var items = new ObservableCollection<string>
{
"Foo",
"Bar",
"Baz"
};
var target = new TestSelector
{
Template = Template(),
Items = items,
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectedIndex = 1;
target.SelectRange(2);
Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems);
items.RemoveAt(0);
Assert.Equal(0, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems);
Assert.Equal(new[] { 0, 1 }, SelectedContainers(target));
}
[Fact]
public void Removing_SelectedItem_With_Multiple_Selection_Active_Should_Update_Selection()
{
var items = new ObservableCollection<string>
{
"Foo",
"Bar",
"Baz"
};
var target = new ListBox
{
Template = Template(),
Items = items,
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectAll();
items.RemoveAt(0);
Assert.Equal(0, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems);
Assert.Equal(new[] { 0, 1 }, SelectedContainers(target));
}
[Fact]
public void Replacing_Selected_Item_Should_Update_SelectedItems()
{
var items = new ObservableCollection<string>
{
"Foo",
"Bar",
"Baz"
};
var target = new ListBox
{
Template = Template(),
Items = items,
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectAll();
items[1] = "Qux";
Assert.Equal(new[] { "Foo", "Qux", "Baz" }, target.SelectedItems);
}
private IEnumerable<int> SelectedContainers(SelectingItemsControl target)
{

Loading…
Cancel
Save