Formato de texto avanzado

Windows Presentation Foundation (WPF) proporciona un conjunto sólido de API para incluir texto en la aplicación. Las API de diseño e interfaz de usuario (UI), como TextBlock, proporcionan los elementos más comunes y de uso general para la presentación de textos. Las API de dibujo, como GlyphRunDrawing y FormattedText, proporcionan un medio para incluir texto formateado en los dibujos. En el nivel más avanzado, WPF proporciona un motor de formato de texto extensible para controlar todos los aspectos de presentación del texto, como la administración del almacén de texto, la administración del formato de ejecución de texto y la administración de objetos incrustados.

En este tema se proporciona una introducción al formato de texto de WPF. Se centra en la implementación del cliente y el uso del motor de formato de texto de WPF.

Nota

Todos los ejemplos de código de este documento pueden encontrarse en el Ejemplo de formato de texto avanzado.

Prerrequisitos

En este tema se supone que está familiarizado con las API de mayor nivel que se usan para la presentación de texto. La mayoría de los escenarios de usuario no requieren las API de formato de texto avanzado que se describen en este tema. Para obtener una introducción a las distintas API de texto, vea Documentos en WPF.

Formato de texto avanzado

El diseño de texto y los controles de interfaz de usuario en WPF ofrecen propiedades de formato que permiten incluir fácilmente texto con formato en la aplicación. Estos controles exponen varias propiedades para controlar la presentación de texto, como las de tipo de letra, tamaño y color. En circunstancias normales, estos controles pueden controlar la mayoría de las características de presentación de texto en la aplicación. Sin embargo, algunos escenarios avanzados requieren el control del almacenamiento de texto, así como su presentación. WPF proporciona un motor de formato de texto extensible para este fin.

Las características de formato de texto avanzadas disponibles en WPF incluyen un motor de formato de texto, un almacén de texto, ejecuciones de texto y propiedades de formato. El motor de formato de texto, TextFormatter, crea líneas de texto que se utilizarán para la presentación. Esto se consigue iniciando el proceso de formateo de líneas y llamando al FormatLine del formateador de texto. El formateador de texto recupera las ejecuciones de texto de su almacén de texto llamando al método GetTextRun del almacén. Los objetos TextRun se convierten en objetos TextLine por el formateador de texto y se entregan a la aplicación para su inspección o visualización.

Uso del formateador de texto

TextFormatter es el motor de formato de texto de WPF y proporciona servicios para formatear y romper líneas de texto. El formateador de texto puede controlar los distintos formatos de caracteres de texto y estilos de párrafo, e incluye compatibilidad para el diseño de texto internacional.

A diferencia de una API de texto tradicional, el TextFormatter interactúa con un cliente de diseño de texto a través de un conjunto de métodos de devolución de llamadas. Requiere que el cliente proporcione estos métodos en una implementación de la clase TextSource. En el diagrama siguiente se muestra la interacción del diseño de texto entre la aplicación cliente y el objeto TextFormatter.

Diagrama de TextFormatter y cliente de diseño de texto

El formateador de texto se usa para recuperar las líneas de texto con formato del almacén de texto, que es una implementación de TextSource. Esto se hace creando primero una instancia del formateador de texto utilizando el método Create. Este método crea una instancia del formateador de texto y establece los valores máximos de alto y ancho de línea. En cuanto se crea una instancia del formateador de texto, se inicia el proceso de creación de líneas llamando al método FormatLine. TextFormatter devuelve la llamada al origen del texto para recuperar los parámetros de texto y formato de las ejecuciones de texto que forman una línea.

En el ejemplo siguiente, se muestra el proceso para formatear un almacén de texto. El objeto TextFormatter se utiliza para recuperar las líneas de texto del almacén de texto y luego formatear la línea de texto para dibujar en el DrawingContext.

// Create a DrawingGroup object for storing formatted text.
textDest = new DrawingGroup();
DrawingContext dc = textDest.Open();

