Control コンテンツ モデルの概要
更新 : 2007 年 11 月
このトピックでは、Control を継承するクラスで使用されるコンテンツ モデルについて説明します。このコンテンツ モデルは、コントロールに含めることができるオブジェクトの型を指定します。このトピックでは、"control" という用語の意味を、クラス階層のどこかに Control クラスを持つクラスに限定します。このトピックで説明する 4 つのコンテンツ モデルは、Control を継承する次の 4 つのクラスによって定義されます。
ContentControl には、項目が 1 つ格納されます。
HeaderedContentControl には、ヘッダーと項目が 1 つ格納されます。
ItemsControl には、項目のコレクションが格納されます。
HeaderedItemsControl には、ヘッダーと項目のコレクションが格納されます。
これらの 4 つのクラスは、WPF のほとんどのコントロールで使用される基本クラスとして機能します。これらのコンテンツ モデルを使用するクラスには、同じ型のコンテンツを格納して、同じように扱うことができます。つまり、ContentControl (または ContentControl を継承するクラス) に格納できるオブジェクトは、どのような型でも、他の 3 つのコンテンツ モデルのどれかを持つコントロールに格納できます。次の図は、イメージやテキストを含む各コンテンツ モデルのコントロールを示しています。
このトピックには次のセクションが含まれています。
- 必要条件
- ContentControl
- HeaderedContentControl
- ItemsControl
- HeaderedItemsControl
- 関連トピック
必要条件
このトピックは、WPF の基本的な理解があり、コントロールをアプリケーションに追加する方法に関する知識があることを想定しています。詳細については、「Windows Presentation Foundation の概要」および「コントロールの概要」を参照してください。
ContentControl
4 つの中で最もシンプルなコンテンツ モデルが ContentControl で、これには Content プロパティがあります。Content プロパティは Object 型で、ContentControl に格納できる項目に制限はありません。Content の設定には、Extensible Application Markup Language (XAML) またはコードを使用できます。
次のコントロールでは ContentControl コンテンツ モデルを使用しません。
次の例では、Content を次のどれかに設定して 4 つの Button コントロールを作成する方法を示します。
メモ : |
---|
このサンプル Extensible Application Markup Language (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;
前の例で作成した 4 つのボタンを次の図に示します。
HeaderedContentControl
HeaderedContentControl は Content プロパティを ContentControl から継承し、Object 型の Header プロパティを定義します。Header はコントロールの見出しを提供します。ContentControl の Content プロパティと同様に、Header の型は任意です。WPF には、HeaderedContentControl を継承する次の 3 つのコントロールが用意されています。
次の例では、TabItem オブジェクトを 2 つ含む TabControl (ItemsControl) を作成します。1 つ目の TabItem は、Header と Content のどちらにもリッチ コンテンツが格納されます。Header は Ellipse と TextBlock を含む StackPanel に設定され、Content は TextBlock と Label を含む StackPanel に設定されます。2 番目の TabItem の Header は文字列に設定され、Content は 1 つの 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 です。ItemsControl の設定には、ItemsSource プロパティまたは Items プロパティを使用できます。
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 プロパティ
ItemsControl を設定するのに IEnumerable を実装したオブジェクトを使用しない場合は、Items プロパティを使用して項目を追加できます。ItemsControl には、異なる型の項目が混在する可能性があります。たとえば、ListBox には、文字列である項目と Image である項目が格納されることがあります。
メモ : |
---|
ItemsSource プロパティが設定されると、Items プロパティを使用して ItemCollection を読み取ることができますが、ItemCollection の追加や変更はできません。ItemsSource プロパティを null 参照 (Visual Basic では Nothing) に設定すると、コレクションは削除され、Items は再び空の ItemCollection として使用されます。 |
次の例では、4 つの異なる型の項目を持つ 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 を次の図に示します。
Item コンテナ クラス
WPF に組み込まれている各 ItemsControl には、ItemsControl に含まれている項目を表す対応クラスが存在します。WPF に組み込まれている ItemsControl オブジェクトと対応する項目コンテナの一覧を次の表に示します。
ItemsControl |
項目コンテナ |
---|---|
ItemsControl に格納されている各項目に対して項目コンテナを明示的に作成することができますが、必須ではありません。項目コンテナを ItemsControl に作成するかどうかは、シナリオによって異なります。たとえば、データを ItemsSource プロパティにバインドする場合は、項目コンテナを明示的に作成することはありません。次の点が重要ですので留意してください。
ItemCollection に格納されるオブジェクトの型は、項目コンテナを明示的に作成するかどうかによって異なります。
項目コンテナは、明示的に作成していない場合でも取得できます。
TargetType が項目コンテナに設定された Style は、その項目コンテナが明示的に作成されたものかどうかに関係なく適用されます。
プロパティの継承の動作は、暗黙の項目コンテナと明示的に作成された項目コンテナとで異なります。その理由は、明示的に作成された項目コンテナは論理ツリーの一部であるためです。
これらの点を説明するために、次の例では 2 つの ListBox コントロールを作成します。この例では、ListBoxItem オブジェクトを最初の ListBox に対して作成しますが、2 番目の ListBox には作成しません。2 番目の例では、ListBoxItem が 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>
各 ListBox の ItemCollection は異なります。最初の ListBox の Items プロパティに含まれている各項目は ListBoxItem ですが、2 番目の 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
*/
前の例で作成した 2 つの 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>
前の例のスタイルが適用されたときの 2 つの ListBox コントロールを次の図に示します。
スタイルと項目コンテナを持つプロパティ インスタンスの動作は、論理ツリーの構造と関連しています。明示的に作成された項目コンテナは、論理ツリーの一部になります。項目コンテナを作成しなかった場合、項目コンテナは論理ツリーの一部にはなりません。前の例の 2 つの ListBox コントロールの論理ツリーにおける違いを次の図に示します。
Visual クラスを継承したオブジェクトは、その論理上の親からプロパティ値を継承します。次の例は、TextBlock コントロールを 2 つ持つ ListBox を作成し、ListBox の Foreground プロパティを "blue" に設定します。最初の TextBlock である textBlock1 は、明示的に作成された ListBoxItem に格納されていますが、2 番目の TextBlock である textBlock2 はそうではありません。また、この例では ListBoxItem の Foreground を "green" に設定する Style を ListBoxItem に定義しています。
<!--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 を次の図に示します。
textBlock1 の文字列は "green" で textBlock2 の文字列は "blue" ですが、その理由は、それぞれの TextBlock コントロールがそれぞれの論理上の親の Foreground プロパティを継承しているためです。textBox1 の論理上の親は ListBoxItem で、textBox2 の論理上の親は ListBox です。詳細については、「プロパティ値の継承」を参照してください。
HeaderedItemsControl
HeaderedItemsControl は ItemsControl クラスを継承しています。HeaderedItemsControl は Header プロパティを定義します。このプロパティは、HeaderedContentControl の Header プロパティと同じ規則に従います。WPF には、HeaderedItemsControl を継承する次の 3 つのコントロールが組み込まれています。
TreeViewItem を作成する例を次に示します。TreeView には 1 つの TreeViewItem が格納されています。名前は TreeViewItem 1 で、次の項目があります。
文字列
DateTime オブジェクト
Header に Rectangle が格納された TreeViewItem
2 つのオブジェクトを格納する StackPanel に設定された Header プロパティを持つ TreeViewItem
メモ : |
---|
この例は、最後の 2 つの項目に対して TreeViewItem オブジェクトを明示的に作成しますが、それは Rectangle と StackPanel が Visual クラスを継承することが理由です。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>