Получить отображаемый текст из TextBlock - PullRequest
23 голосов
/ 01 декабря 2010

У меня есть простой 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>

Какие выходы как это

alt text

Это даст мне «TextBlock: отображаемый текст»

string text = myTextBlock.Text;

Но есть ли способ получить текст, который фактически отображается на экране?
Значение "TextBlock: Показать ..."

Спасибо

Ответы [ 5 ]

17 голосов
/ 06 декабря 2010

Вы можете сделать это, сначала извлекая объект 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 для получения отрисовки Drawing для TextBlock - это будет работать только в том случае, если вещь уже отрисована кстати. И затем метод WalkDrawingForText выполняет фактическую работу - он просто перебирает дерево Drawing в поисках текста.

Это не очень умно - предполагается, что объекты GlyphRunDrawing отображаются в том порядке, в котором вы их хотите. Для вашего конкретного примера это так - мы получаем один GlyphRunDrawing, содержащий усеченный текст, за которым следует второй, содержащий символ многоточия. (И, кстати, это всего лишь один символ Юникода - кодовая точка 2026, и если этот редактор позволяет мне вставлять символы Юникода, это «…». Это не три отдельных периода.)

Если вы хотите сделать это более надежным, вам нужно будет определить положение всех этих 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);
            }
        }
    }
}
10 голосов
/ 05 декабря 2010

Пройдя некоторое время вокруг I Reflector, я обнаружил следующее:

System.Windows.Media.TextFormatting.TextCollapsedRange 

, который имеет свойство Length, которое содержит количество символов, которые НЕ отображаются (в свернутой / скрытой части текстовой строки). Зная это значение, нужно просто вычитать, чтобы получить символы, которые отображаются.

Это свойство не доступно напрямую из объекта TextBlock. Похоже, это часть кода, который используется WPF для рисования текста на экране.

Это может закончиться тем, что будет довольно много дурачиться, чтобы получить значение этого свойства для текстовой строки в вашем TextBlock.

1 голос
/ 06 декабря 2010

Если вам нужен текст для эффекта - может ли этого быть достаточно с изображением отрендеренного текста?Если это так, вы можете использовать VisualBrush или System.Windows.Media.Imaging.RenderTargetBitmap

1 голос
/ 01 декабря 2010

Ну, это немного конкретный запрос, поэтому я не уверен, что в рамках есть готовая функция для этого. Что бы я сделал, чтобы вычислить логическую ширину каждого символа, разделить ActualWidth TextBlock на это значение, и там у вас есть количество символов от начала видимой строки. Это, конечно, при условии, что отсечение будет происходить только справа.

0 голосов
/ 14 января 2014

Также я воспроизвел на .Net Framework со следующим xaml:

<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" работает нормально

...