Condividi tramite


Cenni preliminari sul modello di contenuto dei controlli

Aggiornamento: novembre 2007

In questo argomento vengono analizzati i modelli di contenuto utilizzati dalle classi ereditate da Control. Tramite il modello di contenuto vengono specificati i tipi di oggetti che un controllo può contenere. In questo argomento, il termine "controllo" è limitato a una classe che contiene la classe Control nella propria gerarchia di classi. I quattro modelli di contenuto analizzati in questo argomento sono definiti dalle quattro classi seguenti ereditate da Control:

Queste quattro classi si comportano come classi base per la maggior parte dei controlli in WPF. Le classi che utilizzano questi modelli di contenuto possono contenere gli stessi tipi di contenuto e trattare il contenuto nello stesso modo. Qualsiasi tipo di oggetto che può essere posizionato in un controllo ContentControl (o in una classe ereditata da ContentControl) può essere posizionato in un controllo con uno degli altri tre modelli di contenuto. Nella figura riportata di seguito viene illustrato un controllo di ciascun modello di contenuto che contiene un'immagine e testo.

Button, GroupBox, Listbox, TreeViewItem

Nel presente argomento sono contenute le seguenti sezioni.

  • Prerequisiti
  • ContentControl
  • HeaderedContentControl
  • ItemsControl
  • HeaderedItemsControl
  • Argomenti correlati

Prerequisiti

In questo argomento si presuppone una comprensione di base di WPF e la conoscenza delle modalità per aggiungere controlli a un'applicazione. Per ulteriori informazioni, vedere Guida introduttiva a Windows Presentation Foundation e Cenni preliminari sui controlli.

ContentControl

Il modello di contenuto più semplice tra i quattro analizzati è ContentControl, che dispone di una proprietà Content. La proprietà Content è di tipo Object, pertanto non esistono restrizioni sugli elementi che è possibile inserire in un controllo ContentControl. È possibile utilizzare Extensible Application Markup Language (XAML) oppure codice per impostare la proprietà Content.

Nei controlli riportati di seguito viene utilizzato il modello di contenuto ContentControl:

Nell'esempio riportato di seguito viene illustrato come creare quattro controlli Button con la proprietà Content impostata su uno dei seguenti valori:

Nota

Nella versione di Extensible Application Markup Language (XAML) di esempio è possibile utilizzare i tag <Button.Content> per il contenuto di ogni pulsante, ma non è necessario. Per ulteriori informazioni, vedere Cenni preliminari su 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;

Nella figura riportata di seguito vengono illustrati i quattro pulsanti creati nell'esempio precedente.

Quattro pulsanti

HeaderedContentControl

Il controllo HeaderedContentControl eredita la proprietà Content da ContentControl e definisce la proprietà Header di tipo Object. La proprietà Header fornisce un'intestazione per il controllo. Analogamente alla proprietà Content di un controllo ContentControl, Header può essere di qualsiasi tipo. In WPF sono disponibili tre controlli ereditati da HeaderedContentControl:

Nell'esempio riportato di seguito viene creato un controllo TabControl (ItemsControl) che contiene due oggetti TabItem. Il primo oggetto TabItem presenta contenuto dettagliato in entrambe le proprietà Header e Content: la proprietà Header è impostata su un oggetto StackPanel che contiene un oggetto Ellipse e un oggetto TextBlock, mentre la proprietà Content è impostata su un oggetto StackPanel che contiene un oggetto TextBlock e un oggetto Label. La proprietà Header del secondo oggetto TabItem è impostata su una stringa, mentre la proprietà Content è impostata su un unico oggetto 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>

Nella figura riportata di seguito viene illustrato l'oggetto TabControl creato nell'esempio precedente.

TabControl

ItemsControl

I controlli che ereditano da ItemsControl contengono un insieme di oggetti. Un esempio di controllo ItemsControl è ListBox. È possibile utilizzare la proprietà ItemsSource o la proprietà Items per compilare un controllo ItemsControl.

Proprietà ItemsSource

