Compartir a través de


Información general sobre el modelo de contenido de controles

Actualización: noviembre 2007

En este tema se explican los modelos de contenido utilizados por las clases que heredan de Control. El modelo de contenido especifica los tipos de objetos que un control puede contener. En este tema, el término "control" se limita a una clase que tiene la clase Control en algún punto de su jerarquía de clases. Las cuatro clases siguientes que heredan de Control definen los cuatro modelos de contenido descritos en este tema:

Estas cuatro clases actúan como clases base para la mayoría de los controles de WPF. Las clases que usan estos modelos de contenido pueden contener los mismos tipos de contenido y tratar el contenido de la misma manera; cualquier tipo de objeto que se puede colocar en ContentControl (o en una clase que hereda de ContentControl) se puede colocar en un control que tenga cualquiera de los otros tres modelos de contenido. En la ilustración siguiente se muestra un control de cada modelo de contenido que contiene una imagen y texto.

Elementos Button, GroupBox, Listbox, TreeViewItem

Este tema contiene las secciones siguientes.

  • Requisitos previos
  • ContentControl
  • HeaderedContentControl
  • ItemsControl
  • HeaderedItemsControl
  • Temas relacionados

Requisitos previos

En este tema se supone que el lector tiene conocimientos básicos de WPF y sabe agregar controles a una aplicación. Para obtener más información, consulte Información general sobre Windows Presentation Foundation y Información general sobre controles.

ContentControl

El modelo de contenido más simple de los cuatro es ContentControl, que tiene una propiedad Content. La propiedad Content es del tipo Object, así que no hay ninguna restricción sobre lo que se puede colocar en ContentControl. Puede utilizar Lenguaje de marcado de aplicaciones extensible (XAML) o código para establecer Content.

Los controles siguientes utilizan el modelo de contenido ContentControl:

En el ejemplo siguiente se muestra cómo crear cuatro controles Button con Content establecido en uno de los valores siguientes:

Nota

En el ejemplo de versión de Lenguaje de marcado de aplicaciones extensible (XAML) se podrían utilizar etiquetas <Button.Content> alrededor del contenido de cada botón, pero no es necesario. Para obtener más información, consulte Información general sobre XAML.

<!--Create a Button with a string as its content.-->
<Button>This is string content of a Button</Button>

<!--Create a Button with a DateTime object as its content.-->
<Button xmlns:sys="clr-namespace:System;assembly=mscorlib">
  <sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
</Button>

<!--Create a Button with a single UIElement as its content.-->
<Button>
  <Rectangle Height="40" Width="40" Fill="Blue"/>
</Button>

<!--Create a Button with a panel that contains multiple objects 
as its content.-->
<Button>
  <StackPanel>
    <Ellipse Height="40" Width="40" Fill="Blue"/>
    <TextBlock TextAlignment="Center">Button</TextBlock>
  </StackPanel>
</Button>
' Add a string to a button.
Dim stringContent As New Button()
stringContent.Content = "This is string content of a Button"

' Add a DateTime object to a button.
Dim objectContent As New Button()
Dim dateTime1 As New DateTime(2004, 3, 4, 13, 6, 55)

objectContent.Content = dateTime1

' Add a single UIElement to a button.
Dim uiElementContent As New Button()

Dim rect1 As New Rectangle()
rect1.Width = 40
rect1.Height = 40
rect1.Fill = Brushes.Blue
uiElementContent.Content = rect1

' Add a panel that contains multpile objects to a button.
Dim panelContent As New Button()
Dim stackPanel1 As New StackPanel()
Dim ellipse1 As New Ellipse()
Dim textBlock1 As New TextBlock()

ellipse1.Width = 40
ellipse1.Height = 40
ellipse1.Fill = Brushes.Blue

textBlock1.TextAlignment = TextAlignment.Center
textBlock1.Text = "Button"

stackPanel1.Children.Add(ellipse1)
stackPanel1.Children.Add(textBlock1)

panelContent.Content = stackPanel1
// Create a Button with a string as its content.
Button stringContent = new Button();
stringContent.Content = "This is string content of a Button";

// Create a Button with a DateTime object as its content.
Button objectContent = new Button();
DateTime dateTime1 = new DateTime(2004, 3, 4, 13, 6, 55);

objectContent.Content = dateTime1;

// Create a Button with a single UIElement as its content.
Button uiElementContent = new Button();

Rectangle rect1 = new Rectangle();
rect1.Width = 40;
rect1.Height = 40;
rect1.Fill = Brushes.Blue;
uiElementContent.Content = rect1;

