Información general sobre documentos dinámicos

Los documentos dinámicos están diseñados para optimizar su visualización y legibilidad. En lugar de establecer un diseño predefinido, los documentos dinámicos ajustan y redistribuyen dinámicamente su contenido basándose en variables en tiempo de ejecución, como el tamaño de la ventana, la resolución del dispositivo y las preferencias opcionales del usuario. Además, los documentos dinámicos ofrecen características de documento avanzadas, como paginación y columnas. En este tema se proporciona información general sobre los documentos dinámicos y cómo crearlos.

Qué es un documento dinámico

Un documento dinámico está diseñado para "redistribuir el contenido" según el tamaño de la ventana, la resolución del dispositivo y otras variables del entorno. Además, los documentos dinámicos tienen varias características integradas, como la búsqueda, los modos de visualización que optimizan la legibilidad, y la capacidad de cambiar el tamaño y el aspecto de las fuentes. Los documentos dinámicos se utilizan mejor cuando la facilidad de lectura es el escenario principal de consumo del documento. En cambio, los documentos fijos están diseñados para tener una presentación estática. Los documentos fijos son útiles cuando la fidelidad del contenido de origen es esencial. Consulte Documentos en WPF para obtener más información sobre los diferentes tipos de documentos.

En la ilustración siguiente se muestra un ejemplo de documento dinámico visualizado en varias ventanas de tamaños diferentes. A medida que cambia el área de visualización, el contenido se redistribuye para aprovechar mejor el espacio disponible.

Redistribución del contenido de un documento dinámico

Tal como se muestra en la imagen anterior, el contenido dinámico puede incluir muchos componentes, como párrafos, listas, imágenes, etc. Estos componentes corresponden a elementos de marcado y objetos en el código de procedimiento. Repasaremos estas clases detalladamente más adelante en la sección Clases relacionadas con el flujo de esta introducción. Por ahora, este es un ejemplo de código sencillo que crea un documento dinámico que consta de un párrafo con texto en negrita y una lista.

<!-- This simple flow document includes a paragraph with some
     bold text in it and a list. -->
<FlowDocumentReader xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <FlowDocument>
    <Paragraph>
      <Bold>Some bold text in the paragraph.</Bold>
      Some text that is not bold.
    </Paragraph>

    <List>
      <ListItem>
        <Paragraph>ListItem 1</Paragraph>
      </ListItem>
      <ListItem>
        <Paragraph>ListItem 2</Paragraph>
      </ListItem>
      <ListItem>
        <Paragraph>ListItem 3</Paragraph>
      </ListItem>
    </List>

  </FlowDocument>
</FlowDocumentReader>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace SDKSample
{
    public partial class SimpleFlowExample : Page
    {
        public SimpleFlowExample()
        {

            Paragraph myParagraph = new Paragraph();

            // Add some Bold text to the paragraph
            myParagraph.Inlines.Add(new Bold(new Run("Some bold text in the paragraph.")));

            // Add some plain text to the paragraph
            myParagraph.Inlines.Add(new Run(" Some text that is not bold."));

            // Create a List and populate with three list items.
            List myList = new List();

            // First create paragraphs to go into the list item.
            Paragraph paragraphListItem1 = new Paragraph(new Run("ListItem 1"));
            Paragraph paragraphListItem2 = new Paragraph(new Run("ListItem 2"));
            Paragraph paragraphListItem3 = new Paragraph(new Run("ListItem 3"));

            // Add ListItems with paragraphs in them.
            myList.ListItems.Add(new ListItem(paragraphListItem1));
            myList.ListItems.Add(new ListItem(paragraphListItem2));
            myList.ListItems.Add(new ListItem(paragraphListItem3));

            // Create a FlowDocument with the paragraph and list.
            FlowDocument myFlowDocument = new FlowDocument();
            myFlowDocument.Blocks.Add(myParagraph);
            myFlowDocument.Blocks.Add(myList);

            // Add the FlowDocument to a FlowDocumentReader Control
            FlowDocumentReader myFlowDocumentReader = new FlowDocumentReader();
            myFlowDocumentReader.Document = myFlowDocument;

            this.Content = myFlowDocumentReader;
        }
    }
}

Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Documents

Namespace SDKSample
    Partial Public Class SimpleFlowExample
        Inherits Page
        Public Sub New()

            Dim myParagraph As New Paragraph()

            ' Add some Bold text to the paragraph
            myParagraph.Inlines.Add(New Bold(New Run("Some bold text in the paragraph.")))

            ' Add some plain text to the paragraph
            myParagraph.Inlines.Add(New Run(" Some text that is not bold."))

            ' Create a List and populate with three list items.
            Dim myList As New List()

            ' First create paragraphs to go into the list item.
            Dim paragraphListItem1 As New Paragraph(New Run("ListItem 1"))
            Dim paragraphListItem2 As New Paragraph(New Run("ListItem 2"))
            Dim paragraphListItem3 As New Paragraph(New Run("ListItem 3"))

            ' Add ListItems with paragraphs in them.
            myList.ListItems.Add(New ListItem(paragraphListItem1))
            myList.ListItems.Add(New ListItem(paragraphListItem2))
            myList.ListItems.Add(New ListItem(paragraphListItem3))

            ' Create a FlowDocument with the paragraph and list.
            Dim myFlowDocument As New FlowDocument()
            myFlowDocument.Blocks.Add(myParagraph)
            myFlowDocument.Blocks.Add(myList)

            ' Add the FlowDocument to a FlowDocumentReader Control
            Dim myFlowDocumentReader As New FlowDocumentReader()
            myFlowDocumentReader.Document = myFlowDocument

            Me.Content = myFlowDocumentReader
        End Sub
    End Class
End Namespace

En la ilustración siguiente se muestra el aspecto de este fragmento de código.

