文本块自动换行的最大行数

32

我有一个设置如下的TextBlock

TextWrapping="Wrap"

我可以确定最大行数吗?

例如,考虑以下字符串TextBlock.Text

This is a very good horse under the blackboard!!

目前它的显示方式是这样的:

This is a very 
good horse under 
the blackboard!!

我需要将它变成类似以下的内容:

This is a very 
good horse ...

有任何解决方案吗?

7个回答

54

更新(针对UWP)

在UWP应用程序中,您不需要这样做,可以使用TextBlock属性MaxLines(请参见MSDN


原始答案:

如果您有特定的LineHeight,则可以计算TextBlock的最大高度

示例:

具有最多3行的TextBlock

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

这是您需要使您的需求正常工作所需的全部内容。
如何动态实现?
只需在C#/VB.NET中创建一个扩展TextBlock的新控件,并给它一个新的DependencyProperty int MaxLines。 然后覆盖OnApplyTemplate()方法,并根据LineHeight * MaxLines设置MaxHeight。
这只是一个基本的解决此问题的说明!

7
基于tobi.at和gt的回答,我创建了这个MaxLines行为。重要的是,它不依赖于通过从字体计算行高来设置LineHeight属性。您仍然需要设置TextWrappingTextTrimming,以便将TextBox渲染为您想要的样子。
<TextBlock behaviours:NumLinesBehaviour.MaxLines="3" TextWrapping="Wrap" TextTrimming="CharacterEllipsis" Text="Some text here"/>

同时还有一个MinLines行为,它可以被设置为与MaxLines行为相同或不同的数字,以设置所需的行数。

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

1
在移除了不必要的内容,例如“:Behavior<TextBlock>”和显然源自Behavior基类的以下代码行之后,它就像魔法一样运行。因此,只需使用具有dependencyProperties的静态类即可。 - Martini Bianco

6
如果您设置了HeightTextWrappingTextTrimming,它将按照您的要求进行处理。
<TextBlock Height="60" FontSize="22" FontWeight="Thin"
    TextWrapping="Wrap" TextTrimming="CharacterEllipsis">

上述代码将在两行内折叠,超过该点后使用CharacterEllipsis

2

根据@artistandsocial的回答,我创建了一个附加属性来以编程方式设置最大行数(而不是在WPF中过载 TextBlock ,这是不建议的)。

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

默认情况下,LineHeight被设置为double.NaN,因此必须首先手动设置该值,否则将从TextBlockFontFamilyFontSize计算高度。

然后可以在Style中设置附加属性MaxLines和其他相关属性:

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

2
你需要在中设置TextTrimming="WordEllipsis"

1
谢谢,但这只适用于文本到达“TextBlock”的末尾,这可能是2行或更多。我希望“TextBlock”在例如正好2行之后修剪文本。 - MBZ
@MBZ 你需要编写自定义的转换器,通过 TextBlock 文本并在必要的地方插入省略号。 - Mayank
3
如果您可以设置文本块的高度,那么修剪和换行设置应该就是您所需要的。 - kindasimple

0

对于任何开发UWP或WinRT应用程序的人来说,TextBlock有一个 MaxLines属性可设置。


-4

我怀疑那是可配置的,换行基于许多因素,如字体大小/字距、文本块的可用宽度(horizontalalignment=stretch 可以有很大的差别)、父面板类型(scrollviewer/stackpanel/grid)等。

如果您想显式地使文本换到下一行,您应该使用 "Run" 块,然后对该运行块使用省略号换行。


2
请见下面@kindasimple的评论,那会给你想要的东西。 - moswald
2
为什么选择这个作为答案? - mcont

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接