// Create a Button with a panel that contains multiple objects 
// as its content.
Button panelContent = new Button();
StackPanel stackPanel1 = new StackPanel();
Ellipse ellipse1 = new Ellipse();
TextBlock textBlock1 = new TextBlock();

ellipse1.Width = 40;
ellipse1.Height = 40;
ellipse1.Fill = Brushes.Blue;

textBlock1.TextAlignment = TextAlignment.Center;
textBlock1.Text = "Button";

stackPanel1.Children.Add(ellipse1);
stackPanel1.Children.Add(textBlock1);

panelContent.Content = stackPanel1;

En la ilustración siguiente se muestran los cuatro botones creados en el ejemplo anterior.

Cuatro botones

HeaderedContentControl

HeaderedContentControl hereda la propiedad Content de ContentControl y define la propiedad Header, que es de tipo Object. Header proporciona un encabezado para el control. Al igual que la propiedad Content de ContentControl, Header puede ser cualquier tipo. WPF se distribuye con tres controles que heredan de HeaderedContentControl:

En el ejemplo siguiente se crea un control TabControl (ItemsControl) que contiene dos objetos TabItem. El primer TabItem tiene contenido enriquecido en Header y en Content: Header es establece en un StackPanel que contiene una Ellipse y un TextBlock, y Content se establece en un StackPanel que contiene un TextBlock y una Label. El Header del segundo TabItem se establece en una cadena, y el Content se establece en un solo TextBlock.

<TabControl>
  <TabItem>
    <TabItem.Header>
      <StackPanel Orientation="Horizontal">
        <Ellipse Width="10" Height="10" Fill="DarkGray"/>
        <TextBlock>Tab 1</TextBlock>
      </StackPanel>
    </TabItem.Header>
    <StackPanel>
      <TextBlock>Enter some text</TextBlock>
      <TextBox Name="textBox1" Width="50"/>
    </StackPanel>
  </TabItem>
  <TabItem Header="Tab 2">
    <!--Bind TextBlock.Text to the TextBox on the first
    TabItem.-->
    <TextBlock Text="{Binding ElementName=textBox1, Path=Text}"/>
  </TabItem>
</TabControl>

En la ilustración siguiente se muestra el TabControl creado por el ejemplo anterior.

TabControl

ItemsControl

Los controles que heredan de ItemsControl contienen una colección de objetos. Un ejemplo de ItemsControl es ListBox. Puede utilizar la propiedad ItemsSource o la propiedad Items para rellenar ItemsControl.

Propiedad ItemsSource

La propiedad ItemsSource de ItemsControl permite utilizar cualquier tipo que implemente IEnumerable como contenido de ItemsControl. ItemsSource se suele utilizar para mostrar una recolección de datos o enlazar un ItemsControl a un objeto de colección.

En el ejemplo siguiente se crea una clase denominada MyData que es una colección de cadenas simples.

Public Class MyData
    Inherits ObservableCollection(Of String)

    Public Sub New()  '

        Add("Item 1")
        Add("Item 2")
        Add("Item 3")

    End Sub 'New
End Class 'MyData
public class MyData : ObservableCollection<string>
{
    public MyData()
    {
        Add("Item 1");
        Add("Item 2");
        Add("Item 3");
    }
}

En el ejemplo siguiente se enlaza ItemsSource a MyData.

<!--Create an instance of MyData as a resource.-->
<src:MyData x:Key="dataList"/>


...


<ListBox ItemsSource="{Binding Source={StaticResource dataList}}"/>
Dim listBox1 As New ListBox()
Dim listData As New MyData()
Dim binding1 As New Binding()

binding1.Source = listData
listBox1.SetBinding(ListBox.ItemsSourceProperty, binding1)
ListBox listBox1 = new ListBox();
MyData listData = new MyData();
Binding binding1 = new Binding();

binding1.Source = listData;
listBox1.SetBinding(ListBox.ItemsSourceProperty, binding1);

En la ilustración siguiente se muestra el ListBox creado en el ejemplo anterior.

ListBox

Para obtener más información sobre el enlace de datos, vea Información general sobre el enlace de datos.

Propiedad Items

Si no desea utilizar un objeto que implementa IEnumerable para rellenar ItemsControl, puede agregar elementos mediante la propiedad Items. Los elementos de ItemsControl pueden tener tipos distintos entre sí. Por ejemplo, un ListBox puede contener un elemento que sea una cadena y otro elemento que sea una Image.