Captura de pantalla: Ejemplo de FlowDocument representado

En este ejemplo, el control FlowDocumentReader se usa para hospedar el contenido dinámico. Consulte Tipos de documentos dinámicos para obtener más información sobre los controles de hospedaje de contenido dinámico. Los elementos Paragraph, List, ListItem y Bold se usan para controlar el formato de contenido, en función de su orden en el marcado. Por ejemplo, el elemento Bold abarca solo parte del texto en el párrafo; por tanto, solo esa parte del texto está en negrita. Si ha utilizado HTML, esto le resultará familiar.

Tal como se muestra en la ilustración anterior, hay varias características integradas en los Documentos de flujo:

  • Buscar: permite al usuario realizar una búsqueda de texto completo en todo un documento.

  • Modo de visualización: el usuario puede seleccionar su modo de visualización preferido, incluido el modo de visualización de una sola página (una página a la vez), el modo de visualización de dos página a la vez (formato de lectura de libro) y un modo de desplazamiento continuo (sin límite). Para obtener más información sobre estos modos de visualización, consulte FlowDocumentReaderViewingMode.

  • Controles de navegación de página: si el modo de visualización del documento utiliza páginas, los controles de navegación de páginas incluyen un botón para ir a la página siguiente (flecha abajo) o la página anterior (flecha arriba), así como indicadores para el número de página actual y el número total de páginas. Las páginas también pueden voltearse mediante las teclas de flecha del teclado.

  • Zoom: los controles de zoom permiten al usuario aumentar o disminuir el nivel de zoom haciendo clic en los botones de más o menos, respectivamente. Los controles de zoom incluyen también un control deslizante para ajustar el nivel de zoom. Para obtener más información, vea Zoom.

Estas características pueden modificarse según el control utilizado para hospedar el contenido dinámico. En la siguiente sección se describen los distintos controles.

Tipos de documentos dinámicos

La visualización del contenido de los documentos dinámicos y cómo aparece depende de qué objeto se utilice para hospedar el contenido dinámico. Hay cuatro controles que admiten la visualización del contenido dinámico: FlowDocumentReader, FlowDocumentPageViewer, RichTextBoxy FlowDocumentScrollViewer. Estos controles se describen brevemente a continuación.

Nota

Es obligatorio que FlowDocument hospede contenido de flujo directamente de forma que los controles de visualización consuman FlowDocument para habilitar el hospedaje de contenido dinámico.

FlowDocumentReader

FlowDocumentReader incluye características que permiten elegir dinámicamente entre distintos modos de visualización, incluido un modo de visualización de página única (una página a la vez), un modo de visualización de dos páginas a la vez (formato de lectura de libro) y un modo de visualización de desplazamiento continuo (sin límite). Para obtener más información sobre estos modos de visualización, vea FlowDocumentReaderViewingMode. Si no necesita la capacidad de cambiar dinámicamente entre distintos modos de visualización, FlowDocumentPageViewer y FlowDocumentScrollViewer proporcionan visores de contenido dinámico más ligeros que son fijos para un modo de visualización concreto.

FlowDocumentPageViewer y FlowDocumentScrollViewer

FlowDocumentPageViewer muestra el contenido en el modo de visualización de una página a la vez, mientras que FlowDocumentScrollViewer muestra el contenido en modo de desplazamiento continuo. Tanto FlowDocumentPageViewer como FlowDocumentScrollViewer son fijos para un modo de visualización concreto. Se pueden comparar con FlowDocumentReader, que incluye características que permiten elegir dinámicamente entre distintos modos de visualización (como lo establece la enumeración FlowDocumentReaderViewingMode), a costa de consumir más recursos que FlowDocumentPageViewer o que FlowDocumentScrollViewer.

De manera predeterminada, siempre se muestra una barra de desplazamiento vertical y una barra de desplazamiento horizontal se vuelve visible cuando es necesario. La UI predeterminada para FlowDocumentScrollViewer no incluye una barra de herramientas, pero se puede usar la propiedad IsToolBarVisible para habilitar una barra de herramientas integrada.

RichTextBox

Usará RichTextBox cuando quiera permitir al usuario editar el contenido dinámico. Por ejemplo, si desea crear un editor que le permita al usuario manipular elementos, como tablas, formatos de negrita y cursiva, etc., usaría una RichTextBox. Para obtener más información, consulte Información general sobre el control RichTextBox.

Nota

El contenido dinámico dentro de un RichTextBox no se comporta exactamente igual que el contenido dinámico incluido en otros controles. Por ejemplo, no hay ninguna columna en un RichTextBox y, por tanto, ningún comportamiento automático de cambio de tamaño. Además, las características normalmente integradas de contenido dinámico, como la búsqueda, el modo de visualización, la navegación por las páginas y el zoom, no están disponibles en RichTextBox.

Crear contenido dinámico

El contenido dinámico puede ser complejo al estar compuesto por varios elementos, como texto, imágenes, tablas e incluso clases derivadas de UIElement, como los controles. Para entender cómo crear contenido dinámico complejo, los puntos siguientes son fundamentales:

  • Clases relacionadas con el flujo: cada clase que se utiliza en el contenido dinámico tiene un propósito específico. Además, la relación jerárquica entre las clases dinámicas le ayuda a comprender cómo se utilizan. Por ejemplo, las clases derivadas de la clase Block se utilizan para contener otros objetos, mientras que las clases derivadas de Inline contienen objetos que se muestran.

  • Esquema de contenido: un documento dinámico puede requerir un número importante de elementos anidados. El esquema de contenido especifica las relaciones posibles entre elementos primarios y secundarios.

En las secciones siguientes se repasará cada una de estas áreas con más detalle.

En el diagrama siguiente se muestran los objetos más utilizados con el contenido dinámico:

