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()