Maximum number of lines for a Wrap TextBlock
Asked Answered
E

7

32

I have a TextBlock with the following setting:

TextWrapping="Wrap"

Can I determine the maximum number of lines?

for example consider the following string TextBlock.Text:

This is a very good horse under the blackboard!!

It currently has been shows like this:

This is a very 
good horse under 
the blackboard!!

I need that to become something like:

This is a very 
good horse ...

any solution?

Emersen answered 30/11, 2012 at 1:44 Comment(0)
G
54

Update (for UWP)

In UWP Apps you don't need this and can use the TextBlock property MaxLines (see MSDN)


Original Answer:

If you have a specific LineHeight you can calculate the maximum height for the TextBlock.

Example:

TextBlock with maximum 3 lines

<TextBlock 
  Width="300"
  TextWrapping="Wrap" 
  TextTrimming="WordEllipsis" 
  FontSize="24" 
  LineStackingStrategy="BlockLineHeight"
  LineHeight="28"
  MaxHeight="84">YOUR TEXT</TextBlock>

This is all that you need to get your requirement working.

How to do this dynamically?

Just create a new control in C#/VB.NET that extends TextBlock and give it a new DependencyProperty int MaxLines.
Then override the OnApplyTemplate() method and set the MaxHeight based on the LineHeight * MaxLines.

That's just a basic explanation on how you could solve this problem!

Gascon answered 19/11, 2013 at 19:53 Comment(0)
G
7

Based tobi.at's and gt's answer I have created this MaxLines behaviour. Crucially it doesn't depend upon setting the LineHeight property by calculating the line height from the font. You still need to set TextWrapping and TextTrimming for it the TextBox to be render as you would like.

<TextBlock behaviours:NumLinesBehaviour.MaxLines="3" TextWrapping="Wrap" TextTrimming="CharacterEllipsis" Text="Some text here"/>

There in also a MinLines behaviour which can be different or set to the same number as the MaxLines behaviour to set the number of lines.

public class NumLinesBehaviour : Behavior<TextBlock>
{
    TextBlock textBlock => AssociatedObject;

    public static readonly DependencyProperty MaxLinesProperty =
        DependencyProperty.RegisterAttached(
            "MaxLines",
            typeof(int),
            typeof(NumLinesBehaviour),
            new PropertyMetadata(default(int), OnMaxLinesPropertyChangedCallback));

    public static void SetMaxLines(DependencyObject element, int value)
    {
        element.SetValue(MaxLinesProperty, value);
    }

    public static int GetMaxLines(DependencyObject element)
    {
        return (int)element.GetValue(MaxLinesProperty);
    }

    private static void OnMaxLinesPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        TextBlock element = d as TextBlock;
        element.MaxHeight = getLineHeight(element) * GetMaxLines(element);
    }

    public static readonly DependencyProperty MinLinesProperty =
        DependencyProperty.RegisterAttached(
            "MinLines",
            typeof(int),
            typeof(NumLinesBehaviour),
            new PropertyMetadata(default(int), OnMinLinesPropertyChangedCallback));

    public static void SetMinLines(DependencyObject element, int value)
    {
        element.SetValue(MinLinesProperty, value);
    }

    public static int GetMinLines(DependencyObject element)
    {
        return (int)element.GetValue(MinLinesProperty);
    }

    private static void OnMinLinesPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        TextBlock element = d as TextBlock;
        element.MinHeight = getLineHeight(element) * GetMinLines(element);
    }

    private static double getLineHeight(TextBlock textBlock)
    {
        double lineHeight = textBlock.LineHeight;
        if (double.IsNaN(lineHeight))
            lineHeight = Math.Ceiling(textBlock.FontSize * textBlock.FontFamily.LineSpacing);
        return lineHeight;
    }
}
Giralda answered 31/3, 2017 at 11:43 Comment(1)
After removing the unnecessary stuff, like ": Behavior<TextBlock>" and the following lines of code, that obviously derived from the Behavior base class, it worked like a charm. So only use a static class, with the dependencyProperties.Hornblende
O
6