Diagrama: Jerarquía de clases de elementos de contenido dinámico

A los efectos del contenido dinámico, hay dos categorías importantes:

  1. Clases derivadas de Block: también denominadas "elementos de contenido de Block" o simplemente "elementos Block". Los elementos que se heredan de Block pueden utilizarse para agrupar elementos bajo un elemento primario común o para aplicar atributos comunes a un grupo.

  2. Clases derivadas de Inline: también denominadas "elementos de contenido de Inline" o simplemente "elementos de Inline". Los elementos que se heredan de Inline se encuentran dentro de un elemento de bloque o de otro elemento alineado. Los elementos Inline se utilizan a menudo como contenedor directo del contenido que se representa en la pantalla. Por ejemplo, un Paragraph (elemento de bloque) puede contener un Run (elemento alineado), pero el Run contiene el texto que se representa en pantalla.

Cada clase de estas dos categorías se describe brevemente a continuación.

Clases derivadas de Block

Paragraph

Paragraph se suele usar para agrupar contenido en un párrafo. El uso más sencillo y común de Paragraph es crear un párrafo de texto.

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Paragraph>
    Some paragraph text.
  </Paragraph>
</FlowDocument>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace SDKSample
{
    public partial class ParagraphExample : Page
    {
        public ParagraphExample()
        {

            // Create paragraph with some text.
            Paragraph myParagraph = new Paragraph();
            myParagraph.Inlines.Add(new Run("Some paragraph text."));

            // Create a FlowDocument and add the paragraph to it.
            FlowDocument myFlowDocument = new FlowDocument();
            myFlowDocument.Blocks.Add(myParagraph);

            this.Content = myFlowDocument;
        }
    }
}

Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Documents

Namespace SDKSample
    Partial Public Class ParagraphExample
        Inherits Page
        Public Sub New()

            ' Create paragraph with some text.
            Dim myParagraph As New Paragraph()
            myParagraph.Inlines.Add(New Run("Some paragraph text."))

            ' Create a FlowDocument and add the paragraph to it.
            Dim myFlowDocument As New FlowDocument()
            myFlowDocument.Blocks.Add(myParagraph)

            Me.Content = myFlowDocument
        End Sub
    End Class
End Namespace

Pero también puede contener otros elementos derivados de alineación, como se verá a continuación.

Sección

Section solo se usa para contener otros elementos derivados de Block. No aplica ningún formato predeterminado a los elementos que contiene. Sin embargo, cualquier valor de propiedad establecido en un Section se aplica a sus elementos secundarios. Una sección permite iterar mediante programación en su colección secundaria. Section se usa de forma similar a la etiqueta <DIV> en HTML.

En el ejemplo siguiente, se definen tres párrafos en Section. La sección tiene un valor de propiedad Background de Red, por lo tanto, el color de fondo de los párrafos también será rojo.

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <!-- By default, Section applies no formatting to elements contained
       within it. However, in this example, the section has a Background
       property value of "Red", therefore, the three paragraphs (the block)  
       inside the section also have a red background. -->
  <Section Background="Red">
    <Paragraph>
      Paragraph 1
    </Paragraph>
    <Paragraph>
      Paragraph 2
    </Paragraph>
    <Paragraph>
      Paragraph 3
    </Paragraph>
  </Section>
</FlowDocument>
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Documents;

namespace SDKSample
{
    public partial class SectionExample : Page
    {
        public SectionExample()
        {

            // Create three paragraphs
            Paragraph myParagraph1 = new Paragraph(new Run("Paragraph 1"));
            Paragraph myParagraph2 = new Paragraph(new Run("Paragraph 2"));
            Paragraph myParagraph3 = new Paragraph(new Run("Paragraph 3"));

            // Create a Section and add the three paragraphs to it.
            Section mySection = new Section();
            mySection.Background = Brushes.Red;

            mySection.Blocks.Add(myParagraph1);
            mySection.Blocks.Add(myParagraph2);
            mySection.Blocks.Add(myParagraph3);

            // Create a FlowDocument and add the section to it.
            FlowDocument myFlowDocument = new FlowDocument();
            myFlowDocument.Blocks.Add(mySection);

            this.Content = myFlowDocument;
        }
    }
}

Imports System.Windows
Imports System.Windows.Media
Imports System.Windows.Controls
Imports System.Windows.Documents

Namespace SDKSample
    Partial Public Class SectionExample
        Inherits Page
        Public Sub New()

            ' Create three paragraphs
            Dim myParagraph1 As New Paragraph(New Run("Paragraph 1"))
            Dim myParagraph2 As New Paragraph(New Run("Paragraph 2"))
            Dim myParagraph3 As New Paragraph(New Run("Paragraph 3"))

            ' Create a Section and add the three paragraphs to it.
            Dim mySection As New Section()
            mySection.Background = Brushes.Red

            mySection.Blocks.Add(myParagraph1)
            mySection.Blocks.Add(myParagraph2)
            mySection.Blocks.Add(myParagraph3)

            ' Create a FlowDocument and add the section to it.
            Dim myFlowDocument As New FlowDocument()
            myFlowDocument.Blocks.Add(mySection)

            Me.Content = myFlowDocument
        End Sub
    End Class
End Namespace

BlockUIContainer

BlockUIContainer habilita UIElement elementos (por ejemplo, un Button) para que se inserten en el contenido dinámico derivado de bloques. InlineUIContainer (ver a continuación) se usa para insertar UIElement elementos en un contenido dinámico derivado del alineado. BlockUIContainer y InlineUIContainer son importantes porque no existe otra forma de usar un UIElement en el contenido dinámico a menos que se contenga dentro de uno de estos dos elementos.

El siguiente ejemplo muestra cómo usar el elemento BlockUIContainer para hospedar objetos UIElement dentro del contenido dinámico.

