Controls 內容模型概觀
更新:2007 年 11 月
本主題討論繼承自 Control 之類別使用的內容模型。內容模型會指定控制項可以包含的物件類型。在本主題中,「控制項」一詞限制為在其類別階層之某個位置具有 Control 類別的類別。本主題討論的四個內容模型,是透過下列繼承自 Control 的四個類別所定義:
ContentControl 包含單一項目。
HeaderedContentControl 包含標頭和單一項目。
ItemsControl 包含項目的集合。
HeaderedItemsControl 包含標頭和項目的集合。
這四個類別是做為 WPF 中大部分控制項的基底類別。使用這些內容模型的類別可以包含相同的內容類型,並以相同方式處理內容。而可以放在 ContentControl (或繼承自 ContentControl 的類別) 中的任何物件類型,都可以放在具有其他三個內容模型之任一個內容模型的控制項中。下圖顯示每個內容模型中一個內含影像和某些文字的控制項。
這個主題包含下列章節。
- 必要條件
- ContentControl
- HeaderedContentControl
- ItemsControl
- HeaderedItemsControl
- 相關主題
必要條件
本主題假設您對 WPF 已有基本了解,而且知道如何將控制項加入至應用程式。如需詳細資訊,請參閱 Windows Presentation Foundation 使用者入門 和控制項概觀。
ContentControl
在這四個內容模型中,最簡單的是 ContentControl,具有 Content 屬性。Content 屬性是屬於 Object 類型,因此不會限制您放入 ContentControl 中的內容。您可以使用可延伸標記語言 (XAML) 或程式碼設定 Content。
下列控制項使用 ContentControl 內容模型:
下列範例示範如何建立四個 Button 控制項,其中的 Content 設定為下列其中一項:
![]() |
---|
可延伸標記語言 (XAML) 範例版本可以使用 <Button.Content> 標記括住每個按鈕的內容,但這不是必要動作。如需詳細資訊,請參閱 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;
下圖顯示在先前範例中建立的四個按鈕。
HeaderedContentControl
HeaderedContentControl 會從 ContentControl 繼承 Content 屬性,並定義類型為 Object 的 Header 屬性。Header 提供控制項的標題。與 ContentControl 的 Content 屬性類似,Header 可以是任何類型。WPF 會提供繼承自 HeaderedContentControl 的三個控制項:
下列範例會建立內含兩個 TabItem 物件的 TabControl (ItemsControl)。第一個 TabItem 在 Header 和 Content 中都具有豐富內容:Header 是設定為內含 Ellipse 和 TextBlock 的 StackPanel,而 Content 是設定為內含 TextBlock 和 Label 的 StackPanel。第二個 TabItem 的 Header 是設定為字串,而 Content 是設定為單一 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>
下圖顯示先前範例所建立的 TabControl。
ItemsControl
繼承自 ItemsControl 的控制項會包含物件的集合。ItemsControl 的範例是 ListBox。您可以使用 ItemsSource 屬性或 Items 屬性來填入 (Populate) ItemsControl。
ItemsSource 屬性
ItemsControl 的 ItemsSource 屬性可讓您使用任何類型,將 IEnumerable 實作為 ItemsControl 的內容。ItemsSource 通常用來顯示資料集合,或將 ItemsControl 繫結至集合物件。
下列範例會建立稱為 MyData 的類別,這是簡單字串集合。
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");
}
}
下列範例會將 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);
下圖顯示先前範例中建立的 ListBox。
如需資料繫結的詳細資訊,請參閱資料繫結概觀。
Items 屬性
如果不想要使用實作 IEnumerable 的物件來填入 ItemsControl,則可以使用 Items 屬性加入項目。ItemsControl 中的項目彼此可以具有不同的類型。例如,ListBox 可以包含一個字串項目,以及另一個 Image 項目。
![]() |
---|
設定 ItemsSource 屬性時,您可以使用 Items 屬性讀取 ItemCollection,但是不可以加入或修改 ItemCollection。將 ItemsSource 屬性設定為 Null 參考 (在 Visual Basic 中是Nothing) 會移除集合,並將用法還原為 Items,這會是空 ItemCollection。 |
下列範例會建立具有四個不同項目類型的 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>
' 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);
下圖顯示先前範例中建立的 ListBox。
項目容器類別
WPF 提供的每個 ItemsControl 都會有對應的類別,以代表 ItemsControl 中的項目。下表列出 WPF 提供的 ItemsControl 物件和其對應的項目容器 (Container)。
ItemsControl |
項目容器 |
---|---|
您可以明確地針對 ItemsControl 中的每個項目建立項目容器,但這不是必要動作。是否要在 ItemsControl 中建立項目容器,絕大部分是取決於案例。例如,如果將資料繫結至 ItemsSource 屬性,則不會明確建立項目容器。下列是需要記住的重點:
ItemCollection 中的物件類型會根據是否明確建立項目容器而不同。
即使未明確建立項目容器,還是可以取得項目容器。
不論是否明確建立項目容器,都會套用 TargetType 設定為項目容器的 Style。
因為只有明確建立的項目容器才是屬於邏輯樹狀結構的一部分,所以在隱含和明確建立的項目容器中,屬性繼承 (Inheritance) 的行為會不同。
為了說明這些重點,下列範例會建立兩個 ListBox 控制項。這個範例會為第一個 ListBox 建立 ListBoxItem 物件,但不會為第二個 ListBox 建立。在第二種情況下,會針對 ListBox 中的每個項目隱含建立 ListBoxItem。
<!--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>
這兩個 ListBox 的 ItemCollection 會不同。第一個 ListBox 之 Items 屬性中的每個項目都是 ListBoxItem,但第二個 ListBox 中的類型則不同。下列範例會逐一查看兩個 ListBox 控制項中的項目並檢查每個項目的類型,以便確認這種情況。
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
*/
下圖顯示先前範例中建立的兩個 ListBox 控制項。
通常,您會想要有項目的項目容器,但是卻未在應用程式中明確建立項目容器。若要取得與特定項目相關聯的項目容器,請使用 ContainerFromItem 方法。下列範例顯示在未明確建立 ListBoxItem 時,如何取得與項目相關聯的項目容器。這個範例會假設稱為 itemToSelect 的物件不是 ListBoxItem,而且已加入至 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;
}
TargetType 已設定為項目容器的樣式會套用至隱含和明確建立的項目容器。下列範例會建立 Style 做為 ListBoxItem 的資源,以將 ListBoxItem 中的內容水平置中。將這個樣式套用至 ListBox 物件時,這兩個 ListBox 物件中的項目都會置中。
<!--Create a Style as a Resource.-->
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
下圖顯示套用先前範例中的樣式後的兩個 ListBox 控制項。
屬性繼承與樣式和項目容器搭配運作的方式,與邏輯樹狀結構的邏輯有關,當您明確建立項目容器時,項目容器會是邏輯樹狀結構的一部分。如果未建立項目容器,則項目容器不會是邏輯樹狀結構的一部分。下圖顯示先前範例中這兩個 ListBox 控制項的邏輯樹狀結構差異。
繼承自 Visual 類別的物件會繼承其邏輯父代 (Parent) 的屬性值。下列範例會建立具有兩個 TextBlock 控制項的 ListBox,並將 ListBox 的 Foreground 屬性設定為 blue。第一個 TextBlock (textBlock1) 是包含在明確建立的 ListBoxItem 內,而第二個 TextBlock (textBlock2) 則不是。這個範例也會為 ListBoxItem 定義 Style,以將 ListBoxItem 的 Foreground 設定為 green。
<!--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>
下圖顯示先前範例中建立的 ListBox。
因為每個 TextBlock 控制項的 Foreground 屬性都是繼承自它的個別邏輯父代,所以 textBlock1 中的字串是綠色,而 textBlock2 中的字串會是藍色。textBox1 的邏輯父代是 ListBoxItem,而 textBox2 的邏輯父代是 ListBox。如需詳細資訊,請參閱屬性值繼承。
HeaderedItemsControl
HeaderedItemsControl 繼承自 ItemsControl 類別。HeaderedItemsControl 定義 Header 屬性,而遵循的規則是與 HeaderedContentControl 的 Header 屬性相同。WPF 提供三個繼承自 HeaderedItemsControl 的控制項:
下列範例將建立 TreeViewItem。TreeView 包含單一 TreeViewItem (標籤為 TreeViewItem 1),而且具有下列項目:
字串。
DateTime 物件。
在其 Header 中包含 Rectangle 的 TreeViewItem。
其 Header 屬性設定為內含兩個物件之 StackPanel 的 TreeViewItem。
![]() |
---|
因為 Rectangle 和 StackPanel 是繼承自 Visual 類別,所以這個範例會明確建立後兩個項目的 TreeViewItem 物件。TreeViewItem 的預設樣式會設定 Foreground 屬性。子物件會繼承明確建立之 TreeViewItem 的屬性值,一般就是所需的行為。 |
<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>