Nota

   Cuando la propiedad ItemsSource está establecida, puede utilizar la propiedad Items para leer la ItemCollection, pero no puede agregar elementos a ItemCollection ni modificarla. Si se establece la propiedad ItemsSource en una referencia null (Nothing en Visual Basic) se quita la colección y se restaura el uso de Items, que será una ItemCollection vacía.

En el ejemplo siguiente se crea ListBox con cuatro tipos diferentes de elementos.

<!--Create a ListBox that contains a string, a Rectangle,
     a Panel, and a DateTime object. These items can be accessed
     via the Items property.-->
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib"
         Name="simpleListBox">

  <!-- The <ListBox.Items> element is implicitly used.-->
  This is a string in a ListBox

  <sys:DateTime>2004/3/4 13:6:55</sys:DateTime>

  <Rectangle Height="40" Width="40"  Fill="Blue"/>

  <StackPanel Name="itemToSelect">
    <Ellipse Height="40" Fill="Blue"/>
    <TextBlock>Text below an Ellipse</TextBlock>
  </StackPanel>

  <TextBlock>String in a TextBlock</TextBlock>
  <!--</ListBox.Items>-->
</ListBox>
' Create a Button with a string as its content.
listBox1.Items.Add("This is a string in a ListBox")

' Create a Button with a DateTime object as its content.
Dim dateTime1 As New DateTime(2004, 3, 4, 13, 6, 55)

listBox1.Items.Add(dateTime1)

' Create a Button with a single UIElement as its content.
Dim rect1 As New Rectangle()
rect1.Width = 40
rect1.Height = 40
rect1.Fill = Brushes.Blue
listBox1.Items.Add(rect1)

' Create a Button with a panel that contains multiple objects 
' as its content.
Dim ellipse1 As New Ellipse()
Dim textBlock1 As New TextBlock()

ellipse1.Width = 40
ellipse1.Height = 40
ellipse1.Fill = Brushes.Blue

textBlock1.TextAlignment = TextAlignment.Center
textBlock1.Text = "Text below an Ellipse"

stackPanel1.Children.Add(ellipse1)
stackPanel1.Children.Add(textBlock1)

listBox1.Items.Add(stackPanel1)
// Add a String to the ListBox.
listBox1.Items.Add("This is a string in a ListBox");

// Add a DateTime object to a ListBox.
DateTime dateTime1 = new DateTime(2004, 3, 4, 13, 6, 55);

listBox1.Items.Add(dateTime1);

// Add a Rectangle to the ListBox.
Rectangle rect1 = new Rectangle();
rect1.Width = 40;
rect1.Height = 40;
rect1.Fill = Brushes.Blue;
listBox1.Items.Add(rect1);

// Add a panel that contains multpile objects to the ListBox.
Ellipse ellipse1 = new Ellipse();
TextBlock textBlock1 = new TextBlock();

ellipse1.Width = 40;
ellipse1.Height = 40;
ellipse1.Fill = Brushes.Blue;

textBlock1.TextAlignment = TextAlignment.Center;
textBlock1.Text = "Text below an Ellipse";

stackPanel1.Children.Add(ellipse1);
stackPanel1.Children.Add(textBlock1);

listBox1.Items.Add(stackPanel1);

En la ilustración siguiente se muestra el ListBox creado en el ejemplo anterior.

ListBox con cuatro tipos de contenido

Clases de contenedores de elementos

Cada ItemsControl que se distribuye con WPF tiene una clase correspondiente que representa un elemento de ItemsControl. En la tabla siguiente se muestra una lista de objetos ItemsControl que se distribuyen con WPF y sus contenedores de elementos correspondientes.

ItemsControl

Contenedor de elemento

ComboBox

ComboBoxItem

ContextMenu

MenuItem

ListBox

ListBoxItem

ListView

ListViewItem

Menu

MenuItem

StatusBar

StatusBarItem

TabControl

TabItem

TreeView

TreeViewItem

Puede crear explícitamente un contenedor de elemento para cada elemento de ItemsControl, pero no es necesario. La creación o no de un contenedor de elemento en ItemsControl depende en gran medida del escenario. Por ejemplo, si enlaza datos a la propiedad ItemsSource, no se crea explícitamente un contenedor de elemento. Es importante tener presentes los puntos siguientes:

  • El tipo de los objetos de ItemCollection varía dependiendo de si crea explícitamente un contenedor de elemento.

  • Puede obtener el contenedor de elemento aunque no lo cree explícitamente.

  • Style cuyo TargetType está establecido en un contenedor de elemento se aplica con independencia de si el contenedor de elemento se crea de manera explícita o no.

  • La herencia de propiedades se comporta de manera diferente para los contenedores de elementos creados implícita y explícitamente, porque sólo los creados de manera explícita forman parte del árbol lógico.