<FlowDocument ColumnWidth="400">
  <Section Background="GhostWhite">
    <Paragraph>
      A UIElement element may be embedded directly in flow content
      by enclosing it in a BlockUIContainer element.
    </Paragraph>
    <BlockUIContainer>
      <Button>Click me!</Button>
    </BlockUIContainer>
    <Paragraph>
      The BlockUIContainer element may host no more than one top-level
      UIElement.  However, other UIElements may be nested within the
      UIElement contained by an BlockUIContainer element.  For example,
      a StackPanel can be used to host multiple UIElement elements within
      a BlockUIContainer element.
    </Paragraph>
    <BlockUIContainer>
      <StackPanel>
        <Label Foreground="Blue">Choose a value:</Label>
        <ComboBox>
          <ComboBoxItem IsSelected="True">a</ComboBoxItem>
          <ComboBoxItem>b</ComboBoxItem>
          <ComboBoxItem>c</ComboBoxItem>
        </ComboBox>
        <Label Foreground ="Red">Choose a value:</Label>
        <StackPanel>
          <RadioButton>x</RadioButton>
          <RadioButton>y</RadioButton>
          <RadioButton>z</RadioButton>
        </StackPanel>
        <Label>Enter a value:</Label>
        <TextBox>
          A text editor embedded in flow content.
        </TextBox>
      </StackPanel>
    </BlockUIContainer>
  </Section>
</FlowDocument>

En la ilustración siguiente, se muestra cómo se representa este ejemplo:

Captura de pantalla que muestra UIElement insertado en contenido dinámico.

Lista

List se usa para crear una lista con viñetas o numérica. Establezca la propiedad MarkerStyle a un valor de enumeración TextMarkerStyle para determinar el estilo de la lista. En el ejemplo siguiente se muestra cómo crear una lista sencilla.

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <List>
    <ListItem>
      <Paragraph>
        List Item 1
      </Paragraph>
    </ListItem>
    <ListItem>
      <Paragraph>
        List Item 2
      </Paragraph>
    </ListItem>
    <ListItem>
      <Paragraph>
        List Item 3
      </Paragraph>
    </ListItem>
  </List>
</FlowDocument>
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Documents;

namespace SDKSample
{
    public partial class ListExample : Page
    {
        public ListExample()
        {

            // Create three paragraphs
            Paragraph myParagraph1 = new Paragraph(new Run("List Item 1"));
            Paragraph myParagraph2 = new Paragraph(new Run("List Item 2"));
            Paragraph myParagraph3 = new Paragraph(new Run("List Item 3"));

            // Create the ListItem elements for the List and add the
            // paragraphs to them.
            ListItem myListItem1 = new ListItem();
            myListItem1.Blocks.Add(myParagraph1);
            ListItem myListItem2 = new ListItem();
            myListItem2.Blocks.Add(myParagraph2);
            ListItem myListItem3 = new ListItem();
            myListItem3.Blocks.Add(myParagraph3);

            // Create a List and add the three ListItems to it.
            List myList = new List();

            myList.ListItems.Add(myListItem1);
            myList.ListItems.Add(myListItem2);
            myList.ListItems.Add(myListItem3);

            // Create a FlowDocument and add the section to it.
            FlowDocument myFlowDocument = new FlowDocument();
            myFlowDocument.Blocks.Add(myList);

            this.Content = myFlowDocument;
        }
    }
}

Imports System.Windows
Imports System.Windows.Media
Imports System.Windows.Controls
Imports System.Windows.Documents

Namespace SDKSample
    Partial Public Class ListExample
        Inherits Page
        Public Sub New()

            ' Create three paragraphs
            Dim myParagraph1 As New Paragraph(New Run("List Item 1"))
            Dim myParagraph2 As New Paragraph(New Run("List Item 2"))
            Dim myParagraph3 As New Paragraph(New Run("List Item 3"))

            ' Create the ListItem elements for the List and add the 
            ' paragraphs to them.
            Dim myListItem1 As New ListItem()
            myListItem1.Blocks.Add(myParagraph1)
            Dim myListItem2 As New ListItem()
            myListItem2.Blocks.Add(myParagraph2)
            Dim myListItem3 As New ListItem()
            myListItem3.Blocks.Add(myParagraph3)

            ' Create a List and add the three ListItems to it.
            Dim myList As New List()

            myList.ListItems.Add(myListItem1)
            myList.ListItems.Add(myListItem2)
            myList.ListItems.Add(myListItem3)

            ' Create a FlowDocument and add the section to it.
            Dim myFlowDocument As New FlowDocument()
            myFlowDocument.Blocks.Add(myList)

            Me.Content = myFlowDocument
        End Sub
    End Class
End Namespace

Nota

List es el único elemento de flujo que usa ListItemCollection para administrar elementos secundarios.

Tabla

Table se usa para crear una tabla. Table es similar al elemento Grid, pero dispone de más funcionalidades y, por lo tanto, requiere una mayor sobrecarga de recursos. Ya que Grid es un UIElement, no se puede usar en el contenido dinámico a menos que esté contenido en un BlockUIContainer o InlineUIContainer. Para más información de Table, vea Información general sobre tablas.

Clases derivadas de Inline

Ejecutar

Run se usa para contener texto sin formato. Es de esperar que los objetos Run se utilicen ampliamente en el contenido dinámico. Pero en marcado, no es necesario utilizar los elementos Run de manera explícita. El uso de Run es obligatorio cuando se crean o manipulan documentos dinámicos mediante código. Por ejemplo, en el siguiente marcado, el primer Paragraph especifica el elemento Run de forma explícita mientras que el segundo no lo hace. Ambos párrafos generan el mismo resultado.

<Paragraph>
  <Run>Paragraph that explicitly uses the Run element.</Run>
