Repository: AvaloniaUI/Avalonia

Issue:
Changing TextBlock.Text placed inside an infinite-sized invisible container doesn't re-measure the TextBlock
### Describe the bug

Inside a container which is infinite on both sides and invisible, changing the `Text` of a `TextBlock`, then making the container visible, doesn't re-measure the TextBlock: the text is effectively still invisible.

### To Reproduce

```xaml
<StackPanel>
  <Button Content="Change Text" Click="Button_OnClick" />
  <Grid Name="Grid" RowDefinitions="Auto" ColumnDefinitions="Auto" IsVisible="False">
    <TextBlock Name="TextBlock" />
  </Grid>
</StackPanel>
```

```csharp
private void Button_OnClick(object sender, RoutedEventArgs e)
{
    TextBlock.Text = "Foo";
    Grid.IsVisible = true;
}
```

Click on the button.

### Expected behavior

The text should be visible after clicking the button.

### Avalonia version

11.2.1, 11.2.2

### OS

_No response_

### Additional context

Introduced by #17271
The initial `_constraint` is `Infinite`, which matches the infinite container.
If the `TextLayout` gets created for any reason, it won't be properly invalidated.


Hints:

Patch:
diff --git a/src/Avalonia.Controls/SelectableTextBlock.cs b/src/Avalonia.Controls/SelectableTextBlock.cs
index 8f1248027c5..b0fd78b321e 100644
--- a/src/Avalonia.Controls/SelectableTextBlock.cs
+++ b/src/Avalonia.Controls/SelectableTextBlock.cs
@@ -224,12 +224,14 @@ protected override TextLayout CreateTextLayout(string? text)
                 textSource = new FormattedTextSource(text ?? "", defaultProperties, textStyleOverrides);
             }
 
+            var maxSize = GetMaxSizeFromConstraint();
+
             return new TextLayout(
                 textSource,
                 paragraphProperties,
                 TextTrimming,
-                _constraint.Width,
-                _constraint.Height,
+                maxSize.Width,
+                maxSize.Height,
                 MaxLines);
         }
 
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index 0afafe44b87..f6a0cdbdfdf 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -162,7 +162,7 @@ public class TextBlock : Control, IInlineHost
                 nameof(Inlines), t => t.Inlines, (t, v) => t.Inlines = v);
 
         private TextLayout? _textLayout;
-        protected Size _constraint = Size.Infinity;
+        protected Size _constraint = new(double.NaN, double.NaN);
         protected IReadOnlyList<TextRun>? _textRuns;
         private InlineCollection? _inlines;
 
@@ -366,6 +366,13 @@ public InlineCollection? Inlines
 
         internal bool HasComplexContent => Inlines != null && Inlines.Count > 0;
 
+        private protected Size GetMaxSizeFromConstraint()
+        {
+            var maxWidth = double.IsNaN(_constraint.Width) ? 0.0 : _constraint.Width;
+            var maxHeight = double.IsNaN(_constraint.Height) ? 0.0 : _constraint.Height;
+            return new Size(maxWidth, maxHeight);
+        }
+
         /// <summary>
         /// The BaselineOffset property provides an adjustment to baseline offset
         /// </summary>
@@ -670,12 +677,14 @@ protected virtual TextLayout CreateTextLayout(string? text)
                 textSource = new SimpleTextSource(text ?? "", defaultProperties);
             }
 
+            var maxSize = GetMaxSizeFromConstraint();
+
             return new TextLayout(
                 textSource,
                 paragraphProperties,
                 TextTrimming,
-                _constraint.Width,
-                _constraint.Height,
+                maxSize.Width,
+                maxSize.Height,
                 MaxLines);
         }
 


Test patch:
diff --git a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs
index 3d7c6e6c1bd..4a7281fae3a 100644
--- a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs
@@ -1,3 +1,4 @@
+using System;
 using Avalonia.Controls.Documents;
 using Avalonia.Controls.Templates;
 using Avalonia.Data;
@@ -33,7 +34,9 @@ public void Calling_Measure_Should_Update_TextLayout()
             {
                 var textBlock = new TestTextBlock { Text = "Hello World" };
 
-                Assert.Equal(Size.Infinity, textBlock.Constraint);
+                var constraint = textBlock.Constraint;
+                Assert.True(double.IsNaN(constraint.Width));
+                Assert.True(double.IsNaN(constraint.Height));
 
                 textBlock.Measure(new Size(100, 100));
 
@@ -413,6 +416,24 @@ public void TextBlock_TextLines_Should_Be_Empty()
             }
         }
 
+        [Fact]
+        public void TextBlock_With_Infinite_Size_Should_Be_Remeasured_After_TextLayout_Created()
+        {
+            using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
+
+            var target = new TextBlock { Text = "" };
+            var layout = target.TextLayout;
+
+            Assert.Equal(0.0, layout.MaxWidth);
+            Assert.Equal(0.0, layout.MaxHeight);
+
+            target.Text = "foo";
+            target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+
+            Assert.True(target.DesiredSize.Width > 0);
+            Assert.True(target.DesiredSize.Height > 0);
+        }
+
         private class TestTextBlock : TextBlock
         {
             public Size Constraint => _constraint;