Para ilustrar estos puntos, en el ejemplo siguiente se crean dos controles ListBox. En el ejemplo se crean objetos ListBoxItem para el primer ListBox, pero no para el segundo ListBox. En el segundo caso, ListBoxItem se crea implícitamente para cada elemento de ListBox.

<!--Explicitly create a ListBoxItem for each item in the ListBox-->
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib"
         Name="listBoxItemListBox">
  <!-- The <ListBox.Items> element is implicitly used.-->
  <ListBoxItem>
    This is a string in a ListBox
  </ListBoxItem>
  <ListBoxItem>
    <sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
  </ListBoxItem>
  <ListBoxItem>
    <Rectangle Height="40" Width="40" Fill="Blue"/>
  </ListBoxItem>
  <ListBoxItem>
    <StackPanel>
      <Ellipse Height="40" Width="40" Fill="Blue"/>
      <TextBlock>Text below an Ellipse</TextBlock>
    </StackPanel>
  </ListBoxItem>
  <!--</ListBox.Items>-->
</ListBox>


...


<!--Create a ListBox that contains a string, a Rectangle,
     a Panel, and a DateTime object. These items can be accessed
     via the Items property.-->
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib"
         Name="simpleListBox">

  <!-- The <ListBox.Items> element is implicitly used.-->
  This is a string in a ListBox

  <sys:DateTime>2004/3/4 13:6:55</sys:DateTime>

  <Rectangle Height="40" Width="40"  Fill="Blue"/>

  <StackPanel Name="itemToSelect">
    <Ellipse Height="40" Fill="Blue"/>
    <TextBlock>Text below an Ellipse</TextBlock>
  </StackPanel>

  <TextBlock>String in a TextBlock</TextBlock>
  <!--</ListBox.Items>-->
</ListBox>

La ItemCollection de cada ListBox es distinta. Cada elemento de la propiedad Items del primer ListBox es ListBoxItem, pero su tipo es diferente en el segundo ListBox. En el ejemplo siguiente se confirma esta situación recorriendo en iteración los elementos de ambos controles ListBox y comprobando el tipo de cada elemento.

    Console.WriteLine("Items in simpleListBox:")

    For Each item As Object In simpleListBox.Items
        Console.WriteLine(item.GetType().ToString())
    Next item

    Console.WriteLine(vbCr + "Items in listBoxItemListBox:")

    For Each item As Object In listBoxItemListBox.Items
        Console.WriteLine(item.GetType().ToString())
    Next item

End Sub 'ReportLBIs


...


'
'        Items in simpleListBox:
'        System.String
'        System.Windows.Shapes.Rectangle
'        System.Windows.Controls.StackPanel
'        System.DateTime
'
'        Items in listBoxItemListBox:
'        System.Windows.Controls.ListBoxItem
'        System.Windows.Controls.ListBoxItem
'        System.Windows.Controls.ListBoxItem
'        System.Windows.Controls.ListBoxItem
'        
Console.WriteLine("Items in simpleListBox:");
foreach (object item in simpleListBox.Items)
{
    Console.WriteLine(item.GetType().ToString());
}

Console.WriteLine("\rItems in listBoxItemListBox:");

foreach (object item in listBoxItemListBox.Items)
{
    Console.WriteLine(item.GetType().ToString());
}


...


/*
Items in simpleListBox:
System.String
System.Windows.Shapes.Rectangle
System.Windows.Controls.StackPanel
System.DateTime

Items in listBoxItemListBox:
System.Windows.Controls.ListBoxItem
System.Windows.Controls.ListBoxItem
System.Windows.Controls.ListBoxItem
System.Windows.Controls.ListBoxItem
*/

En la ilustración siguiente se muestran los dos ListBox controles que se crearon en el ejemplo anterior.

Compara contenedores de elementos explícitos e implícitos

A menudo, se necesita el contenedor de elemento de un elemento, pero no se ha creado explícitamente en la aplicación. Para obtener el contenedor de elemento está asociado a un elemento determinado, utilice el método ContainerFromItem. En el ejemplo siguiente se muestra cómo obtener un contenedor de elemento asociado a un elemento cuando ListBoxItem no se ha creado de manera explícita. En el ejemplo se supone que el objeto denominado itemToSelect no es un ListBoxItem y se ha agregado al ListBox, simpleListBox.

Dim lbi As ListBoxItem = _
    CType(simpleListBox.ItemContainerGenerator.ContainerFromItem(itemToSelect),  _
          ListBoxItem)