</Paragraph>

<Paragraph>
  This Paragraph omits the Run element in markup. It renders
  the same as a Paragraph with Run used explicitly. 
</Paragraph>

Nota

A partir del .NET Framework 4, la propiedad Text del objeto Run es una propiedad de dependencia. Puede enlazar la propiedad Text a un origen de datos, como un TextBlock. La propiedad Text admite completamente el enlace de datos unidireccional. La propiedad Text también admite el enlace bidireccional, a excepción de RichTextBox. Para obtener un ejemplo, consulte Run.Text.

Intervalo

Span agrupa otros elementos de contenido insertado. No se aplica ninguna representación inherente al contenido dentro de un elemento Span. Pero los elementos que heredan de Span, incluyendo Hyperlink, Bold, Italic y Underline, aplican formato al texto.

A continuación aparece un ejemplo del uso de Span para contener contenido insertado incluyendo texto, un elemento Bold y un Button.

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Paragraph>
    Text before the Span. <Span Background="Red">Text within the Span is
    red and <Bold>this text is inside the Span-derived element Bold.</Bold>
    A Span can contain more then text, it can contain any inline content. For
    example, it can contain a 
    <InlineUIContainer>
      <Button>Button</Button>
    </InlineUIContainer>
    or other UIElement, a Floater, a Figure, etc.</Span>
  </Paragraph>

</FlowDocument>

En la captura de pantalla siguiente se muestra cómo se representa este ejemplo.

Captura de pantalla: Ejemplo de Span representado

InlineUIContainer

InlineUIContainer permite que los elementos UIElement (es decir, un control como Button) se inserten en un elemento de contenido Inline. Este elemento es el equivalente insertado en BlockUIContainer descrito antes. El ejemplo a continuación usa InlineUIContainer para insertar un Button alineado en un Paragraph.

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Paragraph>
    Text to precede the button...

    <!-- Set the BaselineAlignment property to "Bottom" 
         so that the Button aligns properly with the text. -->
    <InlineUIContainer BaselineAlignment="Bottom">
      <Button>Button</Button>
    </InlineUIContainer>
    Text to follow the button...
  </Paragraph>

</FlowDocument>
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Documents;

namespace SDKSample
{
    public partial class InlineUIContainerExample : Page
    {
        public InlineUIContainerExample()
        {
            Run run1 = new Run(" Text to precede the button... ");
            Run run2 = new Run(" Text to follow the button... ");

            // Create a new button to be hosted in the paragraph.
            Button myButton = new Button();
            myButton.Content = "Click me!";

            // Create a new InlineUIContainer to contain the Button.
            InlineUIContainer myInlineUIContainer = new InlineUIContainer();

            // Set the BaselineAlignment property to "Bottom" so that the
            // Button aligns properly with the text.
            myInlineUIContainer.BaselineAlignment = BaselineAlignment.Bottom;

            // Asign the button as the UI container's child.
            myInlineUIContainer.Child = myButton;

            // Create the paragraph and add content to it.
            Paragraph myParagraph = new Paragraph();
            myParagraph.Inlines.Add(run1);
            myParagraph.Inlines.Add(myInlineUIContainer);
            myParagraph.Inlines.Add(run2);

            // Create a FlowDocument and add the paragraph to it.
            FlowDocument myFlowDocument = new FlowDocument();
            myFlowDocument.Blocks.Add(myParagraph);

            this.Content = myFlowDocument;
        }
    }
}

Imports System.Windows
Imports System.Windows.Media
Imports System.Windows.Controls
Imports System.Windows.Documents

Namespace SDKSample
    Partial Public Class InlineUIContainerExample
        Inherits Page
        Public Sub New()
            Dim run1 As New Run(" Text to precede the button... ")
            Dim run2 As New Run(" Text to follow the button... ")

            ' Create a new button to be hosted in the paragraph.
            Dim myButton As New Button()
            myButton.Content = "Click me!"

            ' Create a new InlineUIContainer to contain the Button.
            Dim myInlineUIContainer As New InlineUIContainer()

            ' Set the BaselineAlignment property to "Bottom" so that the 
            ' Button aligns properly with the text.
            myInlineUIContainer.BaselineAlignment = BaselineAlignment.Bottom

            ' Asign the button as the UI container's child.
            myInlineUIContainer.Child = myButton

            ' Create the paragraph and add content to it.
            Dim myParagraph As New Paragraph()
            myParagraph.Inlines.Add(run1)
            myParagraph.Inlines.Add(myInlineUIContainer)
            myParagraph.Inlines.Add(run2)

            ' Create a FlowDocument and add the paragraph to it.
            Dim myFlowDocument As New FlowDocument()
            myFlowDocument.Blocks.Add(myParagraph)

            Me.Content = myFlowDocument
        End Sub
    End Class
End Namespace

Nota

No es necesario usar InlineUIContainer explícitamente en el marcado. Si lo omite, se creará un InlineUIContainer cuando se compile el código.

Figure y Floater

Figure y Floater se utilizan para insertar contenido en documentos dinámicos con propiedades de colocación que pueden personalizarse independientemente del flujo de contenido primario. Figure o Floater se utilizan a menudo para resaltar o recalcar partes de contenido, para hospedar imágenes auxiliares u otro contenido dentro del flujo de contenido principal, o para insertar contenido con relación imprecisa, como los anuncios.

En el ejemplo siguiente se muestra cómo insertar un elemento Figure en un párrafo de texto.

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Paragraph>
    <Figure 
      Width="300" Height="100" 
      Background="GhostWhite" HorizontalAnchor="PageLeft" >
      <Paragraph FontStyle="Italic" Background="Beige" Foreground="DarkGreen" >
        A Figure embeds content into flow content with placement properties 
        that can be customized independently from the primary content flow
      </Paragraph>
    </Figure>
    Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy
    nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi
    enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis
    nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure.
  </Paragraph>

