从TextBlock获取显示的文本 [英] Get Displayed Text from TextBlock

查看:188
本文介绍了从TextBlock获取显示的文本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个像这样定义的简单TextBlock

I have a simple TextBlock defined like this

<StackPanel>
    <Border Width="106"
            Height="25"
            Margin="6"
            BorderBrush="Black"
            BorderThickness="1"
            HorizontalAlignment="Left">
        <TextBlock Name="myTextBlock"
                   TextTrimming="CharacterEllipsis"
                   Text="TextBlock: Displayed text"/>
    </Border>
</StackPanel>

这样的输出

这将使我获得"TextBlock:显示的文本"

This will get me "TextBlock: Displayed text"

string text = myTextBlock.Text;

但是有没有办法获取屏幕上实际显示的文字?
含义"TextBlock:显示..."

But is there a way to get the text that's actually displayed on the screen?
Meaning "TextBlock: Display..."

谢谢

推荐答案

您可以执行以下操作:首先检索表示可视化树中TextBlock外观的Drawing对象,然后遍历该对象以查找GlyphRunDrawing项-这些项将包含屏幕上的实际渲染文本.这是一个非常粗糙且准备就绪的实现:

You can do this by first retrieving the Drawing object that represents the appearance of the TextBlock in the visual tree, and then walk that looking for GlyphRunDrawing items - those will contain the actual rendered text on the screen. Here's a very rough and ready implementation:

private void button1_Click(object sender, RoutedEventArgs e)
{
    Drawing textBlockDrawing = VisualTreeHelper.GetDrawing(myTextBlock);
    var sb = new StringBuilder();
    WalkDrawingForText(sb, textBlockDrawing);

    Debug.WriteLine(sb.ToString());
}

private static void WalkDrawingForText(StringBuilder sb, Drawing d)
{
    var glyphs = d as GlyphRunDrawing;
    if (glyphs != null)
    {
        sb.Append(glyphs.GlyphRun.Characters.ToArray());
    }
    else
    {
        var g = d as DrawingGroup;
        if (g != null)
        {
            foreach (Drawing child in g.Children)
            {
                WalkDrawingForText(sb, child);
            }
        }
    }
}

这是我刚刚编写的一个测试工具的直接摘录-第一种方法是一个按钮单击处理程序,目的是简化实验.

This is a direct excerpt from a little test harness I just wrote - the first method's a button click handler just for ease of experimentation.

它使用VisualTreeHelperTextBlock获取渲染的Drawing-仅当事物已经被顺便渲染时才起作用.然后WalkDrawingForText方法完成实际工作-它仅遍历Drawing树以查找文本.

It uses the VisualTreeHelper to get the rendered Drawing for the TextBlock - that'll only work if the thing has already been rendered by the way. And then the WalkDrawingForText method does the actual work - it just traverses the Drawing tree looking for text.

这不是很聪明-假定GlyphRunDrawing对象按照您希望它们出现的顺序出现.对于您的特定示例,它确实做到了-我们得到一个包含截短文本的GlyphRunDrawing,然后是另一个包含省略号字符的GlyphRunDrawing. (顺便说一下,它只是一个unicode字符-代码点2026,如果此编辑器允许我粘贴unicode字符,则为…".这不是三个单独的句点.)

This isn't terribly smart - it assumes that the GlyphRunDrawing objects appear in the order you'll want them. For your particular example it does - we get one GlyphRunDrawing containing the truncated text, followed by a second one containing the ellipsis character. (And by the way, it's just one unicode character - codepoint 2026, and if this editor lets me paste in unicode characters, it's "…". It's not three separate periods.)

如果要使其更健壮,则需要确定所有这些GlyphRunDrawing对象的位置,并对它们进行排序,以便按照它们出现的顺序对其进行处理,而不仅仅是希望WPF恰好按此顺序生产它们.

If you wanted to make this more robust, you would need to work out the positions of all those GlyphRunDrawing objects, and sort them, in order to process them in the order in which they appear, rather than merely hoping that WPF happens to produce them in that order.

已更新为添加:

以下是位置感知示例的外观示意图.尽管这有点狭och,但它假设是从左到右的阅读文本.对于国际化的解决方案,您需要更复杂的东西.

Here's a sketch of how a position-aware example might look. Although this is somewhat parochial - it assumes left-to-right reading text. You'd need something more complex for an internationalized solution.

private string GetTextFromVisual(Visual v)
{
    Drawing textBlockDrawing = VisualTreeHelper.GetDrawing(v);
    var glyphs = new List<PositionedGlyphs>();

    WalkDrawingForGlyphRuns(glyphs, Transform.Identity, textBlockDrawing);

    // Round vertical position, to provide some tolerance for rounding errors
    // in position calculation. Not totally robust - would be better to
    // identify lines, but that would complicate the example...
    var glyphsOrderedByPosition = from glyph in glyphs
                                    let roundedBaselineY = Math.Round(glyph.Position.Y, 1)
                                    orderby roundedBaselineY ascending, glyph.Position.X ascending
                                    select new string(glyph.Glyphs.GlyphRun.Characters.ToArray());

    return string.Concat(glyphsOrderedByPosition);
}

[DebuggerDisplay("{Position}")]
public struct PositionedGlyphs
{
    public PositionedGlyphs(Point position, GlyphRunDrawing grd)
    {
        this.Position = position;
        this.Glyphs = grd;
    }
    public readonly Point Position;
    public readonly GlyphRunDrawing Glyphs;
}

private static void WalkDrawingForGlyphRuns(List<PositionedGlyphs> glyphList, Transform tx, Drawing d)
{
    var glyphs = d as GlyphRunDrawing;
    if (glyphs != null)
    {
        var textOrigin = glyphs.GlyphRun.BaselineOrigin;
        Point glyphPosition = tx.Transform(textOrigin);
        glyphList.Add(new PositionedGlyphs(glyphPosition, glyphs));
    }
    else
    {
        var g = d as DrawingGroup;
        if (g != null)
        {
            // Drawing groups are allowed to transform their children, so we need to
            // keep a running accumulated transform for where we are in the tree.
            Matrix current = tx.Value;
            if (g.Transform != null)
            {
                // Note, Matrix is a struct, so this modifies our local copy without
                // affecting the one in the 'tx' Transforms.
                current.Append(g.Transform.Value);
            }
            var accumulatedTransform = new MatrixTransform(current);
            foreach (Drawing child in g.Children)
            {
                WalkDrawingForGlyphRuns(glyphList, accumulatedTransform, child);
            }
        }
    }
}

这篇关于从TextBlock获取显示的文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