diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index e9867e4503..13bc4ed124 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -70,6 +70,14 @@ namespace Avalonia.Controls
Brushes.Black,
inherits: true);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty MaxLinesProperty =
+ AvaloniaProperty.Register(
+ nameof(MaxLines),
+ validate: IsValidMaxLines);
+
///
/// Defines the property.
///
@@ -222,6 +230,15 @@ namespace Avalonia.Controls
set { SetValue(ForegroundProperty, value); }
}
+ ///
+ /// Gets or sets the maximum number of text lines.
+ ///
+ public int MaxLines
+ {
+ get => GetValue(MaxLinesProperty);
+ set => SetValue(MaxLinesProperty, value);
+ }
+
///
/// Gets or sets the control's text wrapping mode.
///
@@ -404,7 +421,8 @@ namespace Avalonia.Controls
TextTrimming,
TextDecorations,
constraint.Width,
- constraint.Height);
+ constraint.Height,
+ MaxLines);
}
///
@@ -451,5 +469,7 @@ namespace Avalonia.Controls
InvalidateMeasure();
}
+
+ private static bool IsValidMaxLines(int maxLines) => maxLines >= 0;
}
}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
index 8d4475d1c3..720185a3ad 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
+++ b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
@@ -32,6 +32,7 @@ namespace Avalonia.Media.TextFormatting
/// The text decorations.
/// The maximum width.
/// The maximum height.
+ /// The maximum number of text lines.
/// The text style overrides.
public TextLayout(
string text,
@@ -44,6 +45,7 @@ namespace Avalonia.Media.TextFormatting
TextDecorationCollection textDecorations = null,
double maxWidth = double.PositiveInfinity,
double maxHeight = double.PositiveInfinity,
+ int maxLines = 0,
IReadOnlyList textStyleOverrides = null)
{
_text = string.IsNullOrEmpty(text) ?
@@ -59,6 +61,8 @@ namespace Avalonia.Media.TextFormatting
MaxHeight = maxHeight;
+ MaxLines = maxLines;
+
UpdateLayout();
}
@@ -73,6 +77,12 @@ namespace Avalonia.Media.TextFormatting
///
public double MaxHeight { get; }
+
+ ///
+ /// Gets the maximum number of text lines.
+ ///
+ public double MaxLines { get; }
+
///
/// Gets the text lines.
///
@@ -192,7 +202,7 @@ namespace Avalonia.Media.TextFormatting
var currentPosition = 0;
- while (currentPosition < _text.Length)
+ while (currentPosition < _text.Length && (MaxLines == 0 || textLines.Count < MaxLines))
{
int length;
@@ -222,7 +232,7 @@ namespace Avalonia.Media.TextFormatting
var remainingLength = length;
- while (remainingLength > 0)
+ while (remainingLength > 0 && (MaxLines == 0 || textLines.Count < MaxLines))
{
var textSlice = _text.AsSlice(currentPosition, remainingLength);
diff --git a/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs
index 0d9fd31e52..fb63df0407 100644
--- a/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs
+++ b/tests/Avalonia.Skia.UnitTests/TextLayoutTests.cs
@@ -506,6 +506,27 @@ namespace Avalonia.Skia.UnitTests
}
}
+ [InlineData("0123456789\r\n0123456789\r\n0123456789", 1, 1)]
+ [InlineData("0123456789\r\n0123456789\r\n0123456789", 2, 2)]
+ [InlineData("0123456789\r\n0123456789\r\n0123456789", 3, 3)]
+ [InlineData("0123456789\r\n0123456789\r\n0123456789", 4, 3)]
+ [Theory]
+ public void Should_Not_Exceed_MaxLines(string text, int maxLines, int expectedLines)
+ {
+ using (Start())
+ {
+ var layout = new TextLayout(
+ text,
+ Typeface.Default,
+ 12,
+ Brushes.Black,
+ maxWidth: 50,
+ maxLines: maxLines);
+
+ Assert.Equal(expectedLines, layout.TextLines.Count);
+ }
+ }
+
private const string Text = "日本でTest一番読まれている英字新聞・ジャパンタイムズが発信する国内外ニュースと、様々なジャンルの特集記事。";
[Fact(Skip= "Only used for profiling.")]