Вы можете сделать это, сначала извлекая объект 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);
}
}
}
}