</FlowDocument>
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Documents;

namespace SDKSample
{
    public partial class FigureExample : Page
    {
        public FigureExample()
        {

            // Create strings to use as content.
            string strFigure = "A Figure embeds content into flow content with" +
                               " placement properties that can be customized" +
                               " independently from the primary content flow";
            string strOther = "Lorem ipsum dolor sit amet, consectetuer adipiscing" +
                              " elit, sed diam nonummy nibh euismod tincidunt ut laoreet" +
                              " dolore magna aliquam erat volutpat. Ut wisi enim ad" +
                              " minim veniam, quis nostrud exerci tation ullamcorper" +
                              " suscipit lobortis nisl ut aliquip ex ea commodo consequat." +
                              " Duis autem vel eum iriure.";

            // Create a Figure and assign content and layout properties to it.
            Figure myFigure = new Figure();
            myFigure.Width = new FigureLength(300);
            myFigure.Height = new FigureLength(100);
            myFigure.Background = Brushes.GhostWhite;
            myFigure.HorizontalAnchor = FigureHorizontalAnchor.PageLeft;
            Paragraph myFigureParagraph = new Paragraph(new Run(strFigure));
            myFigureParagraph.FontStyle = FontStyles.Italic;
            myFigureParagraph.Background = Brushes.Beige;
            myFigureParagraph.Foreground = Brushes.DarkGreen;
            myFigure.Blocks.Add(myFigureParagraph);

            // Create the paragraph and add content to it.
            Paragraph myParagraph = new Paragraph();
            myParagraph.Inlines.Add(myFigure);
            myParagraph.Inlines.Add(new Run(strOther));

            // Create a FlowDocument and add the paragraph to it.
            FlowDocument myFlowDocument = new FlowDocument();
            myFlowDocument.Blocks.Add(myParagraph);

            this.Content = myFlowDocument;
        }
    }
}

Imports System.Windows
Imports System.Windows.Media
Imports System.Windows.Controls
Imports System.Windows.Documents

Namespace SDKSample
    Partial Public Class FigureExample
        Inherits Page
        Public Sub New()

            ' Create strings to use as content.
            Dim strFigure As String = "A Figure embeds content into flow content with" & " placement properties that can be customized" & " independently from the primary content flow"
            Dim strOther As String = "Lorem ipsum dolor sit amet, consectetuer adipiscing" & " elit, sed diam nonummy nibh euismod tincidunt ut laoreet" & " dolore magna aliquam erat volutpat. Ut wisi enim ad" & " minim veniam, quis nostrud exerci tation ullamcorper" & " suscipit lobortis nisl ut aliquip ex ea commodo consequat." & " Duis autem vel eum iriure."

            ' Create a Figure and assign content and layout properties to it.
            Dim myFigure As New Figure()
            myFigure.Width = New FigureLength(300)
            myFigure.Height = New FigureLength(100)
            myFigure.Background = Brushes.GhostWhite
            myFigure.HorizontalAnchor = FigureHorizontalAnchor.PageLeft
            Dim myFigureParagraph As New Paragraph(New Run(strFigure))
            myFigureParagraph.FontStyle = FontStyles.Italic
            myFigureParagraph.Background = Brushes.Beige
            myFigureParagraph.Foreground = Brushes.DarkGreen
            myFigure.Blocks.Add(myFigureParagraph)

            ' Create the paragraph and add content to it.
            Dim myParagraph As New Paragraph()
            myParagraph.Inlines.Add(myFigure)
            myParagraph.Inlines.Add(New Run(strOther))

            ' Create a FlowDocument and add the paragraph to it.
            Dim myFlowDocument As New FlowDocument()
            myFlowDocument.Blocks.Add(myParagraph)

            Me.Content = myFlowDocument
        End Sub
    End Class
End Namespace

En la ilustración siguiente se muestra cómo se representa este ejemplo.

Captura de pantalla: Ejemplo de Figure

Figure y Floater difieren de varias formas y se usan en distintos escenarios.

Figure:

  • Se pueden determinar su posición: puede establecer sus delimitadores horizontal y vertical para acoplarlo con respecto a la página, el contenido, la columna o el párrafo. También puede usar sus propiedades HorizontalOffset y VerticalOffset para especificar desplazamientos arbitrarios.

  • Se puede ajustar a varias columnas: puede establecer el alto y el ancho de Figure en múltiplos del alto o del ancho de la página, el contenido o la columna. Tenga en cuenta que, en el caso de la página y el contenido, no se permiten múltiplos mayores que 1. Por ejemplo, puede establecer el ancho de Figure para que sea "0.5 page", "0.25 content" o "2 Column". También puede establecer el alto y ancho en valores absolutos de píxeles.

  • No se pagina: si el contenido dentro de Figure no cabe en Figure, se representará el contenido que quepa y se perderá el contenido restante

Floater:

  • No se puede establecer su posición y se representará en cualquier espacio que pueda estar a su disposición. No puede establecer Floater como anclaje ni desplazamiento.

  • No puede cambiarse el tamaño a más de una columna: de manera predeterminada, Floater ajusta su tamaño en una columna. Tiene una propiedad Width que puede establecerse en un valor absoluto de píxeles, pero, si este valor es mayor que el ancho de una columna, se omite y el elemento floater tiene un tamaño de una columna. Puede ajustarse el tamaño a menos de una columna si se establece el ancho correcto en píxeles, pero el tamaño no es relativo a la columna, por lo que "0.5Column" no es una expresión válida para el ancho de Floater. Floater no tiene ninguna propiedad de alto y no es posible establecer el alto, ya que depende del contenido

  • Floater se pagina: si su contenido en el ancho especificado se extiende a un alto de más de una columna, el elemento floater se parte y salta a la columna siguiente, la página siguiente, etc.

