Browse Source

Merge branch 'master' into dotnetfoundation

Dariusz Komosiński 5 years ago
parent
commit
d35d6b799e

+ 5 - 9
src/Avalonia.Styling/StyledElement.cs

@@ -453,18 +453,14 @@ namespace Avalonia
                 
                 NotifyResourcesChanged(new ResourcesChangedEventArgs());
 
-                if (Parent is ILogicalRoot || Parent?.IsAttachedToLogicalTree == true || this is ILogicalRoot)
-                {
-                    var newRoot = FindLogicalRoot(this);
-
-                    if (newRoot == null)
-                    {
-                        throw new AvaloniaInternalException("Parent is attached to logical tree but cannot find root.");
-                    }
+                var newRoot = FindLogicalRoot(this);
 
+                if (newRoot is object)
+                {
                     var e = new LogicalTreeAttachmentEventArgs(newRoot, this, parent);
                     OnAttachedToLogicalTreeCore(e);
                 }
+
 #nullable disable
                 RaisePropertyChanged(
                     ParentProperty,
@@ -618,7 +614,7 @@ namespace Avalonia
             }
         }
 
-        private static ILogicalRoot? FindLogicalRoot(IStyleHost e)
+        private static ILogicalRoot? FindLogicalRoot(IStyleHost? e)
         {
             while (e != null)
             {

+ 51 - 0
tests/Avalonia.Styling.UnitTests/StyledElementTests.cs

@@ -8,6 +8,10 @@ using Xunit;
 using Avalonia.LogicalTree;
 using Avalonia.Controls;
 using System.ComponentModel;
+using Avalonia.Controls.Primitives;
+using Avalonia.Markup.Xaml.Templates;
+using Avalonia.Controls.Templates;
+using Avalonia.Data;
 
 namespace Avalonia.Styling.UnitTests
 {
@@ -572,6 +576,53 @@ namespace Avalonia.Styling.UnitTests
                 Times.Once);
         }
 
+        [Fact]
+        public void SetParent_Does_Not_Crash_Due_To_Reentrancy()
+        {
+            // Issue #3708
+            var app = UnitTestApplication.Start(TestServices.StyledWindow);
+
+            ContentControl target;
+            var root = new TestRoot
+            {
+                DataContext = false,
+                Child = target = new ContentControl
+                {
+                    Styles =
+                    {
+                        new Style(x => x.OfType<ContentControl>())
+                        {
+                            Setters =
+                            {
+                                new Setter(
+                                    ContentControl.ContentProperty,
+                                    new FuncTemplate<IControl>(() => new TextBlock { Text = "Enabled" })),
+                            },
+                        },
+                        new Style(x => x.OfType<ContentControl>().Class(":disabled"))
+                        {
+                            Setters =
+                            {
+                                new Setter(
+                                    ContentControl.ContentProperty,
+                                    new FuncTemplate<IControl>(() => new TextBlock { Text = "Disabled" })),
+                            },
+                        },
+                    },
+                    [!ContentControl.IsEnabledProperty] = new Binding(),
+                }
+            };
+
+            root.Measure(Size.Infinity);
+            root.Arrange(new Rect(0, 0, 100, 100));
+
+            var textBlock = Assert.IsType<TextBlock>(target.Content);
+            Assert.Equal("Disabled", textBlock.Text);
+
+            // #3708 was crashing here with AvaloniaInternalException.
+            root.Child = null;
+        }
+
         private interface IDataContextEvents
         {
             event EventHandler DataContextBeginUpdate;