23

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

<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:显示的文本”

string text = myTextBlock.Text;

但是有没有办法获得实际显示在屏幕上的文本?
意思是“文本块:显示...”

谢谢

4

5 回答 5

18

您可以通过首先检索Drawing表示视觉树中的外观的对象来执行此操作TextBlock,然后遍历该对象以查找GlyphRunDrawing项目 - 这些项目将包含屏幕上实际呈现的文本。这是一个非常粗略且现成的实现:

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

这是我刚刚编写的一个小测试工具的直接摘录 - 第一种方法是按钮单击处理程序,只是为了便于实验。

它使用VisualTreeHelper来获取渲染DrawingTextBlock- 只有当事物已经被渲染时才会起作用。然后该WalkDrawingForText方法执行实际工作 - 它只是遍历Drawing树寻找文本。

这不是很聪明——它假设GlyphRunDrawing对象按照你想要的顺序出现。对于您的特定示例,它确实如此-我们得到一个GlyphRunDrawing包含截断文本的文本,然后是第二个包含省略号字符的文本。(顺便说一句,它只是一个 unicode 字符 - 代码点 2026,如果这个编辑器允许我粘贴 unicode 字符,它就是“……”。它不是三个单独的句点。)

如果你想让它更健壮,你需要计算出所有这些GlyphRunDrawing对象的位置,并对它们进行排序,以便按照它们出现的顺序处理它们,而不是仅仅希望 WPF 恰好在那个命令。

更新添加:

这是位置感知示例的外观草图。虽然这有点狭隘 - 它假设从左到右阅读文本。对于国际化的解决方案,您需要更复杂的东西。

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);
            }
        }
    }
}
于 2010-12-06T09:37:05.140 回答
10

在I Reflector周围扎根了一段时间后,我发现了以下内容:

System.Windows.Media.TextFormatting.TextCollapsedRange 

它的Length属性包含未显示的字符数(位于文本行的折叠/隐藏部分中)。知道该值后,只需减法即可获得 ARE 显示的字符。

此属性不能从 TextBlock 对象直接访问。看起来它是 WPF 用来在屏幕上实际绘制文本的代码的一部分。

要真正获取 TextBlock 中文本行的此属性值,最终可能会花很多时间。

于 2010-12-05T11:24:36.083 回答
1

嗯,这是一个特定的请求,所以我不确定框架中是否有现成的函数可以做到这一点。我要做的是计算每个字符的逻辑宽度,将 TextBlock 的 ActualWidth 除以该值,然后就可以得到从字符串开头可见的字符数。这当然是假设剪辑只会从右侧发生。

于 2010-11-30T22:05:38.667 回答
1

如果您需要文本来获得效果 - 那么渲染文本的图像是否就足够了?如果是这样,您可以使用 VisualBrush 或 System.Windows.Media.Imaging.RenderTargetBitmap

于 2010-12-06T08:33:12.817 回答
0

我还使用以下 xaml 在 .Net 框架上进行了复制:

<Window x:Class="TestC1Grid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
        TextOptions.TextFormattingMode="Display"    
        TextOptions.TextRenderingMode="Auto"                                
        ResizeMode="CanResizeWithGrip"               
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"></ColumnDefinition>                
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock TextTrimming="CharacterEllipsis"                       
                       FontFamily="Tahoma"
                       FontSize="12"
                       HorizontalAlignment="Stretch"
                       TextAlignment="Left" xml:lang="nl-nl">My-Text</TextBlock>
            <TextBlock Grid.Column="1" TextTrimming="CharacterEllipsis"                       
                       FontFamily="Tahoma"
                       FontSize="12"
                       IsHyphenationEnabled="True">My-Text</TextBlock>
            <TextBlock Grid.Column="2" TextTrimming="CharacterEllipsis"                       
                       FontFamily="Tahoma"
                       FontSize="12"
                       IsHyphenationEnabled="True">My-Text</TextBlock>
        </Grid>
    </Grid>
</Window>

如果您删除 TextOptions.TextFormattingMode="Display"
TextOptions.TextRenderingMode="Auto"

或删除 xml:lang="nl-nl" 工作正常

于 2014-01-14T18:17:29.667 回答