Figure es un buen lugar para colocar contenido independiente donde se desee controlar el tamaño y la posición, y se quiera estar seguro de que el contenido se ajustará al tamaño especificado. Floater es un buen lugar para colocar contenido que fluye más libremente y de manera similar al contenido de la página principal, pero es independiente de este.

LineBreak

LineBreak provoca un salto de línea en el contenido dinámico. El siguiente ejemplo muestra el uso de LineBreak.

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Paragraph>
    Before the LineBreak in Paragraph.
    <LineBreak />
    After the LineBreak in Paragraph.
    <LineBreak/><LineBreak/>
    After two LineBreaks in Paragraph.
  </Paragraph>

  <Paragraph>
    <LineBreak/>
  </Paragraph>

  <Paragraph>
    After a Paragraph with only a LineBreak in it.
  </Paragraph>
</FlowDocument>

En la captura de pantalla siguiente se muestra cómo se representa este ejemplo.

Captura de pantalla: Ejemplo de LineBreak

Elementos de colección dinámica

En muchos de los ejemplos anteriores, BlockCollection y InlineCollection se usan para la construcción de contenido dinámico mediante programación. Por ejemplo, para añadir elementos a un Paragraph, puede usar la sintaxis:

myParagraph.Inlines.Add(new Run("Some text"));

Esto agrega un Run al objeto InlineCollection de Paragraph. Es igual que el Run implícito dentro de un Paragraph en el marcado:

<Paragraph>
Some Text
</Paragraph>

Como ejemplo de uso del BlockCollection, el siguiente ejemplo crea un nuevo Section y luego usa el método Agregar para agregar un nuevo Paragraph a los contenidos Section.

Section secx = new Section();
secx.Blocks.Add(new Paragraph(new Run("A bit of text content...")));
Dim secx As New Section()
secx.Blocks.Add(New Paragraph(New Run("A bit of text content...")))

Además de agregar elementos a una colección dinámica, también puede quitar elementos. El siguiente ejemplo elimina el último elemento Inline del Span.

spanx.Inlines.Remove(spanx.Inlines.LastInline);
spanx.Inlines.Remove(spanx.Inlines.LastInline)

El siguiente ejemplo borra todo el contenido (Inline elementos) del Span.

spanx.Inlines.Clear();
spanx.Inlines.Clear()

Cuando trabaje con contenido dinámico mediante programación, probablemente utilizará mucho estas colecciones.

El que un elemento de flujo use un InlineCollection (insertados) o BlockCollection (bloques) para contener sus elementos secundarios depende de qué tipo de elementos secundarios (Block o Inline) puede contener el primario. Las reglas de contención para los elementos de contenido dinámico se resumen en el esquema de contenido en la sección siguiente.

Nota

Existe un tercer tipo de colección que se usa con el contenido de flujo, ListItemCollection, pero esta solo se usa con List. Además hay varias colecciones que se usan con Table. Para obtener más información, consulte Información general sobre tablas.

Esquema de contenido

Dado el número de los distintos elementos de contenido dinámico, puede resultar abrumador mantener un seguimiento de qué tipo de elementos secundarios puede contener un elemento. En el diagrama siguiente se resumen las reglas de contención para los elementos dinámicos. Las flechas representan las relaciones posibles entre elementos primarios y secundarios.

Diagrama: Esquema de contención de contenido dinámico

Como se puede observar en el diagrama anterior, los elementos secundarios que se permiten para un elemento no vienen determinados necesariamente por si es un elemento Block o Inline. Por ejemplo, un Span (un elemento Inline) solo puede tener Inline elementos secundarios, mientras que un elemento Figure (también un elemento Inline) solo puede tener Block elementos secundarios. Por tanto, un diagrama es útil para determinar rápidamente qué elemento puede incluirse en otro. Como ejemplo, se usará el diagrama para determinar cómo construir el contenido dinámico de un elemento RichTextBox.

1. Un RichTextBox debe contener un objeto FlowDocument que, a su vez, debe contener un objeto derivado de Block. A continuación, se muestra el segmento correspondiente del diagrama anterior.

Diagrama: Reglas de contención de RichTextBox

Llegados a este punto, esta es la apariencia que podría tener el marcado.

<RichTextBox>
  <FlowDocument>
    <!-- One or more Block-derived object… -->
  </FlowDocument>
</RichTextBox>

2. Según el diagrama, hay varios elementos Block entre los que elegir, incluidos Paragraph, Section, Table, Listy BlockUIContainer (ver arriba Clases derivadas de bloques). Imagine que quiere un objeto Table. Según el diagrama anterior, un Table contiene un elementoTableRowGroup que contiene TableRow elementos que contienen TableCell elementos, que contienen un objeto derivado de Block. A continuación, se muestra el segmento correspondiente a Table del diagrama anterior.

Diagrama: Esquema de elemento primario/secundario para Table

A continuación, se muestra el marcado correspondiente.

<RichTextBox>
  <FlowDocument>
    <Table>
      <TableRowGroup>
        <TableRow>
          <TableCell>
            <!-- One or more Block-derived object… -->
          </TableCell>
        </TableRow>
      </TableRowGroup>
    </Table>
  </FlowDocument>
</RichTextBox>

3. De nuevo, se requieren uno o varios elementos Block bajo un TableCell. Para facilitar el proceso, vamos a colocar texto dentro de la celda. Para hacerlo, se puede usar un Paragraph con un elemento Run. A continuación se encuentran segmentos correspondientes del diagrama se muestra que un objeto Paragraph puede tomar un elemento Inline y que un objeto Run (un elemento Inline) solo puede tomar texto sin formato.

Diagrama: Esquema de elemento primario/secundario para Paragraph