// Update the text store.
_textStore.Text = textToFormat.Text;
_textStore.FontRendering = _currentRendering;

// Create a TextFormatter object.
TextFormatter formatter = TextFormatter.Create();

// Format each line of text from the text store and draw it.
while (textStorePosition < _textStore.Text.Length)
{
   // Create a textline from the text store using the TextFormatter object.
   using (TextLine myTextLine = formatter.FormatLine(
       _textStore,
       textStorePosition,
       96*6,
       new GenericTextParagraphProperties(_currentRendering),
       null))
   {
       // Draw the formatted text into the drawing context.
       myTextLine.Draw(dc, linePosition, InvertAxes.None);

       // Update the index position in the text store.
       textStorePosition += myTextLine.Length;

       // Update the line position coordinate for the displayed line.
       linePosition.Y += myTextLine.Height;
   }
}

// Persist the drawn text content.
dc.Close();

// Display the formatted text in the DrawingGroup object.
myDrawingBrush.Drawing = textDest;
' Create a DrawingGroup object for storing formatted text.
textDest = New DrawingGroup()
Dim dc As DrawingContext = textDest.Open()

' Update the text store.
_textStore.Text = textToFormat.Text
_textStore.FontRendering = _currentRendering

' Create a TextFormatter object.
Dim formatter As TextFormatter = TextFormatter.Create()

' Format each line of text from the text store and draw it.
Do While textStorePosition < _textStore.Text.Length
   ' Create a textline from the text store using the TextFormatter object.
   Using myTextLine As TextLine = formatter.FormatLine(_textStore, textStorePosition, 96*6, New GenericTextParagraphProperties(_currentRendering), Nothing)
       ' Draw the formatted text into the drawing context.
       myTextLine.Draw(dc, linePosition, InvertAxes.None)

       ' Update the index position in the text store.
       textStorePosition += myTextLine.Length

       ' Update the line position coordinate for the displayed line.
       linePosition.Y += myTextLine.Height
   End Using
Loop

' Persist the drawn text content.
dc.Close()

' Display the formatted text in the DrawingGroup object.
myDrawingBrush.Drawing = textDest

Implementación del almacén de texto de cliente

Al ampliar el motor de formato de texto, es necesario que implemente y administre todos los aspectos del almacén de texto. Esta no es una tarea trivial. El almacén de texto es responsable del seguimiento de las propiedades de las ejecuciones de texto, las propiedades de párrafo, los objetos incrustados y otro contenido similar. También proporciona al formateador de texto objetos individuales TextRun que el formateador de texto utiliza para crear objetos TextLine.

Para controlar la virtualización del almacén de texto, este debe derivar de TextSource. TextSource define el método que el formateador de texto usa para recuperar ejecuciones de texto del almacén de texto. GetTextRun es el método utilizado por el formateador de texto para recuperar las ejecuciones de texto utilizadas en el formateo de líneas. La llamada a GetTextRun es realizada repetidamente por el formateador de texto hasta que se produce una de las siguientes condiciones:

  • Se devuelve un TextEndOfLine o una subclase.

  • El ancho acumulado de las ejecuciones de texto supera el ancho de línea máximo especificado en la llamada para crear el formateador de texto o en la llamada al método FormatLine del formateador de texto.

  • Se devuelve una secuencia de nueva línea Unicode, como "CF", "LF" o "CRLF".

Abastecimiento de ejecuciones de texto

La esencia del proceso de formato de texto es la interacción entre el formateador de texto y el almacén de texto. Su implementación de TextSource proporciona al formateador de texto los objetos TextRun y las propiedades con las que formatear las ejecuciones de texto. El método GetTextRun, llamado por el formateador de texto, controla esta interacción.

La siguiente tabla muestra algunos de los objetos predefinidos TextRun.

