データ テンプレートの概要

WPF のデータ テンプレート モデルにより、データの表示を非常に柔軟に定義できます WPF コントロールには、データ表示のカスタマイズをサポートする機能が組み込まれています。 ここでは、まず DataTemplate を定義する方法を示し、次にカスタム ロジックに基づくテンプレートの選択や階層データの表示のサポートなどのその他のデータ テンプレート機能について説明します。

このトピックは、次のセクションで構成されています。

  • 必要条件
  • データ テンプレートの基本
  • DataTemplate への追加
  • データ オブジェクトのプロパティに基づく DataTemplate の選択
  • ItemsControl のスタイルとテンプレートの設定
  • 階層データのサポート
  • 関連トピック

必要条件

ここでは、データ テンプレート機能に焦点を当て、データ バインディングの概念については説明しません。 データ バインディングの基本概念については、「データ バインディングの概要」を参照してください。

DataTemplate はデータの表示に関する機能で、WPF のスタイルとテンプレート モデルが提供する多くの機能の 1 つです。 Style を使用してコントロールのプロパティを設定する方法など、WPF のスタイルとテンプレート モデルの概要については、「スタイルとテンプレート」を参照してください。

また、StyleDataTemplate などのオブジェクトを再利用できるようにする Resources について理解することも重要です。 リソースの詳細については、「リソースの概要」を参照してください。

データ テンプレートの基本

ここでは、次の項目について説明します。

  • DataTemplate を使用しない場合
  • 単純な DataTemplate の定義
  • リソースとしての DataTemplate の作成
  • DataType プロパティ

DataTemplate が重要である理由を説明するために、データ バインディングの例を示します。 この例では、ListBox が Task オブジェクトのリストにバインドされています。 各 Task オブジェクトには、TaskName (文字列)、Description (文字列)、Priority (int)、および値 Home と Work を持つ Enum である TaskType 型のプロパティが含まれます。

<Window x:Class="SDKSample.Window1"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://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 Overrides Function ToString() As String
    Return _name.ToString()
End Function
public override string ToString()
{
    return name.ToString();
}

この場合、ListBox は次のようになります。

データ テンプレート サンプルのスクリーンショット

ただし、これには制限があり、柔軟性に欠けます。 また、XML データにバインドする場合、ToString をオーバーライドすることはできません。

単純な DataTemplate の定義

解決策は、DataTemplate を定義することです。 定義するには、ListBoxItemTemplate プロパティを 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 インラインを定義しました。 次の例に示すように、このテンプレートは、再利用可能なオブジェクトにするために、リソース セクションで定義するのが一般的です。

<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 プロパティになります。

DataType プロパティ

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 が暗黙的に設定されることに注意してください。 したがって、この DataTemplate に x:Key 値を割り当てる場合は、暗黙の x:Key をオーバーライドします。DataTemplate は自動的に適用されません。

ContentControl を Task オブジェクトのコレクションにバインドする場合、ContentControl は上記の DataTemplate を自動的に使用しません。 これは、ContentControl のバインディングは、コレクション全体または個々のオブジェクトのどちらにバインドするのかを区別するための追加情報を必要とするためです。 ContentControlItemsControl 型の選択内容を追跡する場合、ContentControl バインディングの Path プロパティを "/" に設定すると、現在の項目を対象とすることを示すことができます。 例については、「方法 : コレクションにバインドして選択に基づく情報を表示する」を参照してください。 それ以外の場合は、ContentTemplate プロパティを設定して DataTemplate を明示的に指定する必要があります。

DataType プロパティは、データ オブジェクトの異なる型の CompositeCollection がある場合に特に役立ちます。 例については、「方法 : CompositeCollection を実装する」を参照してください。

DataTemplate への追加

現在、必要な情報を含むデータが表示されていますが、改善の余地があります。 ここでは、BorderGrid、および表示されるデータを記述する 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 を示しています。

データ テンプレート サンプルのスクリーンショット

ListBoxHorizontalContentAlignmentStretch に設定して、項目の幅をスペース全体に引き伸ばすことができます。

<ListBox Width="400" Margin="10"
     ItemsSource="{Binding Source={StaticResource myTodoList}}"
     ItemTemplate="{StaticResource myTaskTemplate}" 
     HorizontalContentAlignment="Stretch"/>

HorizontalContentAlignment プロパティを Stretch に設定すると、ListBox は次のようになります。

データ テンプレート サンプルのスクリーンショット

DataTrigger を使用したプロパティ値の適用

現在の表示では、Task が家庭の用事か職場の仕事かを判断できません。 Task オブジェクトには、値 Home および Work を持つ列挙体である TaskType 型の TaskType プロパティが含まれます。

次の例では、TaskType プロパティが TaskType.Home の場合に、DataTrigger は 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>

アプリケーションは、次のようになります。 家庭の用事は黄色の境界線で表示され、職場の仕事は水色の境界線で表示されます。

データ テンプレート サンプルのスクリーンショット

この例では、DataTriggerSetter を使用してプロパティ値を設定します。 トリガー クラスには、アニメーションなどの一連のアクションを開始できるようにする EnterActions プロパティと ExitActions プロパティもあります。 また、複数のデータ バインドされたプロパティ値に基づいて変更を適用できるようにする MultiDataTrigger クラスもあります。

同じ効果を得る別の方法として、BorderBrush プロパティを TaskType プロパティにバインドし、値コンバーターを使用して TaskType 値に基づいて色を返す方法が挙げられます。 コンバーターを使用して上記の効果を得る方が、パフォーマンスの点でわずかに効率的です。 また、独自のコンバーターを作成すると、独自のロジックを指定できるため、柔軟性が高まります。 最終的に、どちらの手法を選択するかはシナリオと設定によって決まります。 コンバーターを作成する方法の詳細については、「IValueConverter」を参照してください。

