diff --git a/build/Microsoft.Reactive.Testing.props b/build/Microsoft.Reactive.Testing.props
index 777bc4bb53..a0ba2163bb 100644
--- a/build/Microsoft.Reactive.Testing.props
+++ b/build/Microsoft.Reactive.Testing.props
@@ -1,5 +1,5 @@
-
+
diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props
index 7caeaf90ae..f827cb9a32 100644
--- a/build/ReactiveUI.props
+++ b/build/ReactiveUI.props
@@ -1,5 +1,5 @@
-
+
diff --git a/dirs.proj b/dirs.proj
index e56320e73f..4b3b1183f0 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -8,7 +8,8 @@
-
+
+
diff --git a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
index 37c7178c56..649c64ab1d 100644
--- a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
+++ b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
@@ -28,12 +28,11 @@ namespace VirtualizationDemo.ViewModels
public MainWindowViewModel()
{
this.WhenAnyValue(x => x.ItemCount).Subscribe(ResizeItems);
+ RecreateCommand = ReactiveCommand.Create(() => Recreate());
- RecreateCommand = ReactiveCommand.Create(Recreate);
+ AddItemCommand = ReactiveCommand.Create(() => AddItem());
- AddItemCommand = ReactiveCommand.Create(AddItem);
-
- RemoveItemCommand = ReactiveCommand.Create(Remove);
+ RemoveItemCommand = ReactiveCommand.Create(() => Remove());
SelectFirstCommand = ReactiveCommand.Create(() => SelectItem(0));
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index cc0c5f52be..a50175bdee 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -304,6 +304,11 @@ namespace Avalonia.Controls.Primitives
{
base.ItemsCollectionChanged(sender, e);
+ if (_updateCount > 0)
+ {
+ return;
+ }
+
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
@@ -1071,13 +1076,20 @@ namespace Avalonia.Controls.Primitives
private void UpdateFinished()
{
- if (_updateSelectedIndex != int.MinValue)
+ if (_updateSelectedItem != null)
{
- SelectedIndex = _updateSelectedIndex;
+ SelectedItem = _updateSelectedItem;
}
- else if (_updateSelectedItem != null)
+ else
{
- SelectedItem = _updateSelectedItem;
+ if (ItemCount == 0 && SelectedIndex != -1)
+ {
+ SelectedIndex = -1;
+ }
+ else
+ {
+ SelectedIndex = _updateSelectedIndex != int.MinValue ? _updateSelectedIndex : 0;
+ }
}
}
diff --git a/src/Avalonia.ReactiveUI/RoutedViewHost.cs b/src/Avalonia.ReactiveUI/RoutedViewHost.cs
index 5d96fd264a..ac5db32c14 100644
--- a/src/Avalonia.ReactiveUI/RoutedViewHost.cs
+++ b/src/Avalonia.ReactiveUI/RoutedViewHost.cs
@@ -118,4 +118,4 @@ namespace Avalonia.ReactiveUI
Content = viewInstance;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
index 0ff0285a04..efcc555159 100644
--- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
+++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
@@ -540,7 +540,7 @@ namespace Avalonia.Rendering
foreach (var visual in _recalculateChildren)
{
var node = scene.FindNode(visual);
- ((VisualNode)node)?.UpdateChildren(scene);
+ ((VisualNode)node)?.SortChildren(scene);
}
_recalculateChildren.Clear();
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
index 2fcea8c205..161cbc099e 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
@@ -53,6 +53,16 @@ namespace Avalonia.Rendering.SceneGraph
if (visual.VisualRoot != null)
{
+ if (node?.Parent != null &&
+ visual.VisualParent != null &&
+ node.Parent.Visual != visual.VisualParent)
+ {
+ // The control has changed parents. Remove the node and recurse into the new parent node.
+ ((VisualNode)node.Parent).RemoveChild(node);
+ Deindex(scene, node);
+ node = (VisualNode)scene.FindNode(visual.VisualParent);
+ }
+
if (visual.IsVisible)
{
// If the node isn't yet part of the scene, find the nearest ancestor that is.
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
index f079023c6d..f579bf0a62 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
@@ -174,12 +174,12 @@ namespace Avalonia.Rendering.SceneGraph
///
/// Sorts the collection according to the order of the visual's
- /// children and their z-index and removes controls that are no longer children.
+ /// children and their z-index.
///
/// The scene that the node is a part of.
- public void UpdateChildren(Scene scene)
+ public void SortChildren(Scene scene)
{
- if (_children == null || _children.Count == 0)
+ if (_children == null || _children.Count <= 1)
{
return;
}
@@ -193,12 +193,9 @@ namespace Avalonia.Rendering.SceneGraph
keys.Add(((long)zIndex << 32) + i);
}
- var toRemove = _children.ToList();
-
keys.Sort();
_children.Clear();
-
foreach (var i in keys)
{
var child = Visual.VisualChildren[(int)(i & 0xffffffff)];
@@ -207,14 +204,8 @@ namespace Avalonia.Rendering.SceneGraph
if (node != null)
{
_children.Add(node);
- toRemove.Remove(node);
}
}
-
- foreach (var node in toRemove)
- {
- scene.Remove(node);
- }
}
///
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
index 4e4d92afdc..8f27ebe853 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
@@ -109,6 +109,28 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.True(items[1].IsSelected);
}
+ [Fact]
+ public void Setting_SelectedIndex_During_Initialize_Should_Select_Item_When_AlwaysSelected_Is_Used()
+ {
+ var listBox = new ListBox
+ {
+ SelectionMode = SelectionMode.Single | SelectionMode.AlwaysSelected
+ };
+
+ listBox.BeginInit();
+
+ listBox.SelectedIndex = 1;
+ var items = new AvaloniaList();
+ listBox.Items = items;
+ items.Add("A");
+ items.Add("B");
+ items.Add("C");
+
+ listBox.EndInit();
+
+ Assert.Equal("B", listBox.SelectedItem);
+ }
+
[Fact]
public void Setting_SelectedIndex_Before_ApplyTemplate_Should_Set_Item_IsSelected_True()
{
diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs
index 5b97ab7ae6..ff9e259eab 100644
--- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs
+++ b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_AttachedProperty.cs
@@ -120,6 +120,8 @@ namespace Avalonia.Markup.UnitTests.Parsers
var result = run();
result.Item1.Subscribe(x => { });
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
GC.Collect();
Assert.Null(result.Item2.Target);
diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
index 568ccb81d8..b4743e900d 100644
--- a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
@@ -96,6 +96,55 @@ namespace Avalonia.Visuals.UnitTests.Rendering
Assert.Equal(new List { root, decorator, border, canvas }, result);
}
+ [Fact]
+ public void Should_Add_Dirty_Rect_On_Child_Remove()
+ {
+ var dispatcher = new ImmediateDispatcher();
+ var loop = new Mock();
+
+ Decorator decorator;
+ Border border;
+ var root = new TestRoot
+ {
+ Width = 100,
+ Height= 100,
+ Child = decorator = new Decorator
+ {
+ Child = border = new Border
+ {
+ Width = 50,
+ Height = 50,
+ Background = Brushes.Red,
+ },
+ }
+ };
+
+ root.Measure(Size.Infinity);
+ root.Arrange(new Rect(root.DesiredSize));
+
+ var sceneBuilder = new SceneBuilder();
+ var target = new DeferredRenderer(
+ root,
+ loop.Object,
+ sceneBuilder: sceneBuilder,
+ dispatcher: dispatcher);
+
+ root.Renderer = target;
+ target.Start();
+ RunFrame(target);
+
+ decorator.Child = null;
+
+ RunFrame(target);
+
+ var scene = target.UnitTestScene();
+ var stackNode = scene.FindNode(decorator);
+ var dirty = scene.Layers[0].Dirty.ToList();
+
+ Assert.Equal(1, dirty.Count);
+ Assert.Equal(new Rect(25, 25, 50, 50), dirty[0]);
+ }
+
[Fact]
public void Should_Update_VisualNode_Order_On_Child_Remove_Insert()
{
diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
index dcf23e94e2..13bcd27240 100644
--- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
@@ -521,8 +521,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
moveFromNode = (VisualNode)scene.FindNode(moveFrom);
moveToNode = (VisualNode)scene.FindNode(moveTo);
- moveFromNode.UpdateChildren(scene);
- moveToNode.UpdateChildren(scene);
+ moveFromNode.SortChildren(scene);
+ moveToNode.SortChildren(scene);
sceneBuilder.Update(scene, moveFrom);
sceneBuilder.Update(scene, moveTo);
sceneBuilder.Update(scene, moveMe);
diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs
index d4f7a6a142..24ba2d1c48 100644
--- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs
@@ -99,7 +99,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
var node = new VisualNode(Mock.Of(), null);
var scene = new Scene(Mock.Of());
- node.UpdateChildren(scene);
+ node.SortChildren(scene);
}
}
}