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;

しかし、実際に画面に表示されているテキストを取得する方法はありますか?
意味「TextBlock:Display...」

ありがとう

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てレンダリングされDrawingたものを取得しますTextBlock- これは、物事がすでにレンダリングされている場合にのみ機能します。次に、WalkDrawingForTextメソッドが実際の作業を行います。つまりDrawing、テキストを探してツリーをトラバースするだけです。

これはそれほどスマートではありませんGlyphRunDrawing。オブジェクトが必要な順序で表示されることを前提としています。あなたの特定の例ではGlyphRunDrawing、切り捨てられたテキストを含むものを取得し、次に省略記号を含むものを取得します。(ちなみに、これはコードポイント 2026 の 1 つの Unicode 文字にすぎません。このエディターで Unicode 文字を貼り付けることができる場合、それは "..." です。ピリオドが 3 つあるわけではありません。)

これをより堅牢にしたい場合は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

しばらくの間IReflectorを応援した後、私は次のことを発見しました。

System.Windows.Media.TextFormatting.TextCollapsedRange 

これには、Length表示されない(テキスト行の折りたたまれた/非表示の部分にある)文字数を含むプロパティがあります。その値を知っているので、表示されている文字を取得するのは減算の問題です。

このプロパティには、TextBlockオブジェクトから直接アクセスすることはできません。これは、WPFが実際に画面にテキストをペイントするために使用するコードの一部のようです。

TextBlockのテキスト行のこのプロパティの値を実際に取得することは、かなり多くのことを騙してしまう可能性があります。

于 2010-12-05T11:24:36.083 に答える
1

効果のためにテキストが必要な場合-レンダリングされたテキストの画像で十分でしょうか?その場合は、VisualBrushまたはSystem.Windows.Media.Imaging.RenderTargetBitmapを使用できます

于 2010-12-06T08:33:12.817 に答える
1

まあ、それは少し具体的な要求なので、フレームワークにそれを行うための既製の機能があるかどうかはわかりません。私がすることは、各文字の論理幅を計算し、TextBlock の ActualWidth をこの値で割ると、文字列の先頭から表示されている文字数が得られます。もちろん、これはクリッピングが右からのみ発生することを前提としています。

于 2010-11-30T22:05:38.667 に答える
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 に答える