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