diff --git a/.gitignore b/.gitignore
index b5a46e16f4..7d672c7755 100644
--- a/.gitignore
+++ b/.gitignore
@@ -117,6 +117,7 @@ ClientBin/
*.[Pp]ublish.xml
*.pfx
*.publishsettings
+Events_Avalonia.cs
# RIA/Silverlight projects
Generated_Code/
diff --git a/.ncrunch/Sandbox.v3.ncrunchproject b/.ncrunch/Sandbox.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/Sandbox.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index 24398accbb..7e2bbc13bc 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -241,7 +241,6 @@ partial class Build : NukeBuild
RunCoreTest("Avalonia.Visuals.UnitTests");
RunCoreTest("Avalonia.Skia.UnitTests");
RunCoreTest("Avalonia.ReactiveUI.UnitTests");
- RunCoreTest("Avalonia.ReactiveUI.Events.UnitTests");
});
Target RunRenderTests => _ => _
diff --git a/src/Avalonia.Controls/Selection/SelectionModel.cs b/src/Avalonia.Controls/Selection/SelectionModel.cs
index fd27cb340a..3b5d57a7b8 100644
--- a/src/Avalonia.Controls/Selection/SelectionModel.cs
+++ b/src/Avalonia.Controls/Selection/SelectionModel.cs
@@ -242,12 +242,7 @@ namespace Avalonia.Controls.Selection
{
using var update = BatchUpdate();
var o = update.Operation;
- var range = CoerceRange(start, end);
-
- if (range.Begin == -1)
- {
- return;
- }
+ var range = new IndexRange(Math.Max(0, start), end);
if (RangesEnabled)
{
diff --git a/src/Avalonia.ReactiveUI.Events/Avalonia.ReactiveUI.Events.csproj b/src/Avalonia.ReactiveUI.Events/Avalonia.ReactiveUI.Events.csproj
index b95c8946e2..75eeb92f42 100644
--- a/src/Avalonia.ReactiveUI.Events/Avalonia.ReactiveUI.Events.csproj
+++ b/src/Avalonia.ReactiveUI.Events/Avalonia.ReactiveUI.Events.csproj
@@ -1,7 +1,7 @@
netstandard2.0
- Avalonia.ReactiveUI
+ Avalonia.ReactiveUI.Events
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
index 192d6e0286..514d3b5475 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
@@ -1813,6 +1813,88 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Equal(1, raised);
}
+ [Fact]
+ public void Handles_Removing_Last_Item_In_Two_Controls_With_Bound_SelectedIndex()
+ {
+ var items = new ObservableCollection { "foo" };
+
+ // Simulates problem with TabStrip and Carousel with bound SelectedIndex.
+ var tabStrip = new TestSelector
+ {
+ Items = items,
+ SelectionMode = SelectionMode.AlwaysSelected,
+ };
+
+ var carousel = new TestSelector
+ {
+ Items = items,
+ [!Carousel.SelectedIndexProperty] = tabStrip[!TabStrip.SelectedIndexProperty],
+ };
+
+ var tabStripRaised = 0;
+ var carouselRaised = 0;
+
+ tabStrip.SelectionChanged += (s, e) =>
+ {
+ Assert.Equal(new[] { "foo" }, e.RemovedItems);
+ Assert.Empty(e.AddedItems);
+ ++tabStripRaised;
+ };
+
+ carousel.SelectionChanged += (s, e) =>
+ {
+ Assert.Equal(new[] { "foo" }, e.RemovedItems);
+ Assert.Empty(e.AddedItems);
+ ++carouselRaised;
+ };
+
+ items.RemoveAt(0);
+
+ Assert.Equal(1, tabStripRaised);
+ Assert.Equal(1, carouselRaised);
+ }
+
+ [Fact]
+ public void Handles_Removing_Last_Item_In_Controls_With_Bound_SelectedItem()
+ {
+ var items = new ObservableCollection { "foo" };
+
+ // Simulates problem with TabStrip and Carousel with bound SelectedItem.
+ var tabStrip = new TestSelector
+ {
+ Items = items,
+ SelectionMode = SelectionMode.AlwaysSelected,
+ };
+
+ var carousel = new TestSelector
+ {
+ Items = items,
+ [!Carousel.SelectedItemProperty] = tabStrip[!TabStrip.SelectedItemProperty],
+ };
+
+ var tabStripRaised = 0;
+ var carouselRaised = 0;
+
+ tabStrip.SelectionChanged += (s, e) =>
+ {
+ Assert.Equal(new[] { "foo" }, e.RemovedItems);
+ Assert.Empty(e.AddedItems);
+ ++tabStripRaised;
+ };
+
+ carousel.SelectionChanged += (s, e) =>
+ {
+ Assert.Equal(new[] { "foo" }, e.RemovedItems);
+ Assert.Empty(e.AddedItems);
+ ++carouselRaised;
+ };
+
+ items.RemoveAt(0);
+
+ Assert.Equal(1, tabStripRaised);
+ Assert.Equal(1, carouselRaised);
+ }
+
private static void Prepare(SelectingItemsControl target)
{
var root = new TestRoot
diff --git a/tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj b/tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj
deleted file mode 100644
index 19a6fd138e..0000000000
--- a/tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
- netcoreapp3.1
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/Avalonia.ReactiveUI.Events.UnitTests/BasicControlEventsTest.cs b/tests/Avalonia.ReactiveUI.Events.UnitTests/BasicControlEventsTest.cs
deleted file mode 100644
index 1092c98246..0000000000
--- a/tests/Avalonia.ReactiveUI.Events.UnitTests/BasicControlEventsTest.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System;
-using System.Reactive.Linq;
-using Avalonia.Controls;
-using Avalonia.UnitTests;
-using Xunit;
-
-namespace Avalonia.ReactiveUI.Events.UnitTests
-{
- public class BasicControlEventsTest
- {
- public class EventsControl : UserControl
- {
- public bool IsAttached { get; private set; }
-
- public EventsControl()
- {
- var attached = this
- .Events()
- .AttachedToVisualTree
- .Select(args => true);
-
- this.Events()
- .DetachedFromVisualTree
- .Select(args => false)
- .Merge(attached)
- .Subscribe(marker => IsAttached = marker);
- }
- }
-
- [Fact]
- public void Should_Generate_Events_Wrappers()
- {
- var root = new TestRoot();
- var control = new EventsControl();
- Assert.False(control.IsAttached);
-
- root.Child = control;
- Assert.True(control.IsAttached);
-
- root.Child = null;
- Assert.False(control.IsAttached);
- }
- }
-}