La proprietà ItemsSource di ItemsControl consente di utilizzare qualsiasi tipo che implementi IEnumerable come contenuto del controllo ItemsControl. La proprietà ItemsSource viene in genere utilizzata per visualizzare un insieme di dati o per associare ItemsControl a un oggetto dell'insieme.

Nell'esempio riportato di seguito viene creata una classe denominata MyData costituita da un semplice insieme di stringhe.

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");
    }
}

Nell'esempio riportato di seguito la proprietà ItemsSource viene associata 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);

Nella figura riportata di seguito viene illustrato l'oggetto ListBox creato nell'esempio precedente.

ListBox

Per ulteriori informazioni sull'associazione dati, vedere Cenni preliminari sull'associazione dati.

Proprietà Items

Se non si desidera utilizzare un oggetto che implementa IEnumerable per compilare ItemsControl, è possibile aggiungere elementi utilizzando la proprietà Items. Gli elementi in un controllo ItemsControl possono avere tipi diversi tra loro. Ad esempio, un controllo ListBox può contenere un elemento costituito da una stringa e un altro elemento costituito da un oggetto Image.

Nota

   Quando viene impostata la proprietà ItemsSource, è possibile utilizzare la proprietà Items per leggere ItemCollection, ma non è possibile aggiungere o modificare ItemCollection. Con l'impostazione della proprietà ItemsSource su un riferimento null (Nothing in Visual Basic) viene rimosso l'insieme e viene ripristinato l'utilizzo della proprietà Items, che sarà un oggetto ItemCollection vuoto.

Nell'esempio riportato di seguito viene creato un oggetto ListBox con quattro tipi diversi di elementi.

<!--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);

Nella figura riportata di seguito viene illustrato l'oggetto ListBox creato nell'esempio precedente.

ListBox con quattro tipi di contenuto

Classi di contenitori di elementi

In ogni ItemsControl fornito con WPF è presente una classe corrispondente che rappresenta un elemento in ItemsControl. Nella tabella riportata di seguito vengono elencati gli oggetti ItemsControl forniti con WPF e i contenitori di elementi corrispondenti.

ItemsControl

Contenitore di elementi

ComboBox

ComboBoxItem

ContextMenu

MenuItem

ListBox

ListBoxItem

ListView

ListViewItem

Menu

MenuItem

StatusBar

StatusBarItem

TabControl

TabItem

TreeView

TreeViewItem

È possibile creare in modo esplicito un contenitore di elementi per ogni elemento dell'oggetto ItemsControl, ma non è necessario. La possibilità di creare un contenitore di elementi in ItemsControl dipende prevalentemente dallo scenario. Ad esempio, se si associano dati alla proprietà ItemsSource, non verrà creato un contenitore di elementi in modo esplicito. È importante tenere in considerazione i punti seguenti:

  • Il tipo degli oggetti in ItemCollection si differenzia in base alla scelta di creare o meno un contenitore di elementi in modo esplicito.

  • È possibile ottenere il contenitore di elementi anche se non lo si crea in modo esplicito.

  • Un oggetto Style con la proprietà TargetType impostata su un contenitore di elementi viene applicato indipendentemente dal fatto che il contenitore di elementi venga creato in modo esplicito o meno.

  • L'ereditarietà delle proprietà si comporta in modo diverso per contenitori di elementi creati in modo implicito o esplicito poiché solo i contenitori di elementi creati in modo esplicito fanno parte dell'albero logico.

Per illustrare questi punti, nell'esempio riportato di seguito vengono creati i due controlli ListBox. Nell'esempio vengono creati oggetti ListBoxItem per il primo controllo ListBox, ma non per il secondo. Nel secondo caso, viene creato in modo implicito un oggetto ListBoxItem per ogni elemento in 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>

L'oggetto ItemCollection per ogni controllo ListBox è diverso. Ogni elemento nella proprietà Items del primo controllo ListBox è un oggetto ListBoxItem, ma ha un tipo diverso nel secondo controllo ListBox. Nell'esempio riportato di seguito viene confermata questa diversità scorrendo gli elementi in entrambi i controlli ListBox e controllando il tipo di ciascun 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
*/