Diagrama: Esquema de elemento primario/secundario para Run

A continuación se muestra el ejemplo completo en el marcado.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <RichTextBox>
    <FlowDocument>
      
      <!-- Normally a table would have multiple rows and multiple
           cells but this code is for demonstration purposes.-->
      <Table>
        <TableRowGroup>
          <TableRow>
            <TableCell>
              <Paragraph>

                <!-- The schema does not actually require
                     explicit use of the Run tag in markup. It 
                     is only included here for clarity. -->
                <Run>Paragraph in a Table Cell.</Run>
              </Paragraph>
            </TableCell>
          </TableRow>
        </TableRowGroup>
      </Table>

    </FlowDocument>
  </RichTextBox>
</Page>

Personalizar texto

En general, el texto es el tipo de contenido más frecuente de un documento dinámico. Aunque los objetos descritos anteriormente pueden utilizarse para controlar la mayoría de los aspectos de cómo se representa el texto, hay otros métodos para personalizar el texto, según se describen en esta sección.

Decoraciones de texto

Las decoraciones de texto le permiten aplicar efectos de subrayado, línea alta, línea base y tachado al texto (consulte las imágenes siguientes). Estas decoraciones se agregan mediante la propiedad TextDecorations expuesta por una serie de objetos, incluidos Inline, Paragraph, TextBlock y TextBox.

En el ejemplo siguiente se muestra cómo se establece la propiedad TextDecorations de Paragraph.

<FlowDocument ColumnWidth="200">
  <Paragraph TextDecorations="Strikethrough">
    This text will render with the strikethrough effect.
  </Paragraph>
</FlowDocument>
Paragraph parx = new Paragraph(new Run("This text will render with the strikethrough effect."));
parx.TextDecorations = TextDecorations.Strikethrough;
Dim parx As New Paragraph(New Run("This text will render with the strikethrough effect."))
parx.TextDecorations = TextDecorations.Strikethrough

En la ilustración siguiente se muestra cómo se representa este ejemplo.

Captura de pantalla: Texto con efecto de tachado predeterminado

Las ilustraciones siguientes muestran cómo se representan las decoraciones Línea alta, Línea base y Subrayado, respectivamente.

Captura de pantalla: TextDecorator de línea alta

Captura de pantalla: Efecto de línea base predeterminada en texto

Captura de pantalla: Texto con efecto de subrayado predeterminado

Tipografía

La mayoría del contenido relacionado con el flujo expone la propiedad Typography, TextElement, FlowDocument, TextBlocky TextBox. Esta propiedad se utiliza para controlar características/variaciones tipográficas del texto (es decir, versalitas o mayúsculas, superíndices y subíndices, etc.).

En el ejemplo siguiente se muestra cómo establecer el Typography atributo, mediante el uso de Paragraph como elemento de ejemplo.

<Paragraph
  TextAlignment="Left"
  FontSize="18" 
  FontFamily="Palatino Linotype"
  Typography.NumeralStyle="OldStyle"
  Typography.Fraction="Stacked"
  Typography.Variants="Inferior"
>
  <Run>
    This text has some altered typography characteristics.  Note
    that use of an open type font is necessary for most typographic
    properties to be effective.
  </Run>
  <LineBreak/><LineBreak/>
  <Run>
    0123456789 10 11 12 13
  </Run>
  <LineBreak/><LineBreak/>
  <Run>
    1/2 2/3 3/4
  </Run>
</Paragraph>

En la ilustración siguiente se muestra cómo se representa este ejemplo.

Captura de pantalla que muestra texto con tipografía modificada.

En cambio, en la siguiente ilustración se muestra cómo se representa un ejemplo similar con propiedades tipográficas predeterminadas.

Captura de pantalla que muestra el texto con la tipografía predeterminada.

En el ejemplo siguiente se muestra cómo establecer la propiedad Typography mediante programación.

Paragraph par = new Paragraph();

Run runText = new Run(
    "This text has some altered typography characteristics.  Note" +
    "that use of an open type font is necessary for most typographic" +
    "properties to be effective.");
Run runNumerals = new Run("0123456789 10 11 12 13");
Run runFractions = new Run("1/2 2/3 3/4");

par.Inlines.Add(runText);
par.Inlines.Add(new LineBreak());
par.Inlines.Add(new LineBreak());
par.Inlines.Add(runNumerals);
par.Inlines.Add(new LineBreak());
par.Inlines.Add(new LineBreak());
par.Inlines.Add(runFractions);

par.TextAlignment = TextAlignment.Left;
par.FontSize = 18;
par.FontFamily = new FontFamily("Palatino Linotype");

par.Typography.NumeralStyle = FontNumeralStyle.OldStyle;
par.Typography.Fraction = FontFraction.Stacked;
par.Typography.Variants = FontVariants.Inferior;
Dim par As New Paragraph()

Dim runText As New Run("This text has some altered typography characteristics.  Note" & "that use of an open type font is necessary for most typographic" & "properties to be effective.")
Dim runNumerals As New Run("0123456789 10 11 12 13")
Dim runFractions As New Run("1/2 2/3 3/4")

par.Inlines.Add(runText)
par.Inlines.Add(New LineBreak())
par.Inlines.Add(New LineBreak())
par.Inlines.Add(runNumerals)
par.Inlines.Add(New LineBreak())
par.Inlines.Add(New LineBreak())
par.Inlines.Add(runFractions)

par.TextAlignment = TextAlignment.Left
par.FontSize = 18
par.FontFamily = New FontFamily("Palatino Linotype")

par.Typography.NumeralStyle = FontNumeralStyle.OldStyle
par.Typography.Fraction = FontFraction.Stacked
par.Typography.Variants = FontVariants.Inferior

Para obtener más información sobre la tipografía, consulte Tipografía en WPF.

Vea también