データ テンプレートの概要
WPF データ テンプレート モデルを使用すると、データのプレゼンテーションを柔軟に定義できます。 WPF コントロールには、データプレゼンテーションのカスタマイズをサポートする機能が組み込まれています。 このトピックでは、最初に DataTemplate を定義する方法について説明してから、カスタム ロジックに基づくテンプレートの選択や階層データの表示のサポートなど、他のデータ テンプレート機能について説明します。
前提 条件
このトピックでは、データ テンプレート機能に焦点を当てており、データ バインディングの概念の導入ではありません。 基本的なデータ バインディングの概念については、「データ バインディングの概要」を参照してください。
DataTemplate はデータの表示に関するものであり、WPF のスタイルとテンプレート モデルによって提供される多くの機能の 1 つです。 Style を使用してコントロールのプロパティを設定する方法など、WPF スタイルとテンプレート モデルの概要については、「のスタイル設定とテンプレートの」トピックを参照してください。
さらに、Style や DataTemplate などのオブジェクトを再利用できるようにするための基本的な Resources
を理解することが重要です。 リソースの詳細については、「XAML リソース」を参照してください。
データ テンプレートの基本
DataTemplate が重要である理由を示すために、データ バインディングの例を見てみましょう。 この例では、ListBox が Task
オブジェクトのリストにバインドされています。 各 Task
オブジェクトには、TaskName
(文字列)、Description
(文字列)、Priority
(int)、および値が Home
と Work
を持つ Enum
である TaskType
型のプロパティがあります。
<Window x:Class="SDKSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample"
Title="Introduction to Data Templating Sample">
<Window.Resources>
<local:Tasks x:Key="myTodoList"/>
</Window.Resources>
<StackPanel>
<TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"/>
</StackPanel>
</Window>
DataTemplate がない場合
DataTemplateがないと、現在、ListBox は次のようになります。
特定の指示がない場合には、コレクション内のオブジェクトを表示しようとすると、既定で ListBox が ToString
を呼び出します。 したがって、Task
オブジェクトが ToString
メソッドをオーバーライドする場合、ListBox は基になるコレクション内の各ソース オブジェクトの文字列表現を表示します。
たとえば、Task
クラスがこのように ToString
メソッドをオーバーライドする場合、name
は TaskName
プロパティのフィールドです。
public override string ToString()
{
return name.ToString();
}
Public Overrides Function ToString() As String
Return _name.ToString()
End Function
その後、ListBox は次のようになります。
ただし、これは制限されており、柔軟性はありません。 また、XML データにバインドする場合は、ToString
をオーバーライドすることはできません。
単純な DataTemplate の定義
解決策は、DataTemplateを定義することです。 これを行う方法の 1 つは、ListBox の ItemTemplate プロパティを DataTemplateに設定する方法です。 DataTemplate で指定した内容は、データ オブジェクトの視覚的な構造になります。 次の DataTemplate は非常に簡単です。 各項目が StackPanel内の 3 つの TextBlock 要素として表示されることを指示しています。 各 TextBlock 要素は、Task
クラスのプロパティにバインドされます。
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
このトピックの例の基になるデータは、CLR オブジェクトのコレクションです。 XML データにバインドする場合、基本的な概念は同じですが、構文に若干の違いがあります。 たとえば、Path=TaskName
ではなく、XPath を @TaskName
に設定します (TaskName
が XML ノードの属性の場合)。
これで、ListBox は次のようになります。
DataTemplate をリソースとして作成する
上記の例では、DataTemplate をインラインで定義しました。 次の例のように、再利用可能なオブジェクトになるように resources セクションで定義する方が一般的です。
<Window.Resources>
<DataTemplate x:Key="myTaskTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
次の例のように、myTaskTemplate
をリソースとして使用できるようになりました。
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"/>
myTaskTemplate
はリソースであるため、DataTemplate 型を受け取るプロパティを持つ他のコントロールで使用できるようになりました。 上に示すように、ListBoxなどの ItemsControl オブジェクトの場合は、ItemTemplate プロパティです。 ContentControl オブジェクトの場合は、ContentTemplate プロパティです。
データ型 プロパティ
DataTemplate クラスには、Style クラスの TargetType プロパティによく似た DataType プロパティがあります。 そのため、上記の例で DataTemplate の x:Key
を指定する代わりに、次の操作を行うことができます。
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
この DataTemplate は、すべての Task
オブジェクトに自動的に適用されます。 この場合、x:Key
は暗黙的に設定されることに注意してください。 したがって、この DataTemplatex:Key
値を割り当てると、暗黙的な x:Key
がオーバーライドされ、DataTemplate は自動的には適用されません。
Task
オブジェクトのコレクションに ContentControl をバインドする場合、ContentControl は上記の DataTemplate を自動的に使用しません。 これは、ContentControl のバインドでは、コレクション全体にバインドするか、個々のオブジェクトにバインドするかを区別するために、より多くの情報が必要になるためです。 ContentControl が ItemsControl 型の選択を追跡している場合は、ContentControl バインドの Path プロパティを "/
" に設定して、現在の項目に関心があることを示すことができます。 例については、「コレクションにバインドし、選択に基づいて情報を表示する」を参照してください。 それ以外の場合は、ContentTemplate プロパティを設定して、DataTemplate を明示的に指定する必要があります。
DataType プロパティは、さまざまな種類のデータ オブジェクトの CompositeCollection がある場合に特に便利です。 例については、「CompositeCollectionを実装する」を参照してください。
DataTemplate に情報を追加する
現在、データは必要な情報と共に表示されますが、間違いなく改善の余地があります。 表示されるデータを記述する Border、Grid、およびいくつかの TextBlock 要素を追加して、プレゼンテーションを改善しましょう。
<DataTemplate x:Key="myTaskTemplate">
<Border Name="border" BorderBrush="Aqua" BorderThickness="1"
Padding="5" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Task Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Description:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Priority:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
</Grid>
</Border>
</DataTemplate>
次のスクリーンショットは、この変更された DataTemplateを含む ListBox を示しています。
ListBox において HorizontalContentAlignment を Stretch に設定することで、項目の幅がスペース全体を占めるようにすることができます。
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"
HorizontalContentAlignment="Stretch"/>
HorizontalContentAlignment プロパティを Stretchに設定すると、ListBox は次のようになります。
DataTriggers を使用してプロパティ値を適用する
現在のプレゼンテーションでは、Task
がホーム タスクかオフィス タスクかは表示されません。 Task
オブジェクトには TaskType
型の TaskType
プロパティがあります。それは Home
と Work
の値を持つ列挙型です。
次の例では、DataTrigger は、TaskType
プロパティが TaskType.Home
場合に、border
という名前の要素の BorderBrush を Yellow
に設定します。
<DataTemplate x:Key="myTaskTemplate">
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=TaskType}">
<DataTrigger.Value>
<local:TaskType>Home</local:TaskType>
</DataTrigger.Value>
<Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
アプリケーションは次のようになります。 ホーム タスクは黄色の枠線で表示され、オフィス タスクはアクア罫線で表示されます。
この例では、DataTrigger は Setter を使用してプロパティ値を設定します。 トリガー クラスには、アニメーションなどの一連のアクションを開始できる EnterActions プロパティと ExitActions プロパティもあります。 さらに、複数のデータ バインド プロパティ値に基づいて変更を適用できる MultiDataTrigger クラスもあります。
同じ効果を得る別の方法として、BorderBrush プロパティを TaskType
プロパティにバインドし、値コンバーターを使用して TaskType
値に基づいて色を返します。 コンバーターを使用して上記の効果を作成すると、パフォーマンスの点で若干効率的です。 さらに、独自のコンバーターを作成すると、独自のロジックを提供するため、柔軟性が向上します。 最終的に、どの手法を選択するかは、シナリオとユーザーの好みによって異なります。 コンバーターを記述する方法については、IValueConverterを参照してください。
DataTemplate に属するもの
前の例では、DataTemplate.Triggers プロパティを使用して DataTemplate 内にトリガーを配置しました。 トリガーの Setter は、DataTemplate内にある要素 (Border 要素) のプロパティの値を設定します。 ただし、Setters
が関係するプロパティが現在の DataTemplate内にある要素のプロパティでない場合は、ListBoxItem クラス用の Style を使用してプロパティを設定する方が適している場合があります (バインドするコントロールが ListBoxの場合)。 たとえば、マウスで項目をポイントしたときに Trigger で項目の Opacity 値をアニメーション化する場合は、ListBoxItem スタイル内でトリガーを定義します。 例としては、「スタイル設定とテンプレートのサンプル の概要」を参照してください。
一般に、生成された各 ListBoxItem に DataTemplate が適用されることに注意してください (実際に適用される方法と場所の詳細については、ItemTemplate ページを参照してください)。 DataTemplate は、データ オブジェクトのプレゼンテーションと外観にのみ関係します。 ほとんどの場合、プレゼンテーションの他のすべての側面 (項目が選択されたときの外観や、ListBox によるアイテムのレイアウト方法など) は、DataTemplateの定義に属していません。 例については、「ItemsControl のスタイル設定とテンプレート 」セクションを参照してください。
データ オブジェクトのプロパティに基づく DataTemplate の選択
DataType プロパティ セクションでは、さまざまなデータ オブジェクトに対して異なるデータ テンプレートを定義できることを説明しました。 これは、異なる型の CompositeCollection がある場合や、異なる型の項目を持つコレクションがある場合に特に便利です。 DataTriggers を使用したプロパティ値の適用 セクションでは、同じ種類のデータ オブジェクトのコレクションがある場合は、DataTemplate を作成し、トリガーを使用して各データ オブジェクトのプロパティ値に基づいて変更を適用できることを示しました。 ただし、トリガーを使用すると、プロパティ値を適用したり、アニメーションを開始したりできますが、データ オブジェクトの構造を柔軟に再構築することはできません。 シナリオによっては、同じ型でプロパティが異なるデータ オブジェクトに対して異なる DataTemplate を作成することが必要になる場合があります。
たとえば、Task
オブジェクトに 1
の Priority
値がある場合は、自分のアラートとして機能するようにまったく異なる外観を指定できます。 その場合は、優先度の高い Task
オブジェクトを表示するための DataTemplate を作成します。 リソース セクションに次の DataTemplate を追加しましょう。
<DataTemplate x:Key="importantTaskTemplate">
<DataTemplate.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="20"/>
</Style>
</DataTemplate.Resources>
<Border Name="border" BorderBrush="Red" BorderThickness="1"
Padding="5" Margin="5">
<DockPanel HorizontalAlignment="Center">
<TextBlock Text="{Binding Path=Description}" />
<TextBlock>!</TextBlock>
</DockPanel>
</Border>
</DataTemplate>
この例では、DataTemplate.Resources プロパティを使用します。 そのセクションで定義されているリソースは、DataTemplate内の要素によって共有されます。
データ オブジェクトの Priority
値に基づいて使用する DataTemplate を選択するロジックを指定するには、DataTemplateSelector のサブクラスを作成し、SelectTemplate メソッドをオーバーライドします。 次の例では、SelectTemplate メソッドは、Priority
プロパティの値に基づいて適切なテンプレートを返すロジックを提供します。 返すテンプレートは、囲む Window 要素のリソースにあります。
using System.Windows;
using System.Windows.Controls;
namespace SDKSample
{
public class TaskListDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate
SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null && item is Task)
{
Task taskitem = item as Task;
if (taskitem.Priority == 1)
return
element.FindResource("importantTaskTemplate") as DataTemplate;
else
return
element.FindResource("myTaskTemplate") as DataTemplate;
}
return null;
}
}
}
Namespace SDKSample
Public Class TaskListDataTemplateSelector
Inherits DataTemplateSelector
Public Overrides Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate
Dim element As FrameworkElement
element = TryCast(container, FrameworkElement)
If element IsNot Nothing AndAlso item IsNot Nothing AndAlso TypeOf item Is Task Then
Dim taskitem As Task = TryCast(item, Task)
If taskitem.Priority = 1 Then
Return TryCast(element.FindResource("importantTaskTemplate"), DataTemplate)
Else
Return TryCast(element.FindResource("myTaskTemplate"), DataTemplate)
End If
End If
Return Nothing
End Function
End Class
End Namespace
その後、TaskListDataTemplateSelector
をリソースとして宣言できます。
<Window.Resources>
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
</Window.Resources>
テンプレート セレクター リソースを使用するには、ListBoxの ItemTemplateSelector プロパティに割り当てます。 ListBox は、基になるコレクション内の各項目の TaskListDataTemplateSelector
の SelectTemplate メソッドを呼び出します。 呼び出しは、項目パラメーターとしてデータ オブジェクトを渡します。 メソッドによって返される DataTemplate は、そのデータ オブジェクトに適用されます。
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
HorizontalContentAlignment="Stretch"/>
テンプレート セレクターを配置すると、ListBox は次のように表示されます。
これで、この例の説明は終了です。 完全なサンプルについては、「データ テンプレートのサンプルの概要」を参照してください。
ItemsControl のスタイル設定とテンプレート化
ItemsControl は、DataTemplate を使用できる唯一のコントロール型ではありませんが、ItemsControl をコレクションにバインドすることは非常に一般的なシナリオです。 「DataTemplate の Tasks
コレクションにバインドされています。 デモンストレーションの目的で、この例のスタイルとテンプレートはすべてインラインで宣言されています。
<ItemsControl Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}">
<!--The ItemsControl has no default visual appearance.
Use the Template property to specify a ControlTemplate to define
the appearance of an ItemsControl. The ItemsPresenter uses the specified
ItemsPanelTemplate (see below) to layout the items. If an
ItemsPanelTemplate is not specified, the default is used. (For ItemsControl,
the default is an ItemsPanelTemplate that specifies a StackPanel.-->
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border BorderBrush="Aqua" BorderThickness="1" CornerRadius="15">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<!--Use the ItemsPanel property to specify an ItemsPanelTemplate
that defines the panel that is used to hold the generated items.
In other words, use this property if you want to affect
how the items are laid out.-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--Use the ItemTemplate to set a DataTemplate to define
the visualization of the data objects. This DataTemplate
specifies that each data object appears with the Proriity
and TaskName on top of a silver ellipse.-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="18"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataTemplate.Resources>
<Grid>
<Ellipse Fill="Silver"/>
<StackPanel>
<TextBlock Margin="3,3,3,0"
Text="{Binding Path=Priority}"/>
<TextBlock Margin="3,0,3,7"
Text="{Binding Path=TaskName}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!--Use the ItemContainerStyle property to specify the appearance
of the element that contains the data. This ItemContainerStyle
gives each item container a margin and a width. There is also
a trigger that sets a tooltip that shows the description of
the data object when the mouse hovers over the item container.-->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Control.Width" Value="100"/>
<Setter Property="Control.Margin" Value="5"/>
<Style.Triggers>
<Trigger Property="Control.IsMouseOver" Value="True">
<Setter Property="Control.ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=Content.Description}"/>
</Trigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
レンダリング時の例のスクリーンショットを次に示します。
ItemTemplateを使用する代わりに、ItemTemplateSelectorを使用できることに注意してください。 例については、前のセクションを参照してください。 同様に、ItemContainerStyleを使用する代わりに、ItemContainerStyleSelectorを使用することもできます。
ここに示されていない ItemsControl の他の 2 つのスタイル関連プロパティは、GroupStyle と GroupStyleSelectorです。
階層データのサポート
ここまでは、1 つのコレクションにバインドして表示する方法のみを見てきた。 他のコレクションを含むコレクションがある場合があります。 HierarchicalDataTemplate クラスは、このようなデータを表示するために HeaderedItemsControl 型と共に使用するように設計されています。 次の例では、ListLeagueList
は League
オブジェクトの一覧です。 各 League
オブジェクトには、Name
と Division
オブジェクトのコレクションがあります。 各 Division
には Name
と Team
オブジェクトのコレクションがあり、各 Team
オブジェクトには Name
があります。
<Window x:Class="SDKSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="HierarchicalDataTemplate Sample"
xmlns:src="clr-namespace:SDKSample">
<DockPanel>
<DockPanel.Resources>
<src:ListLeagueList x:Key="MyList"/>
<HierarchicalDataTemplate DataType = "{x:Type src:League}"
ItemsSource = "{Binding Path=Divisions}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType = "{x:Type src:Division}"
ItemsSource = "{Binding Path=Teams}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type src:Team}">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</DockPanel.Resources>
<Menu Name="menu1" DockPanel.Dock="Top" Margin="10,10,10,10">
<MenuItem Header="My Soccer Leagues"
ItemsSource="{Binding Source={StaticResource MyList}}" />
</Menu>
<TreeView>
<TreeViewItem ItemsSource="{Binding Source={StaticResource MyList}}" Header="My Soccer Leagues" />
</TreeView>
</DockPanel>
</Window>
この例では、HierarchicalDataTemplateを使用すると、他のリストを含むリスト データを簡単に表示できることを示しています。 この例のスクリーンショットを次に示します。
相關聯項目
.NET Desktop feedback