DataTemplate に属しているもの

前の例では、DataTemplate.Triggers プロパティを使用して DataTemplate 内にトリガーを配置しました。 トリガーの Setter は、DataTemplate 内の要素 (Border 要素) のプロパティの値を設定します。 ただし、Setters が関係するプロパティが現在の DataTemplate 内の要素のプロパティではない場合は、ListBoxItem クラス (バインドするコントロールが ListBox である場合) の Style を使用してプロパティを設定する方が適切な場合があります。 たとえば、マウスが項目を指したときに Trigger で項目の Opacity 値をアニメーション化する場合は、トリガーを ListBoxItem スタイル内で定義します。 サンプル全体については、スタイルとテンプレート サンプルの概要を参照してください。

通常、DataTemplate は生成された各 ListBoxItem に適用されることに留意してください (実際の適用方法と適用場所の詳細については、ItemTemplate のページを参照してください)。 DataTemplate は、データ オブジェクトの表示と外観のみに関係します。 多くの場合、項目の選択時の表示や ListBox の項目の配置方法など、他のすべての表示の特性は DataTemplate の定義には含まれません。 例については、「ItemsControl のスタイルとテンプレートの設定」を参照してください。

データ オブジェクトのプロパティに基づく DataTemplate の選択

「DataType プロパティ」で、異なるデータ オブジェクトに対して異なるデータ テンプレートを定義できることを説明しました。 これは、異なる型の CompositeCollection または異なる型の項目を含むコレクションがある場合に特に役立ちます。 「DataTrigger を使用したプロパティ値の適用」で、同じ型のデータ オブジェクトのコレクションがある場合、DataTemplate を作成してトリガーを使用し、各データ オブジェクトのプロパティ値に基づいて変更を適用できることを示しました。 ただし、トリガーを使用すると、プロパティ値を適用したりアニメーションを開始したりすることができますが、データ オブジェクトの構造を柔軟に再構築することはできません。 シナリオによっては、異なるプロパティを持つ同じ型の複数のデータ オブジェクトに対して別の DataTemplate を作成する必要が生じることもあります。

たとえば、Task オブジェクトの Priority 値が 1 である場合、注意を促すようにその外観をまったく異なるものにすることができます。 この場合、優先順位の高い 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 要素のリソース内にあります。


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

その後、リソースとして TaskListDataTemplateSelector を宣言できます。

<Window.Resources>


...


<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>


...


</Window.Resources>

テンプレート セレクター リソースを使用するには、それを ListBoxItemTemplateSelector プロパティに割り当てます。 ListBox は、基になるコレクション内の各項目に対して TaskListDataTemplateSelector の SelectTemplate メソッドを呼び出します。 この呼び出しは、項目パラメーターとしてデータ オブジェクトを渡します。 次に、メソッドから返された DataTemplate がそのデータ オブジェクトに適用されます。

<ListBox Width="400" Margin="10"
         ItemsSource="{Binding Source={StaticResource myTodoList}}"
         ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
         HorizontalContentAlignment="Stretch"/>

テンプレート セレクターを配置すると、ListBox は次のようになります。

データ テンプレート サンプルのスクリーンショット

これで、この例の説明を終了します。 サンプル全体については、データ テンプレート サンプルの概要を参照してください。

ItemsControl のスタイルとテンプレートの設定

ItemsControlDataTemplate で使用できる唯一のコントロールではありませんが、ItemsControl をコレクションにバインドするシナリオが一般的です。 「DataTemplate に属しているもの」で、DataTemplate の定義はデータの表示のみに関係することを説明しました。 DataTemplate の使用が適していない場合を判断するには、ItemsControl によって提供されるさまざまなスタイル プロパティやテンプレート プロパティを理解することが重要です。 このような各種プロパティの機能を説明するために用意された例を次に示します。 この例の ItemsControl は、前の例と同じ 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>

この例が描画されたときのスクリーンショットを次に示します。

ItemsControl の例のスクリーンショット

ItemTemplate を使用する代わりに ItemTemplateSelector を使用できることに注意してください。 例については、前のセクションを参照してください。 同様に、ItemContainerStyle を使用する代わりに ItemContainerStyleSelector を使用することもできます。

ここに表示されていない ItemsControl の他の 2 つのスタイル関連プロパティに、GroupStyleGroupStyleSelector があります。

階層データのサポート

これまで、1 つのコレクションへのバインディングとその表示方法についてのみ説明してきました。 場合によっては、他のコレクションを含むコレクションもあります。 HierarchicalDataTemplate クラスは、そのようなデータを表示するために HeaderedItemsControl 型と共に使用されます。 次の例では、ListLeagueList は League オブジェクトのリストです。 League オブジェクトにはそれぞれ Division オブジェクトのコレクションと Name があります。 Division にはそれぞれ Team オブジェクトのコレクションと Name があり、Team オブジェクトにはそれぞれ Name があります。

<Window x:Class="SDKSample.Window1"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://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 を使用することにより、他のリストが含まれるリスト データを簡単に表示できることを示しています。 この例のスクリーンショットを次に示します。

HierarchicalDataTemplate のサンプルのスクリーンショット

参照

処理手順

方法 : DataTemplate によって生成された要素を検索する

概念

パフォーマンスの最適化 : データ バインディング

スタイルとテンプレート

データ バインディングの概要

GridView の列ヘッダー スタイルおyびテンプレートの概要