从RichTextBlock获取可见文本

时间:2017-01-24 00:31:23

标签: text uwp visible richtextblock

在UWP应用中,我使用的RichTextBlock会填充一些内容。它启用了自动换行功能,并设置了最大行数,这样无论内容的长度如何,它都只会显示一定数量的富文本行。

我想知道是否有办法弄清楚什么是可见文字?

所以,如果我有:

<RichTextBlock TextWrapping="Wrap" MaxLines="2">
    <RichTextBlock.Blocks>
        <Paragraph>
            <Paragraph.Inlines>
                A bunch of runs go in here with text that are several lines
            </Paragraph.Inlines>
        </Paragraph>
    </RichTextBlock.Blocks>
</RichTextBlock>

我想知道有多少文字实际可见。

我试图检测文本长度超过设定行数的情况并添加&#34; ...阅读更多&#34;在最后一行的末尾(用&#34替换最后13个字符; ...阅读更多&#34;)

1 个答案:

答案 0 :(得分:1)

所以我写了一些代码来获取我想要的行为,但不幸的是,这是相当缓慢和低效的。因此,如果您在主要显示大量需要截断的文本的应用程序中使用它(如带有大量文本项的ListView),那么这会降低您的应用程序性能。我仍然想知道是否有更好的方法来做到这一点。

这是我的代码(只处理Run和Hyperlink内联,所以你必须修改以处理你需要的其他类型):

private static void TrimText_Slow(RichTextBlock rtb)
{
    var paragraph = rtb?.Blocks?.FirstOrDefault() as Paragraph;
    if (paragraph == null) { return; }

    // Ensure RichTextBlock has passed a measure step so that its HasOverflowContent is updated.
    rtb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
    if (rtb.HasOverflowContent == false) { return; }


    // Start from end and remove all inlines that are not visible
    Inline lastInline = null;
    var idx = paragraph.Inlines.Count - 1;
    while (idx >= 0 && rtb.HasOverflowContent)
    {
        lastInline = paragraph.Inlines[idx];
        paragraph.Inlines.Remove(lastInline);
        idx--;
        // Ensure RichTextBlock has passed a measure step now with an inline removed, so that its HasOverflowContent is updated.
        rtb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
    }

    // The last inline could be partially visible. The easiest thing to do here is to always
    // add back the last inline and then remove characters from it until everything is in view.
    if (lastInline != null)
    {
        paragraph.Inlines.Add(lastInline);
    }

    // Make room to insert "... Read More"
    DeleteCharactersFromEnd(paragraph.Inlines, 13);

    // Insert "... Continue Reading"
    paragraph.Inlines.Add(new Run { Text = "... " });
    paragraph.Inlines.Add(new Run { Text = "Read More", Foreground = new SolidColorBrush(Colors.Blue) });

    // Ensure RichTextBlock has passed a measure step now with the new inlines added, so that its HasOverflowContent is updated.
    rtb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));

    // Keep deleting chars until "... Continue Reading" comes into view
    idx = paragraph.Inlines.Count - 3; // skip the last 2 inlines since they are "..." and "Read More"
    while (idx >= 0 && rtb.HasOverflowContent)
    {
        Run run;

        if (paragraph.Inlines[idx] is Hyperlink)
        {
            run = ((Hyperlink)paragraph.Inlines[idx]).Inlines.FirstOrDefault() as Run;
        }
        else
        {
            run = paragraph.Inlines[idx] as Run;
        }

        if (string.IsNullOrEmpty(run?.Text))
        {
            paragraph.Inlines.Remove(run);
            idx--;
        }
        else
        {
            run.Text = run.Text.Substring(0, run.Text.Length - 1);
        }

        // Ensure RichTextBlock has passed a measure step now with the new inline content updated, so that its HasOverflowContent is updated.
        rtb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
    }
}

private static void DeleteCharactersFromEnd(InlineCollection inlines, int numCharsToDelete)
{
    if (inlines == null || inlines.Count < 1 || numCharsToDelete < 1) { return; }

    var idx = inlines.Count - 1;

    while (numCharsToDelete > 0)
    {
        Run run;

        if (inlines[idx] is Hyperlink)
        {
            run = ((Hyperlink)inlines[idx]).Inlines.FirstOrDefault() as Run;
        }
        else
        {
            run = inlines[idx] as Run;
        }

        if (run == null)
        {
            inlines.Remove(inlines[idx]);
            idx--;
        }
        else
        {
            var textLength = run.Text.Length;
            if (textLength <= numCharsToDelete)
            {
                numCharsToDelete -= textLength;
                inlines.Remove(inlines[idx]);
                idx--;
            }
            else
            {
                run.Text = run.Text.Substring(0, textLength - numCharsToDelete);
                numCharsToDelete = 0;
            }
        }
    }
}