If Not (lbi Is Nothing) Then
    lbi.IsSelected = True
End If
ListBoxItem lbi =
    simpleListBox.ItemContainerGenerator.ContainerFromItem(itemToSelect) 
    as ListBoxItem;

if (lbi != null)
{
    lbi.IsSelected = true;
}

Los estilos cuyo TargetType está establecido en un contenedor de elemento se aplican a los contenedores de elemento creados de manera implícita y explícita. En el ejemplo siguiente se crea Style como un recurso para un ListBoxItem que centra horizontalmente el contenido de ListBoxItem. Cuando este estilo se aplica a los objetos ListBox, se centran los elementos en ambos objetos ListBox.

<!--Create a Style as a Resource.-->
<Style TargetType="ListBoxItem">
  <Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>

La figura siguiente muestra los dos controles ListBox cuando se aplica el estilo del ejemplo anterior.

Dos controles ListBox

La herencia de propiedades con los estilos y los contenedores de elementos está relacionada con la estructura del árbol lógico. Al crear explícitamente el contenedor de elemento, éste forma parte del árbol lógico. Si no se crea el contenedor de elemento, no forma parte del árbol lógico. En la ilustración siguiente se muestra la diferencia en el árbol lógico entre los dos controles ListBox del ejemplo anterior.

Árboles visuales para dos objetos ListBox

Los objetos que heredan de la clase Visual heredan los valores de la propiedad de su elemento primario lógico. En el ejemplo siguiente se crea un ListBox con dos controles TextBlock y se establece la propiedad Foreground de ListBox en el color azul. El primer TextBlock, textBlock1, está contenido en un ListBoxItem creado de manera explícita; el segundo TextBlock, textBlock2, no lo está. En el ejemplo también se define Style para un ListBoxItem que establece el Foreground de ListBoxItem en el color verde.

<!--Create a Style as a Resource.-->
<Style TargetType="ListBoxItem">
  <Setter Property="Foreground" Value="Green"/>
</Style>


...


<ListBox Foreground="Blue">
  <ListBoxItem>
    <TextBlock Name="textBlock1">TextBlock in a ListBoxItem.</TextBlock>
  </ListBoxItem>
  <TextBlock Name="textBlock2">TextBlock not in a ListBoxItem.</TextBlock>
</ListBox>

En la ilustración siguiente se muestra el ListBox creado en el ejemplo anterior.

Dos elementos ListBoxItem en un control ListBox

La cadena de textBlock1 es verde y la cadena de textBlock2 es azul, porque cada control TextBlock hereda la propiedad Foreground de su elemento primario lógico respectivo. El elemento primario lógico de textBox1 es ListBoxItem, y el elemento primario lógico de textBox2 es ListBox. Para obtener más información, consulte Herencia de valores de propiedad.

HeaderedItemsControl

HeaderedItemsControl hereda de la clase ItemsControl. HeaderedItemsControl define la propiedad Header, que sigue las mismas reglas que la propiedad Header de un HeaderedContentControl. WPF se distribuye con tres controles que heredan de HeaderedItemsControl:

En el ejemplo siguiente se crea un control TreeViewItem. TreeView contiene un solo TreeViewItem, que tiene la etiqueta TreeViewItem 1, y contiene los elementos siguientes:

Nota

En el ejemplo se crean de manera explícita objetos TreeViewItem para estos dos últimos elementos, porque Rectangle y StackPanel heredan de la clase Visual. El estilo predeterminado de TreeViewItem establece la propiedad Foreground. Los objetos secundarios heredan el valor de la propiedad del TreeViewItemcreado de manera explícita, lo que suele ser el comportamiento deseado.

<TreeView xmlns:sys="clr-namespace:System;assembly=mscorlib"
          Margin="10">
  <TreeViewItem Header="TreeViewItem 1" IsExpanded="True">
    TreeViewItem 1a
    <sys:DateTime>2004/3/4 13:6:55</sys:DateTime>
    <TreeViewItem>
      <TreeViewItem.Header>
        <Rectangle Height="10" Width="10" Fill="Blue"/>
      </TreeViewItem.Header>
    </TreeViewItem>
    <TreeViewItem>
      <TreeViewItem.Header>
        <StackPanel Orientation="Horizontal">
          <Ellipse Width="10" Height="10" Fill="DarkGray"/>
          <TextBlock >TreeViewItem 1d</TextBlock>
        </StackPanel>
      </TreeViewItem.Header>
    </TreeViewItem>
  </TreeViewItem>
</TreeView>

Vea también

Conceptos

Modelo de contenido de WPF