Maximum number of lines for a Wrap TextBlock

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.


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;
    }
}

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!