diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Styling/StyledElement.cs index 241ca1d0ec..1a4bc5b3a3 100644 --- a/src/Avalonia.Styling/StyledElement.cs +++ b/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) { diff --git a/tests/Avalonia.Styling.UnitTests/StyledElementTests.cs b/tests/Avalonia.Styling.UnitTests/StyledElementTests.cs index 6d02db4b6e..e336fbf838 100644 --- a/tests/Avalonia.Styling.UnitTests/StyledElementTests.cs +++ b/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()) + { + Setters = + { + new Setter( + ContentControl.ContentProperty, + new FuncTemplate(() => new TextBlock { Text = "Enabled" })), + }, + }, + new Style(x => x.OfType().Class(":disabled")) + { + Setters = + { + new Setter( + ContentControl.ContentProperty, + new FuncTemplate(() => new TextBlock { Text = "Disabled" })), + }, + }, + }, + [!ContentControl.IsEnabledProperty] = new Binding(), + } + }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(0, 0, 100, 100)); + + var textBlock = Assert.IsType(target.Content); + Assert.Equal("Disabled", textBlock.Text); + + // #3708 was crashing here with AvaloniaInternalException. + root.Child = null; + } + private interface IDataContextEvents { event EventHandler DataContextBeginUpdate;