Tipo TextRun Uso
TextCharacters Ejecución de texto especializada que se usa para devolver una representación de glifos de caracteres al formateador de texto.
TextEmbeddedObject Ejecución de texto especializada que se usa para proporcionar contenido donde la medida, la prueba de posicionamiento y el dibujo se ejecutan de forma global, como un botón o una imagen dentro del texto.
TextEndOfLine Ejecución de texto especializada que se usa para marcar el final de una línea.
TextEndOfParagraph Ejecución de texto especializada que se usa para marcar el final de un párrafo.
TextEndOfSegment Ejecución de texto especializada que se usa para marcar el final de un segmento, por ejemplo, para finalizar el ámbito afectado por una ejecución de TextModifier anterior.
TextHidden Ejecución de texto especializada que se usa para marcar un intervalo de caracteres ocultos.
TextModifier Ejecución de texto especializada que se usa para modificar las propiedades de las ejecuciones de texto en su ámbito. El alcance se extiende a la siguiente ejecución de texto TextEndOfSegment que coincida, o a la siguiente TextEndOfParagraph.

Cualquiera de los objetos predefinidos TextRun puede subclasificarse. Esto permite que el origen del texto proporcione al formateador de texto ejecuciones de texto que incluyan datos personalizados.

En el siguiente ejemplo se muestra un GetTextRun método. Este almacén de texto devuelve objetos TextRun al formateador de texto para su procesamiento.

// Used by the TextFormatter object to retrieve a run of text from the text source.
public override TextRun GetTextRun(int textSourceCharacterIndex)
{
   // Make sure text source index is in bounds.
   if (textSourceCharacterIndex < 0)
      throw new ArgumentOutOfRangeException("textSourceCharacterIndex", "Value must be greater than 0.");
   if (textSourceCharacterIndex >= _text.Length)
   {
      return new TextEndOfParagraph(1);
   }

   // Create TextCharacters using the current font rendering properties.
   if (textSourceCharacterIndex < _text.Length)
   {
      return new TextCharacters(
         _text,
         textSourceCharacterIndex,
         _text.Length - textSourceCharacterIndex,
         new GenericTextRunProperties(_currentRendering));
   }

   // Return an end-of-paragraph if no more text source.
   return new TextEndOfParagraph(1);
}
' Used by the TextFormatter object to retrieve a run of text from the text source.
Public Overrides Function GetTextRun(ByVal textSourceCharacterIndex As Integer) As TextRun
   ' Make sure text source index is in bounds.
   If textSourceCharacterIndex < 0 Then
      Throw New ArgumentOutOfRangeException("textSourceCharacterIndex", "Value must be greater than 0.")
   End If
   If textSourceCharacterIndex >= _text.Length Then
      Return New TextEndOfParagraph(1)
   End If

   ' Create TextCharacters using the current font rendering properties.
   If textSourceCharacterIndex < _text.Length Then
      Return New TextCharacters(_text, textSourceCharacterIndex, _text.Length - textSourceCharacterIndex, New GenericTextRunProperties(_currentRendering))
   End If

   ' Return an end-of-paragraph if no more text source.
   Return New TextEndOfParagraph(1)
End Function

Nota

En este ejemplo, el almacén de texto proporciona las mismas propiedades de texto para todo el texto. Los almacenes de texto avanzados tendrían que implementar su propia administración de intervalos para permitir que caracteres individuales tengan propiedades diferentes.

Especificación de las propiedades de formato

TextRun Los objetos se formatean utilizando las propiedades proporcionadas por el almacén de texto. Estas propiedades son de dos tipos, TextParagraphProperties y TextRunProperties. TextParagraphProperties controlan propiedades inclusivas de párrafos como TextAlignment y FlowDirection. TextRunProperties son propiedades que pueden ser diferentes para cada ejecución de texto dentro de un párrafo, como el pincel de primer plano, Typeface y el tamaño de fuente. Para implementar los tipos de propiedades de ejecución de párrafo y texto personalizados, su aplicación debe crear clases que deriven de TextParagraphProperties y TextRunProperties respectivamente.

Vea también