diff --git a/samples/BindingDemo/MainWindow.xaml b/samples/BindingDemo/MainWindow.xaml
index b57a9a0a9e..26a62ebca6 100644
--- a/samples/BindingDemo/MainWindow.xaml
+++ b/samples/BindingDemo/MainWindow.xaml
@@ -74,11 +74,11 @@
-
+
-
+
diff --git a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
index 22d01e0765..a66038ff3e 100644
--- a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
+++ b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
@@ -6,6 +6,7 @@ using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Threading;
using ReactiveUI;
+using Avalonia.Controls;
namespace BindingDemo.ViewModels
{
@@ -27,7 +28,7 @@ namespace BindingDemo.ViewModels
Detail = "Item " + x + " details",
}));
- SelectedItems = new ObservableCollection();
+ Selection = new SelectionModel();
ShuffleItems = ReactiveCommand.Create(() =>
{
@@ -56,7 +57,7 @@ namespace BindingDemo.ViewModels
}
public ObservableCollection Items { get; }
- public ObservableCollection SelectedItems { get; }
+ public SelectionModel Selection { get; }
public ReactiveCommand ShuffleItems { get; }
public string BooleanString
diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml
index b1b3112e60..47b4ce7151 100644
--- a/samples/ControlCatalog/Pages/ListBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml
@@ -10,7 +10,7 @@
HorizontalAlignment="Center"
Spacing="16">
-
+
diff --git a/samples/VirtualizationDemo/MainWindow.xaml b/samples/VirtualizationDemo/MainWindow.xaml
index 12137cd03d..4bd657bf93 100644
--- a/samples/VirtualizationDemo/MainWindow.xaml
+++ b/samples/VirtualizationDemo/MainWindow.xaml
@@ -45,7 +45,7 @@
SelectedItems { get; }
- = new AvaloniaList();
+ public SelectionModel Selection { get; } = new SelectionModel();
public AvaloniaList Items
{
@@ -141,9 +140,9 @@ namespace VirtualizationDemo.ViewModels
{
var index = Items.Count;
- if (SelectedItems.Count > 0)
+ if (Selection.SelectedIndices.Count > 0)
{
- index = Items.IndexOf(SelectedItems[0]);
+ index = Selection.SelectedIndex.GetAt(0);
}
Items.Insert(index, new ItemViewModel(_newItemIndex++, NewItemString));
@@ -151,9 +150,9 @@ namespace VirtualizationDemo.ViewModels
private void Remove()
{
- if (SelectedItems.Count > 0)
+ if (Selection.SelectedItems.Count > 0)
{
- Items.RemoveAll(SelectedItems);
+ Items.RemoveAll(Selection.SelectedItems.Cast().ToList());
}
}
@@ -167,8 +166,7 @@ namespace VirtualizationDemo.ViewModels
private void SelectItem(int index)
{
- SelectedItems.Clear();
- SelectedItems.Add(Items[index]);
+ Selection.SelectedIndex = new IndexPath(index);
}
}
}
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index c2cf20b32d..0722802962 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -289,9 +289,9 @@ namespace Avalonia.Controls
{
var container = ItemContainerGenerator.ContainerFromIndex(selectedIndex);
- if (container == null && SelectedItems.Count > 0)
+ if (container == null && SelectedIndex != -1)
{
- ScrollIntoView(SelectedItems[0]);
+ ScrollIntoView(Selection.SelectedItem);
container = ItemContainerGenerator.ContainerFromIndex(selectedIndex);
}
diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs
index 4966e669ed..12c02b81c9 100644
--- a/src/Avalonia.Controls/ListBox.cs
+++ b/src/Avalonia.Controls/ListBox.cs
@@ -29,10 +29,10 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect(nameof(Scroll), o => o.Scroll);
///
- /// Defines the property.
+ /// Defines the property.
///
- public static readonly new DirectProperty SelectedItemsProperty =
- SelectingItemsControl.SelectedItemsProperty;
+ public static readonly new DirectProperty SelectionProperty =
+ SelectingItemsControl.SelectionProperty;
///
/// Defines the property.
@@ -66,11 +66,13 @@ namespace Avalonia.Controls
private set { SetAndRaise(ScrollProperty, ref _scroll, value); }
}
- ///
- public new IList SelectedItems
+ ///
+ /// Gets or sets a model holding the current selection.
+ ///
+ public new ISelectionModel Selection
{
- get => base.SelectedItems;
- set => base.SelectedItems = value;
+ get => base.Selection;
+ set => base.Selection = value;
}
///
@@ -98,12 +100,12 @@ namespace Avalonia.Controls
///
/// Selects all items in the .
///
- public new void SelectAll() => base.SelectAll();
+ public void SelectAll() => Selection.SelectAll();
///
/// Deselects all items in the .
///
- public new void UnselectAll() => base.UnselectAll();
+ public void UnselectAll() => Selection.ClearSelection();
///
protected override IItemContainerGenerator CreateItemContainerGenerator()
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index 6bc4e71508..8c3487e893 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -5,15 +5,14 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
+using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
-using Avalonia.Collections;
using Avalonia.Controls.Generators;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
-using Avalonia.Logging;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Primitives
@@ -26,9 +25,9 @@ namespace Avalonia.Controls.Primitives
/// provides a base class for s
/// that maintain a selection (single or multiple). By default only its
/// and properties are visible; the
- /// current multiple selection together with the
- /// properties are protected, however a derived class can expose
- /// these if it wishes to support multiple selection.
+ /// current multiple together with the
+ /// properties are protected, however a derived class can expose these if it wishes to support
+ /// multiple selection.
///
///
/// maintains a selection respecting the current
@@ -69,13 +68,13 @@ namespace Avalonia.Controls.Primitives
defaultBindingMode: BindingMode.TwoWay);
///
- /// Defines the property.
+ /// Defines the property.
///
- protected static readonly DirectProperty SelectedItemsProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(SelectedItems),
- o => o.SelectedItems,
- (o, v) => o.SelectedItems = v);
+ public static readonly DirectProperty SelectionProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Selection),
+ o => o.Selection,
+ (o, v) => o.Selection = v);
///
/// Defines the property.
@@ -104,16 +103,20 @@ namespace Avalonia.Controls.Primitives
private static readonly IList Empty = Array.Empty
public int SelectedIndex
{
- get
- {
- return _selectedIndex;
- }
-
+ get => Selection.SelectedIndex != default ? Selection.SelectedIndex.GetAt(0) : -1;
set
{
if (_updateCount == 0)
{
- var effective = (value >= 0 && value < ItemCount) ? value : -1;
- UpdateSelectedItem(effective);
+ if (value != SelectedIndex)
+ {
+ Selection.SelectedIndex = new IndexPath(value);
+ }
}
else
{
@@ -170,16 +171,12 @@ namespace Avalonia.Controls.Primitives
///
public object SelectedItem
{
- get
- {
- return _selectedItem;
- }
-
+ get => Selection.SelectedItem;
set
{
if (_updateCount == 0)
{
- UpdateSelectedItem(IndexOf(Items, value));
+ SelectedIndex = IndexOf(Items, value);
}
else
{
@@ -190,32 +187,101 @@ namespace Avalonia.Controls.Primitives
}
///
- /// Gets the selected items.
+ /// Gets or sets a model holding the current selection.
///
- protected IList SelectedItems
+ protected ISelectionModel Selection
{
- get
+ get => _selection;
+ set
{
- if (_selectedItems == null)
+ value ??= new SelectionModel
{
- _selectedItems = new AvaloniaList
///
/// - Items is bound to DataContext first, followed by say SelectedIndex
- /// - When the ListBox is removed from the visual tree, DataContext becomes null (as it's
- /// inherited)
- /// - This changes Items to null, which changes SelectedIndex to null as there are no
- /// longer any items
- /// - However, the news that DataContext is now null hasn't yet reached the SelectedItems
- /// binding and so the unselection is sent back to the ViewModel
+ /// - When the ListBox is removed from the tree, DataContext becomes null (as it's inherited)
+ /// - This changes Items and Selection to null
+ /// - However, the news that DataContext is now null reaches the Selection after the change to
+ /// Items, so the SelectionModel.Source is set to null
///
/// This is a similar problem to that tested by XamlBindingTest.Should_Not_Write_To_Old_DataContext.
/// However, that tests a general property binding problem: here we are writing directly
- /// to the SelectedItems collection - not via a binding - so it's something that the
- /// binding system cannot solve. Instead we solve it by not clearing SelectedItems when
- /// DataContext is in the process of changing.
+ /// to the SelectionModel - not via a binding - so it's something that the binding system cannot
+ /// solve. Instead we solve it by not clearing SelectedItems when DataContext is in the process of
+ /// changing.
///
[Fact]
- public void Should_Not_Write_To_Old_DataContext()
+ public void Should_Not_Reset_Selection_Source_When_DataContext_Changes()
{
var vm = new OldDataContextViewModel();
var target = new TestSelector();
@@ -533,33 +561,38 @@ namespace Avalonia.Controls.UnitTests.Primitives
Mode = BindingMode.OneWay,
};
- var selectedItemsBinding = new Binding
+ var selectionBinding = new Binding
{
- Path = "SelectedItems",
+ Path = "Selection",
Mode = BindingMode.OneWay,
};
// Bind Items and SelectedItems to the VM.
target.Bind(TestSelector.ItemsProperty, itemsBinding);
- target.Bind(TestSelector.SelectedItemsProperty, selectedItemsBinding);
+ target.Bind(TestSelector.SelectionProperty, selectionBinding);
// Set DataContext and SelectedIndex
target.DataContext = vm;
target.SelectedIndex = 1;
- // Make sure SelectedItems are written back to VM.
- Assert.Equal(new[] { "bar" }, vm.SelectedItems);
+ // Make sure selection is written back to VM.
+ Assert.Same(vm.Selection, target.Selection);
+ Assert.Equal(new[] { "bar" }, vm.Selection.SelectedItems);
+
+ vm.Selection.SelectionChanged += (s, e) => { };
- // Clear DataContext and ensure that SelectedItems is still set in the VM.
+ // Clear DataContext and ensure that selection is still set in the VM.
target.DataContext = null;
- Assert.Equal(new[] { "bar" }, vm.SelectedItems);
+ Assert.Same(vm.Items, vm.Selection.Source);
+ Assert.NotSame(vm.Selection, target.Selection);
+ Assert.Equal(new[] { "bar" }, vm.Selection.SelectedItems);
- // Ensure target's SelectedItems is now clear.
- Assert.Empty(target.SelectedItems);
+ // Ensure target's selection is now clear.
+ Assert.Empty(target.Selection.SelectedItems);
}
[Fact]
- public void Unbound_SelectedItems_Should_Be_Cleared_When_DataContext_Cleared()
+ public void Unbound_Selected_Items_Should_Be_Cleared_When_DataContext_Cleared()
{
var data = new
{
@@ -577,14 +610,14 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Same(data.Items, target.Items);
- target.SelectedItems.Add("bar");
+ target.Selection.Select(1);
target.DataContext = null;
- Assert.Empty(target.SelectedItems);
+ Assert.Empty(target.Selection.SelectedItems);
}
[Fact]
- public void Adding_To_SelectedItems_Should_Raise_SelectionChanged()
+ public void Adding_Selected_Items_Should_Raise_SelectionChanged()
{
var items = new[] { "foo", "bar", "baz" };
@@ -604,13 +637,13 @@ namespace Avalonia.Controls.UnitTests.Primitives
called = true;
};
- target.SelectedItems.Add("bar");
+ target.Selection.Select(1);
Assert.True(called);
}
[Fact]
- public void Removing_From_SelectedItems_Should_Raise_SelectionChanged()
+ public void Removing_Selected_Items_Should_Raise_SelectionChanged()
{
var items = new[] { "foo", "bar", "baz" };
@@ -630,13 +663,13 @@ namespace Avalonia.Controls.UnitTests.Primitives
called = true;
};
- target.SelectedItems.Remove("bar");
+ target.Selection.Deselect(1);
Assert.True(called);
}
[Fact]
- public void Assigning_SelectedItems_Should_Raise_SelectionChanged()
+ public void Assigning_Selection_Should_Raise_SelectionChanged()
{
var items = new[] { "foo", "bar", "baz" };
@@ -647,22 +680,35 @@ namespace Avalonia.Controls.UnitTests.Primitives
SelectedItem = "bar",
};
- var called = false;
+ var raised = 0;
target.SelectionChanged += (s, e) =>
{
- Assert.Equal(new[] { "foo", "baz" }, e.AddedItems.Cast());
- Assert.Equal(new[] { "bar" }, e.RemovedItems.Cast());
- called = true;
+ if (raised == 0)
+ {
+ Assert.Empty(e.AddedItems.Cast());
+ Assert.Equal(new[] { "bar" }, e.RemovedItems.Cast());
+ }
+ else
+ {
+ Assert.Equal(new[] { "foo", "baz" }, e.AddedItems.Cast());
+ Assert.Empty(e.RemovedItems.Cast());
+ }
+
+ ++raised;
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
- target.SelectedItems = new AvaloniaList("foo", "baz");
- Assert.True(called);
+ var selection = new SelectionModel { Source = items };
+ selection.Select(0);
+ selection.Select(2);
+ target.Selection = selection;
+
+ Assert.Equal(2, raised);
}
-
+
[Fact]
public void Shift_Selecting_From_No_Selection_Selects_From_Start()
{
@@ -679,7 +725,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
var panel = target.Presenter.Panel;
- Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems);
+ Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.Selection.SelectedItems);
Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target));
}
@@ -752,13 +798,13 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Equal(1, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
- Assert.Equal(new[] { "Bar", "Baz", "Qux" }, target.SelectedItems);
+ Assert.Equal(new[] { "Bar", "Baz", "Qux" }, target.Selection.SelectedItems);
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: InputModifiers.Control);
Assert.Equal(2, target.SelectedIndex);
Assert.Equal("Baz", target.SelectedItem);
- Assert.Equal(new[] { "Baz", "Qux" }, target.SelectedItems);
+ Assert.Equal(new[] { "Baz", "Qux" }, target.Selection.SelectedItems);
}
[Fact]
@@ -802,7 +848,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
var panel = target.Presenter.Panel;
- Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems);
+ Assert.Equal(new[] { "Foo", "Bar" }, target.Selection.SelectedItems);
Assert.Equal(new[] { 3, 4 }, SelectedContainers(target));
}
@@ -823,7 +869,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
var panel = target.Presenter.Panel;
- Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems);
+ Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.Selection.SelectedItems);
Assert.Equal(new[] { 3, 4, 5 }, SelectedContainers(target));
}
@@ -844,7 +890,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
var panel = target.Presenter.Panel;
- Assert.Equal(new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, target.SelectedItems);
+ Assert.Equal(new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, target.Selection.SelectedItems);
Assert.Equal(new[] { 0, 1, 2, 3, 4, 5 }, SelectedContainers(target));
}
@@ -891,6 +937,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Shift);
+
VerifyRemoved("Qux");
}
@@ -908,19 +955,19 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
- Assert.Equal(new[] { "Foo" }, target.SelectedItems);
+ Assert.Equal(new[] { "Foo" }, target.Selection.SelectedItems);
_helper.Click((Interactive)target.Presenter.Panel.Children[4], modifiers: InputModifiers.Control);
- Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems);
+ Assert.Equal(new[] { "Foo", "Bar" }, target.Selection.SelectedItems);
_helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: InputModifiers.Control);
- Assert.Equal(new[] { "Foo", "Bar", "Foo" }, target.SelectedItems);
+ Assert.Equal(new[] { "Foo", "Foo", "Bar" }, target.Selection.SelectedItems);
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: InputModifiers.Control);
- Assert.Equal(new[] { "Foo", "Bar", "Foo", "Bar" }, target.SelectedItems);
+ Assert.Equal(new[] { "Foo", "Bar", "Foo", "Bar" }, target.Selection.SelectedItems);
}
[Fact]
@@ -1000,7 +1047,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Presenter.ApplyTemplate();
target.SelectAll();
- Assert.Equal(new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, target.SelectedItems);
+ Assert.Equal(new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, target.Selection.SelectedItems);
}
[Fact]
@@ -1028,7 +1075,7 @@ 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[] { "Foo", "Bar", "Baz" }, target.Selection.SelectedItems);
Assert.Equal(new[] { 1, 2, 3 }, SelectedContainers(target));
}
@@ -1055,13 +1102,13 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.SelectedIndex = 1;
target.SelectRange(2);
- Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems);
+ Assert.Equal(new[] { "Bar", "Baz" }, target.Selection.SelectedItems);
items.RemoveAt(0);
Assert.Equal(0, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
- Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems);
+ Assert.Equal(new[] { "Bar", "Baz" }, target.Selection.SelectedItems);
Assert.Equal(new[] { 0, 1 }, SelectedContainers(target));
}
@@ -1090,7 +1137,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Equal(0, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
- Assert.Equal(new[] { "Bar", "Baz" }, target.SelectedItems);
+ Assert.Equal(new[] { "Bar", "Baz" }, target.Selection.SelectedItems);
Assert.Equal(new[] { 0, 1 }, SelectedContainers(target));
}
@@ -1117,7 +1164,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.SelectAll();
items[1] = "Qux";
- Assert.Equal(new[] { "Foo", "Qux", "Baz" }, target.SelectedItems);
+ Assert.Equal(new[] { "Foo", "Baz" }, target.Selection.SelectedItems);
}
[Fact]
@@ -1135,12 +1182,12 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Presenter.ApplyTemplate();
target.SelectAll();
- Assert.Equal(3, target.SelectedItems.Count);
+ Assert.Equal(3, target.Selection.SelectedItems.Count);
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
- Assert.Equal(1, target.SelectedItems.Count);
- Assert.Equal(new[] { "Foo", }, target.SelectedItems);
+ Assert.Equal(1, target.Selection.SelectedItems.Count);
+ Assert.Equal(new[] { "Foo", }, target.Selection.SelectedItems);
Assert.Equal(new[] { 0 }, SelectedContainers(target));
}
@@ -1159,11 +1206,11 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.Presenter.ApplyTemplate();
target.SelectAll();
- Assert.Equal(3, target.SelectedItems.Count);
+ Assert.Equal(3, target.Selection.SelectedItems.Count);
_helper.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right);
- Assert.Equal(3, target.SelectedItems.Count);
+ Assert.Equal(3, target.Selection.SelectedItems.Count);
}
[Fact]
@@ -1182,11 +1229,11 @@ namespace Avalonia.Controls.UnitTests.Primitives
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: InputModifiers.Shift);
- Assert.Equal(2, target.SelectedItems.Count);
+ Assert.Equal(2, target.Selection.SelectedItems.Count);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right);
- Assert.Equal(1, target.SelectedItems.Count);
+ Assert.Equal(1, target.Selection.SelectedItems.Count);
}
[Fact]
@@ -1212,7 +1259,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Equal(2, target.SelectedIndex);
Assert.Equal(items[2], target.SelectedItem);
- Assert.Equal(new[] { items[2], items[3] }, target.SelectedItems);
+ Assert.Equal(new[] { items[2], items[3] }, target.Selection.SelectedItems);
}
[Fact]
@@ -1232,7 +1279,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right, modifiers: InputModifiers.Shift);
- Assert.Equal(1, target.SelectedItems.Count);
+ Assert.Equal(1, target.Selection.SelectedItems.Count);
}
[Fact]
@@ -1252,7 +1299,29 @@ namespace Avalonia.Controls.UnitTests.Primitives
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right, modifiers: InputModifiers.Control);
- Assert.Equal(1, target.SelectedItems.Count);
+ Assert.Equal(1, target.Selection.SelectedItems.Count);
+ }
+
+ [Fact]
+ public void Ctrl_Left_Click_With_Bound_SelectedIndex_Should_Not_Clear_Selection()
+ {
+ var target = new ListBox
+ {
+ DataContext = new SelectionViewModel(),
+ Template = Template(),
+ Items = new[] { "Foo", "Bar", "Baz" },
+ ItemTemplate = new FuncDataTemplate((x, _) => new TextBlock { Width = 20, Height = 10 }),
+ SelectionMode = SelectionMode.Multiple,
+ [!ListBox.SelectedIndexProperty] = new Binding(nameof(SelectionViewModel.SelectedIndex)),
+ };
+
+ target.ApplyTemplate();
+ target.Presenter.ApplyTemplate();
+ target.Selection.Select(1);
+
+ _helper.Click((Interactive)target.Presenter.Panel.Children[0], modifiers: InputModifiers.Control);
+
+ Assert.Equal(new[] { "Foo", "Bar" }, target.Selection.SelectedItems);
}
private IEnumerable SelectedContainers(SelectingItemsControl target)
@@ -1275,13 +1344,15 @@ namespace Avalonia.Controls.UnitTests.Primitives
private class TestSelector : SelectingItemsControl
{
- public static readonly new AvaloniaProperty SelectedItemsProperty =
- SelectingItemsControl.SelectedItemsProperty;
+ public TestSelector()
+ {
+ SelectionMode = SelectionMode.Multiple;
+ }
- public new IList SelectedItems
+ public new ISelectionModel Selection
{
- get { return base.SelectedItems; }
- set { base.SelectedItems = value; }
+ get => base.Selection;
+ set => base.Selection = value;
}
public new SelectionMode SelectionMode
@@ -1290,22 +1361,40 @@ namespace Avalonia.Controls.UnitTests.Primitives
set { base.SelectionMode = value; }
}
- public new void SelectAll() => base.SelectAll();
- public new void UnselectAll() => base.UnselectAll();
+ public void SelectAll() => Selection.SelectAll();
+ public void UnselectAll() => Selection.ClearSelection();
public void SelectRange(int index) => UpdateSelection(index, true, true);
public void Toggle(int index) => UpdateSelection(index, true, false, true);
}
+ private class SelectionViewModel : NotifyingBase
+ {
+ private int _selectedIndex = -1;
+
+ public int SelectedIndex
+ {
+ get => _selectedIndex;
+ set
+ {
+ if (_selectedIndex != value)
+ {
+ _selectedIndex = value;
+ RaisePropertyChanged(nameof(SelectedIndex));
+ }
+ }
+ }
+ }
+
private class OldDataContextViewModel
{
public OldDataContextViewModel()
{
Items = new List { "foo", "bar" };
- SelectedItems = new List();
+ Selection = new SelectionModel { Source = Items };
}
public List Items { get; }
- public List SelectedItems { get; }
+ public SelectionModel Selection { get; }
}
private class ItemContainer : Control, ISelectable
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs
index b4570ec229..707723f809 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs
@@ -70,7 +70,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
[Fact]
- public void Removing_Selected_Should_Select_Next()
+ public void Removing_Selected_Should_Select_First()
{
var items = new ObservableCollection()
{
@@ -99,10 +99,9 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Same(items[1], target.SelectedItem);
items.RemoveAt(1);
- // Assert for former element [2] now [1] == "3rd"
- Assert.Equal(1, target.SelectedIndex);
- Assert.Same(items[1], target.SelectedItem);
- Assert.Same("3rd", ((TabItem)target.SelectedItem).Name);
+ Assert.Equal(0, target.SelectedIndex);
+ Assert.Same(items[0], target.SelectedItem);
+ Assert.Same("first", ((TabItem)target.SelectedItem).Name);
}
private Control CreateTabStripTemplate(TabStrip parent, INameScope scope)
diff --git a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
index a9e86d71ee..44084eb444 100644
--- a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
@@ -95,7 +95,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
- public void Removal_Should_Set_Next_Tab()
+ public void Removal_Should_Set_First_Tab()
{
var collection = new ObservableCollection()
{
@@ -126,8 +126,7 @@ namespace Avalonia.Controls.UnitTests
target.SelectedItem = collection[1];
collection.RemoveAt(1);
- // compare with former [2] now [1] == "3rd"
- Assert.Same(collection[1], target.SelectedItem);
+ Assert.Same(collection[0], target.SelectedItem);
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
index eaf9f22406..d7004468c3 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
@@ -836,31 +836,31 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal((object)NonControl.StringProperty, txt.Tag);
}
- [Fact]
- public void Binding_To_List_AvaloniaProperty_Is_Operational()
- {
- using (UnitTestApplication.Start(TestServices.MockWindowingPlatform))
- {
- var xaml = @"
-
-
-";
-
- var window = AvaloniaXamlLoader.Parse(xaml);
- var listBox = (ListBox)window.Content;
-
- var vm = new SelectedItemsViewModel()
- {
- Items = new string[] { "foo", "bar", "baz" }
- };
-
- window.DataContext = vm;
-
- Assert.Equal(vm.Items, listBox.Items);
-
- Assert.Equal(vm.SelectedItems, listBox.SelectedItems);
- }
- }
+//// [Fact]
+//// public void Binding_To_List_AvaloniaProperty_Is_Operational()
+//// {
+//// using (UnitTestApplication.Start(TestServices.MockWindowingPlatform))
+//// {
+//// var xaml = @"
+////
+////
+////";
+
+//// var window = AvaloniaXamlLoader.Parse(xaml);
+//// var listBox = (ListBox)window.Content;
+
+//// var vm = new SelectedItemsViewModel()
+//// {
+//// Items = new string[] { "foo", "bar", "baz" }
+//// };
+
+//// window.DataContext = vm;
+
+//// Assert.Equal(vm.Items, listBox.Items);
+
+//// Assert.Equal(vm.SelectedItems, listBox.SelectedItems);
+//// }
+//// }
[Fact]
public void Element_Whitespace_Should_Be_Trimmed()