Nella figura riportata di seguito vengono illustrati i due controlli ListBox creati nell'esempio precedente.

Confronta contenitori di elementi espliciti e impliciti

Spesso, può rivelarsi necessario disporre del contenitore per un elemento, che tuttavia non è stato creato in modo esplicito nell'applicazione. Per ottenere il contenitore di elementi associato a un determinato elemento, utilizzare il metodo ContainerFromItem. Nell'esempio riportato di seguito viene illustrato come ottenere un contenitore di elementi associato a un elemento quando non è stato creato in modo esplicito un oggetto ListBoxItem. In questo esempio si presuppone che l'oggetto denominato itemToSelect non sia ListBoxItem e sia stato aggiunto a 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;
}

Gli stili con la proprietà TargetType impostata su un contenitore di elementi vengono applicati ai contenitori di elementi creati in modo implicito ed esplicito. Nell'esempio riportato di seguito viene creato un oggetto Style come risorsa per un oggetto ListBoxItem che consente di centrare orizzontalmente il contenuto in ListBoxItem. Quando questo stile viene applicato agli oggetti ListBox, gli elementi in entrambi gli oggetti ListBox vengono centrati.

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

Nella figura riportata di seguito vengono illustrati i due controlli ListBox quando viene applicato lo stile dell'esempio precedente.

Due controlli ListBox

La modalità di funzionamento dell'ereditarietà delle proprietà con gli stili e i contenitori di elementi è correlata al modo in cui è strutturato l'albero logico. Quando si crea in modo esplicito il contenitore di elementi, quest'ultimo fa parte dell'albero logico. Se non si crea il contenitore di elementi, quest'ultimo non fa parte dell'albero logico. Nella figura riportata di seguito viene illustrata la differenza nell'albero logico per i due controlli ListBox dell'esempio precedente.

Strutture ad albero visuali per due oggetti ListBox

Gli oggetti ereditati dalla classe Visual ereditano i valori delle proprietà dal relativo elemento padre logico. Nell'esempio riportato di seguito viene creato un oggetto ListBox con due controlli TextBlock e la proprietà Foreground di ListBox viene impostata su blu. Il primo controllo TextBlock, textBlock1 è contenuto all'interno di un oggetto ListBoxItem creato in modo esplicito, a differenza del secondo controllo TextBlock, textBlock2. Nell'esempio viene anche definito un oggetto Style per un oggetto ListBoxItem tramite cui la proprietà Foreground di un oggetto ListBoxItem viene impostata su 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>

Nella figura riportata di seguito viene illustrato l'oggetto ListBox creato nell'esempio precedente.

Due ListBoxItem in un controllo ListBox

La stringa in textBlock1 è verde mentre la stringa in textBlock2 è blu poiché ciascun controllo TextBlock eredita la proprietà Foreground dal rispettivo elemento padre logico. L'elemento padre logico di textBox1 è ListBoxItem, mentre l'elemento padre logico di textBox2 è ListBox. Per ulteriori informazioni, vedere Ereditarietà del valore della proprietà.

HeaderedItemsControl

Il controllo HeaderedItemsControl viene ereditato dalla classe ItemsControl. HeaderedItemsControl definisce la proprietà Header, che segue le stesse regole della proprietà Header di un controllo HeaderedContentControl. In WPF vengono forniti tre controlli che ereditano da HeaderedItemsControl:

Nell'esempio riportato di seguito viene creato un oggetto TreeViewItem. TreeView contiene un solo oggetto TreeViewItem, con etichetta TreeViewItem 1, che dispone dei seguenti elementi:

Nota

Nell'esempio vengono creati in modo esplicito gli oggetti TreeViewItem per gli ultimi due elementi poiché Rectangle e StackPanel vengono ereditati dalla classe Visual. La proprietà Foreground viene impostata con lo stile predefinito dell'oggetto TreeViewItem. Gli oggetti figlio ereditano il valore della proprietà dall'oggetto TreeViewItem creato in modo esplicito, che in genere è il comportamento desiderato.

<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>

Vedere anche

Concetti

Modello di contenuto WPF