Usted puede hacer esto mediante la recuperación primero el objeto Drawing
que representa el aspecto de la TextBlock
en el árbol visual, y luego a pie que busca GlyphRunDrawing
elementos - que contendrán el texto real presentado en la pantalla. He aquí una aplicación muy áspera y listo:
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);
}
}
}
}
Este es un extracto directo de un pequeño instrumento de prueba que acabo de escribir - el primer método es un clic de botón manejador simplemente para facilitar la experimentación.
Utiliza el VisualTreeHelper
para obtener el Drawing
renderizado para el TextBlock
- eso solo funcionará si la cosa ya se ha procesado por cierto. Y luego el método WalkDrawingForText
hace el trabajo real, simplemente atraviesa el árbol Drawing
en busca de texto.
Esto no es terriblemente inteligente: asume que los objetos GlyphRunDrawing
aparecen en el orden en que los quiere. Para su ejemplo particular, lo hace - obtenemos un GlyphRunDrawing
que contiene el texto truncado, seguido de un segundo que contiene el carácter de puntos suspensivos.(Y por cierto, es solo un carácter unicode - codepoint 2026, y si este editor me permite pegar en caracteres Unicode, es "...". No son tres períodos separados.)
Si quiere hacer esto más robusto , necesitaría calcular las posiciones de todos esos objetos GlyphRunDrawing
, y ordenarlos, para procesarlos en el orden en que aparecen, en lugar de simplemente esperar que WPF los produzca en ese orden.
actualizado para añadir:
Aquí hay un bosquejo de cómo un ejemplo de posición podría ser conscientes. Aunque esto es un poco parroquial, supone texto de lectura de izquierda a derecha. Necesitarías algo más complejo para una solución internacionalizada.
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);
}
}
}
}
Me encantaría saber por qué querrías hacer esto ... – Guy
@Guy: Hehe, buena pregunta :) Estoy creando un efecto para TextBlock, pero para hacerlo necesitaré el texto mostrado –