Vue d'ensemble du modèle de contenu des contrôles
Mise à jour : novembre 2007
Cette rubrique discute des modèles de contenu utilisés par les classes qui héritent de Control. Le modèle de contenu spécifie les types d'objets qu'un contrôle peut contenir. Dans cette rubrique, le terme "contrôle" est limité à une classe qui comporte quelque part dans sa hiérarchie de classe la classe Control. Les quatre modèles de contenu abordés dans cette rubrique sont définis par les quatre classes suivantes qui héritent de Control :
ContentControl contient un élément unique.
HeaderedContentControl contient un en-tête et un élément unique.
ItemsControl contient une collection d'éléments.
HeaderedItemsControl contient un en-tête et une collection d'éléments.
Ces quatre classes agissent comme classes de base pour la plupart des contrôles dans WPF. Les classes qui utilisent ces modèles de contenu peuvent contenir les mêmes types de contenu et traiter le contenu de la même façon ; tout type d'objet qui peut être placé dans un ContentControl (ou une classe qui hérite de ContentControl) peut être placé dans un contrôle qui comporte l'un des autres trois modèles de contenu. L'illustration suivante montre un contrôle de chaque modèle de contenu qui contient une image et du texte.
Cette rubrique comprend les sections suivantes.
- Composants requis
- ContentControl
- HeaderedContentControl
- ItemsControl
- HeaderedItemsControl
- Rubriques connexes
Composants requis
Cette rubrique suppose que vous avez des connaissances de base de WPF et savez comment ajouter des contrôles à une application. Pour plus d'informations, consultez Mise en route de Windows Presentation Foundation et Vue d'ensemble des contrôles.
ContentControl
Le modèle de contenu le plus simple des quatre est le ContentControl, qui a une propriété Content. La propriété Content est de type Object, il n'y a doc pas de restrictions sur ce que vous pouvez mettre dans un ContentControl. Vous pouvez utiliser XAML (Extensible Application Markup Language) ou le code pour définir Content.
Les contrôles suivants utilisent le modèle de contenu ContentControl :
L'exemple suivant montre comment créer quatre contrôles Button avec Content ayant pour valeur l'un des éléments suivants :
Remarque : |
---|
L'exemple en version XAML (Extensible Application Markup Language) pourrait utiliser les balises <Button.Content> autour du contenu de chaque bouton, mais ce n'est pas nécessaire. Pour plus d'informations, consultez Vue d'ensemble du langage 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;
L'illustration suivante montre les quatre boutons créés dans l'exemple précédent.
HeaderedContentControl
Le HeaderedContentControl hérite de la propriété Content de ContentControl et définit la propriété Header qui est de type Object. Header fournit un titre au contrôle. Comme la propriété Content d'un ContentControl, le Header peut être de tout type. WPF fournit trois contrôles qui héritent de HeaderedContentControl :
L'exemple suivant crée un TabControl (un ItemsControl) qui contient deux objets TabItem. Le premier TabItem a un contenu enrichi à la fois dans Header et Content : Header a pour valeur un StackPanel qui contient un Ellipse et un TextBlock, et Content a pour valeur un StackPanel qui contient un TextBlock et un Label. Header du second TabItem a pour valeur une chaîne, et le Content a pour valeur un TextBlock unique.
<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>
L'illustration suivante montre le TabControl créé par l'exemple précédent.
ItemsControl
Contrôle qui hérite de ItemsControl et qui contient une collection d'objets. Un exemple de ItemsControl est la ListBox. Vous pouvez utiliser la propriété ItemsSource ou la propriété Items pour remplir un ItemsControl.
Propriété ItemsSource
La propriété ItemsSource du ItemsControl vous permet d'utiliser tout type implémentant IEnumerable comme contenu du ItemsControl. ItemsSource sert généralement à afficher une collection de données ou à lier un ItemsControl à un objet de la collection.
L'exemple suivant crée une classe appelée MyData qui est une collection de chaînes simple.
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");
}
}
L'exemple suivant lie ItemsSource à 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);
L'illustration suivante affiche le ListBox créé dans l'exemple précédent.
Pour plus d'informations sur la liaison de données, consultez Vue d'ensemble de la liaison de données.
Propriété Items
Si vous ne souhaitez pas utiliser un objet qui implémente IEnumerable pour remplir le ItemsControl, vous pouvez ajouter des éléments à l'aide de la propriété Items. Les éléments dans un ItemsControl peuvent avoir des types différents l'un de l'autre. Par exemple, une ListBox peut contenir un élément qui est une chaîne et un autre élément qui sont une Image.
Remarque : |
---|
Lorsque la propriété ItemsSource est définie, vous pouvez utiliser la propriété Items pour lire la ItemCollection, mais ne pouvez pas ajouter ou modifier la ItemCollection. La définition de la propriété ItemsSource à une référence nulle (Nothing dans Visual Basic) supprime la collection et rétablit l'utilisation des Items, qui seront une ItemCollection vide. |
L'exemple suivant crée une ListBox avec quatre types d'éléments différents.
<!--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);
L'illustration suivante affiche le ListBox créé dans l'exemple précédent.
Classes de conteneur d'élément
Chaque ItemsControl fourni avec WPF a une classe correspondante qui représente un élément dans le ItemsControl. La table suivante répertorie les objets ItemsControl fournis avec WPF et leurs conteneurs d'éléments correspondants.
ItemsControl |
Conteneur d'élément |
---|---|
Vous pouvez créer explicitement un conteneur d'élément pour chaque élément dans le ItemsControl, mais ce n'est pas nécessaire. La création ou non d'un conteneur d'élément dans votre ItemsControl dépend en grande partie de votre scénario. Par exemple, si vous liez des données à la propriété ItemsSource, vous ne créerez pas explicitement de conteneur d'élément. Il faut retenir les points importants suivants :
Le type des objets dans la ItemCollection diffère selon que vous créez un conteneur d'élément explicitement.
Vous pouvez obtenir le conteneur d'élément même si vous ne le créez pas explicitement.
Un Style avec le TargetType défini sur un conteneur d'élément s'applique que le conteneur d'élément soit créé explicitement ou pas.
L'héritage des propriétés se comporte différemment pour les conteneurs d'éléments créés implicitement et explicitement, car seuls les conteneurs d'éléments créés explicitement font partie de l'arborescence logique.
Pour illustrer ces points, l'exemple suivant crée les deux contrôles ListBox. L'exemple crée des objets ListBoxItem pour la première ListBox, mais pas la deuxième ListBox. Dans le deuxième cas, un ListBoxItem est créé implicitement pour chaque élément dans la 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 est différente pour chaque ListBox. Chaque élément dans la propriété Items de la première ListBox est un ListBoxItem, mais est d'un type différent dans la deuxième ListBox. L'exemple suivant le confirme en itérant à travers les éléments dans les contrôles ListBox et en vérifiant le type de chaque élément.
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
*/
L'illustration suivante affiche les deux contrôles ListBox qui ont été créés dans l'exemple précédent.
Vous souhaiterez parfois disposer du conteneur d'élément pour un élément, mais vous ne l'avez pas créé explicitement dans votre application. Pour obtenir le conteneur d'élément associé à un élément particulier, utilisez la méthode ContainerFromItem. L'exemple suivant montre comment obtenir un conteneur d'élément associé à un élément lorsqu'un ListBoxItem n'est pas créé explicitement. L'exemple suppose que l'objet appelé itemToSelect n'est pas un ListBoxItem et a été ajouté à la 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;
}
Les styles qui ont le TargetType défini sur un conteneur d'élément sont appliqués à la fois sur les conteneurs d'élément créés implicitement et explicitement. L'exemple suivant crée un Style comme une ressource pour un ListBoxItem qui centre horizontalement le contenu dans l'ListBoxItem. Lorsque ce style est appliqué aux objets ListBox, les éléments des deux objets ListBox sont centrés.
<!--Create a Style as a Resource.-->
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
L'illustration suivante montre les deux contrôles ListBox lorsque le style dans l'exemple précédent est appliqué.
La manière dont fonctionne l'héritage des propriétés avec les styles et les conteneurs d'élément est liée à la façon dont l'arborescence logique est structurée. Lorsque vous créez le conteneur d'élément explicitement, il fait partie de l'arborescence logique. Si vous ne créez pas le conteneur d'élément, il ne fait pas partie de l'arborescence logique. L'illustration suivante montre la différence dans l'arborescence logique pour les deux contrôles ListBox de l'exemple précédent.
Les objets qui héritent de la classe Visual héritent des valeurs de propriété de leur parent logique. L'exemple suivant crée une ListBox avec deux contrôles TextBlock et définit la propriété Foreground de la ListBox à bleue. Le premier TextBlock, textBlock1, est contenu dans un ListBoxItem créé explicitement et le deuxième TextBlock, textBlock2, ne l'est pas. L'exemple définit également un Style pour un ListBoxItem qui définit l'Foreground d'un ListBoxItem à vert.
<!--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>
L'illustration suivante affiche le ListBox créé dans l'exemple précédent.
La chaîne dans textBlock1 est verte et la chaîne dans textBlock2 est bleue car chaque contrôle TextBlock hérite de la propriété Foreground de son parent logique respectif. Le parent logique de textBox1 est l'ListBoxItem, et le parent logique de textBox2 est la ListBox. Pour plus d'informations, consultez Héritage de la valeur de propriété.
HeaderedItemsControl
Le HeaderedItemsControl hérite de la classe ItemsControl.) Le HeaderedItemsControl définit la propriété Header, qui suit les mêmes règles que la propriété Header d'un HeaderedContentControl. WPF fournit trois contrôles qui héritent de HeaderedItemsControl :
L'exemple suivant crée un TreeViewItem. Le TreeView contient un TreeViewItem unique, qui est libellé TreeViewItem 1 et qui contient les éléments suivants :
Une chaîne.
Un objet DateTime
Un TreeViewItem qui contient un Rectangle dans son Header.
Un TreeViewItem dont la propriété Header a la valeur d'un StackPanel qui contient deux objets.
Remarque : |
---|
L'exemple crée explicitement des objets TreeViewItem pour les deux derniers éléments car Rectangle et StackPanel héritent de la classe Visual. Le style par défaut pour TreeViewItem définit la propriété Foreground. Les objets enfants héritent de la valeur de propriété de TreeViewItem créé explicitement, qui est en général le comportement désiré. |
<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>