If you have Height, TextWrapping, and TextTrimming all set, it will behave exactly like you want:

<TextBlock Height="60" FontSize="22" FontWeight="Thin"
    TextWrapping="Wrap" TextTrimming="CharacterEllipsis">

The above code will wrap up to two lines, then use CharacterEllipsis beyond that point.

Ovine answered 21/10, 2014 at 19:38 Comment(0)
M
2

you need TextTrimming="WordEllipsis" setting in your TextBlock

Monogamous answered 30/11, 2012 at 2:9 Comment(3)
Thanks, but that only works if the text reaches the end of the TextBlock which might be 2 lines or more. I want TextBlock to trim the text after e.g. exactly 2 lines.Emersen
@Emersen For that you need to write custom converter which goes through your TextBlock text and inserts ellipses where needed.Monogamous
If you can set the height of the textblock, trimming and wrapping set should be all that you needNonce
A
2

Based on @artistandsocial's answer, I created a attached property to set the maximum number of lines programatically (rather than having to overload TextBlock which is discouraged in WPF).

public class LineHeightBehavior
{
    public static readonly DependencyProperty MaxLinesProperty =
        DependencyProperty.RegisterAttached(
            "MaxLines",
            typeof(int),
            typeof(LineHeightBehavior),
            new PropertyMetadata(default(int), OnMaxLinesPropertyChangedCallback));

    public static void SetMaxLines(TextBlock element, int value) => element.SetValue(MaxLinesProperty, value);

    public static int GetMaxLines(TextBlock element) =>(int)element.GetValue(MaxLinesProperty);

    private static void OnMaxLinesPropertyChangedCallback(
        DependencyObject d, 
        DependencyPropertyChangedEventArgs e)
    {
        if (d is TextBlock textBlock)
        {
            if (textBlock.IsLoaded)
            {
               SetLineHeight();
            }
            else
            {
                textBlock.Loaded += OnLoaded;

                void OnLoaded(object _, RoutedEventArgs __)
                {
                    textBlock.Loaded -= OnLoaded;
                    SetLineHeight();
                }
            }

            void SetLineHeight()
            {
                double lineHeight =
                   double.IsNaN(textBlock.LineHeight)
                        ? textBlock.FontFamily.LineSpacing * textBlock.FontSize
                        : textBlock.LineHeight;
                textBlock.MaxHeight = Math.Ceiling(lineHeight * GetMaxLines(textBlock));
            }
        }
    }
}

By default, the LineHeight is set to double.NaN, so this value must first be set manually, otherwise a height is calculated from the FontFamily and FontSize of the TextBlock.

The attached property MaxLines and other relevant properties can then be set in a Style:

<Style TargetType="{x:Type TextBlock}"
       BasedOn="{StaticResource {x:Type TextBlock}}">
    <Setter Property="TextTrimming"
            Value="CharacterEllipsis" />
    <Setter Property="TextWrapping"
            Value="Wrap" />
    <Setter Property="LineHeight"
            Value="16" />
    <Setter Property="LineStackingStrategy"
            Value="BlockLineHeight" />
    <Setter Property="behaviors:LineHeightBehavior.MaxLines"
            Value="2" />
</Style>
Appreciation answered 15/4, 2016 at 12:45 Comment(0)
E
0

For anybody developing UWP or WinRT Applications, TextBlock has a MaxLines property you can set.

Excoriate answered 1/3, 2017 at 22:0 Comment(0)
C
-4

I doubt that is configurable, Wrapping is based on a number of factors such as font-size/kerning, available width of the textblock (horizontalalignment=stretch can make a big difference), parent's panel type (scrollviewer/stackpanel/grid) etc.

If you want the text to flow to the next line explicitly you should use "Run" blocks instead and then use wrapping of type ellipses for that run block.

Cotonou answered 30/11, 2012 at 2:12 Comment(2)
See @kindasimple's comment below, that will give you what you want.Aten
Why is this chosen as the answer?Drinkable

© 2022 - 2024 — McMap. All rights reserved.