diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs
index 62070f68474..1fd3960eeef 100644
--- a/src/Avalonia.Controls/TextBox.cs
+++ b/src/Avalonia.Controls/TextBox.cs
@@ -823,6 +823,20 @@ public bool CanRedo
private set => SetAndRaise(CanRedoProperty, ref _canRedo, value);
}
+ ///
+ /// Get the number of lines in the TextBox.
+ ///
+ /// number of lines in the TextBox, or -1 if no layout information is available
+ ///
+ /// If Wrap == true, changing the width of the TextBox may change this value.
+ /// The value returned is the number of lines in the entire TextBox, regardless of how many are
+ /// currently in view.
+ ///
+ public int GetLineCount()
+ {
+ return this._presenter?.TextLayout.TextLines.Count ?? -1;
+ }
+
///
/// Raised when content is being copied to the clipboard
///
diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
index d6b24b627f9..8e4f3a8a288 100644
--- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs
@@ -1214,6 +1214,106 @@ public void MinLines_Sets_ScrollViewer_MinHeight_With_TextPresenter_Margin(int m
Assert.Equal((minLines * target.LineHeight) + textPresenterMargin.Top + textPresenterMargin.Bottom, scrollViewer.MinHeight);
}
}
+
+ [Theory]
+ [InlineData(null, 1)]
+ [InlineData("", 1)]
+ [InlineData("Hello", 1)]
+ [InlineData("Hello\r\nWorld", 2)]
+ public void LineCount_Is_Correct(string? text, int lineCount)
+ {
+ using (UnitTestApplication.Start(Services))
+ {
+ var target = new TextBox
+ {
+ Template = CreateTemplate(),
+ Text = text,
+ AcceptsReturn = true
+ };
+
+ var impl = CreateMockTopLevelImpl();
+ var topLevel = new TestTopLevel(impl.Object)
+ {
+ Template = CreateTopLevelTemplate()
+ };
+ topLevel.Content = target;
+ topLevel.ApplyTemplate();
+ topLevel.LayoutManager.ExecuteInitialLayoutPass();
+
+ target.ApplyTemplate();
+ target.Measure(Size.Infinity);
+
+ Assert.Equal(lineCount, target.GetLineCount());
+ }
+ }
+
+ [Fact]
+ public void Unmeasured_TextBox_Has_Negative_LineCount()
+ {
+ var b = new TextBox();
+ Assert.Equal(-1, b.GetLineCount());
+ }
+
+ [Fact]
+ public void LineCount_Is_Correct_After_Text_Change()
+ {
+ using (UnitTestApplication.Start(Services))
+ {
+ var target = new TextBox
+ {
+ Template = CreateTemplate(),
+ Text = "Hello",
+ AcceptsReturn = true
+ };
+
+ var impl = CreateMockTopLevelImpl();
+ var topLevel = new TestTopLevel(impl.Object)
+ {
+ Template = CreateTopLevelTemplate()
+ };
+ topLevel.Content = target;
+ topLevel.ApplyTemplate();
+ topLevel.LayoutManager.ExecuteInitialLayoutPass();
+
+ target.ApplyTemplate();
+ target.Measure(Size.Infinity);
+
+ Assert.Equal(1, target.GetLineCount());
+
+ target.Text = "Hello\r\nWorld";
+
+ Assert.Equal(2, target.GetLineCount());
+ }
+ }
+
+ [Fact]
+ public void Visible_LineCount_DoesNot_Affect_LineCount()
+ {
+ using (UnitTestApplication.Start(Services))
+ {
+ var target = new TextBox
+ {
+ Template = CreateTemplate(),
+ Text = "Hello\r\nWorld\r\nHello\r\nAvalonia",
+ AcceptsReturn = true,
+ MaxLines = 2,
+ };
+
+ var impl = CreateMockTopLevelImpl();
+ var topLevel = new TestTopLevel(impl.Object)
+ {
+ Template = CreateTopLevelTemplate()
+ };
+ topLevel.Content = target;
+ topLevel.ApplyTemplate();
+ topLevel.LayoutManager.ExecuteInitialLayoutPass();
+
+ target.ApplyTemplate();
+ target.Measure(Size.Infinity);
+
+ Assert.Equal(4, target.GetLineCount());
+ }
+ }
[Fact]
public void CanUndo_CanRedo_Is_False_When_Initialized()