次の方法で共有


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

WPF データ テンプレート モデルを使用すると、データのプレゼンテーションを柔軟に定義できます。 WPF コントロールには、データプレゼンテーションのカスタマイズをサポートする機能が組み込まれています。 このトピックでは、最初に DataTemplate を定義する方法について説明してから、カスタム ロジックに基づくテンプレートの選択や階層データの表示のサポートなど、他のデータ テンプレート機能について説明します。

[前提条件]

このトピックでは、データ テンプレート機能に焦点を当てており、データ バインディングの概念の導入ではありません。 基本的なデータ バインディングの概念については、「 データ バインディングの概要」を参照してください。

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

さらに、ResourcesStyleなどのオブジェクトを再利用できるようにするための基本的なDataTemplateを理解することが重要です。 リソースの詳細については、「 XAML リソース」を参照してください。

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

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

<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は次のようになります。

各ソース オブジェクトの文字列表現 SDKSample.Task が表示されている [マイ タスク リスト リスト ボックス] を示す [データ テンプレートサンプルの概要] ウィンドウのスクリーンショット。

特に指示がない場合、コレクション内のオブジェクトを表示しようとすると、既定でListBoxToStringを呼び出します。 したがって、 Task オブジェクトが ToString メソッドをオーバーライドする場合、 ListBox は基になるコレクション内の各ソース オブジェクトの文字列形式を表示します。

たとえば、 Task クラスがこのように ToString メソッドをオーバーライドする場合、 nameTaskName プロパティのフィールドです。

public override string ToString()
{
    return name.ToString();
}
Public Overrides Function ToString() As String
    Return _name.ToString()
End Function

その後、 ListBox は次のようになります。

タスクの一覧を表示する [マイ タスク リスト リスト ボックス] を示す [データ テンプレートサンプルの概要] ウィンドウのスクリーンショット。

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

単純な DataTemplate の定義

解決策は、 DataTemplateを定義することです。 これを行う方法の 1 つは、ItemTemplateListBox プロパティをDataTemplateに設定する方法です。 DataTemplateで指定した内容は、データ オブジェクトの視覚的な構造になります。 次の DataTemplate は非常に簡単です。 各項目が 1 つのTextBlock内で 3 つのStackPanel要素として表示されるように指示しています。 各 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は次のようになります。

タスクを TextBlock 要素として表示する [マイ タスク リスト リスト ボックス] を示す [データ テンプレートサンプルの概要] ウィンドウのスクリーンショット。

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型を受け取るプロパティを持つ他のコントロールで使用できるようになりました。 上に示すように、ItemsControlなどのListBox オブジェクトの場合は、ItemTemplate プロパティです。 ContentControl オブジェクトの場合は、ContentTemplate プロパティです。

データタイププロパティ

DataTemplate クラスには、DataType クラスのTargetType プロパティによく似たStyle プロパティがあります。 そのため、上記の例でx:KeyDataTemplateを指定する代わりに、次の操作を行うことができます。

<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は自動的には適用されません。

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

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>

次のスクリーンショットは、この変更されたListBoxを含むDataTemplateを示しています。

変更された DataTemplate を含むマイ タスク リスト リスト ボックスを示す [データ テンプレートサンプルの概要] ウィンドウのスクリーンショット。

HorizontalContentAlignmentStretchListBoxに設定して、項目の幅がスペース全体を占めるようにすることができます。

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

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

[データ テンプレートサンプルの概要] ウィンドウのスクリーンショット。画面が水平方向に収まるように引き伸ばされた [マイ タスク リスト リスト ボックス] が表示されています。

DataTriggers を使用してプロパティ値を適用する

現在のプレゼンテーションでは、 Task がホーム タスクかオフィス タスクかは表示されません。 Task オブジェクトには、TaskType 型のTaskType プロパティがあります。これは値としてHomeWorkを持つ列挙型です。

次の例では、DataTriggerは、BorderBrush プロパティがborder場合、Yellow という名前の要素のTaskTypeTaskType.Homeに設定します。

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

アプリケーションは次のようになります。 ホーム タスクは黄色の枠線で表示され、オフィス タスクはアクア罫線で表示されます。

[Introduction to Data Templating Sample]\(データ テンプレートサンプルの概要\) ウィンドウのスクリーンショット。ホームタスクとオフィスタスクの境界線が色で強調表示されている [マイ タスク リスト リスト] ボックスが表示されています。

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

同じ効果を得る別の方法として、 BorderBrush プロパティを TaskType プロパティにバインドし、値コンバーターを使用して TaskType 値に基づいて色を返します。 コンバーターを使用して上記の効果を作成すると、パフォーマンスの点で若干効率的です。 さらに、独自のコンバーターを作成すると、独自のロジックを提供するため、柔軟性が向上します。 最終的に、どの手法を選択するかは、シナリオとユーザーの好みによって異なります。 コンバーターを記述する方法については、 IValueConverterを参照してください。

DataTemplate に属するもの

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

一般に、生成された各DataTemplateListBoxItemが適用されることに注意してください (実際に適用される方法と場所の詳細については、ItemTemplateページを参照してください)。 DataTemplateは、データ オブジェクトのプレゼンテーションと外観にのみ関係します。 ほとんどの場合、アイテムが選択されたときの外観や、 ListBox がアイテムをレイアウトする方法など、プレゼンテーションの他のすべての側面は、 DataTemplateの定義に属していません。 例については、「 ItemsControl のスタイル設定とテンプレート」セクションを 参照してください。

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

「DataType プロパティ」セクションでは、さまざまなデータ オブジェクトに対して異なるデータ テンプレートを定義できることを説明しました。 これは、異なる型の CompositeCollection がある場合や、異なる型の項目を持つコレクションがある場合に特に便利です。 「 DataTriggers を使用してプロパティ値を適用する 」セクションでは、同じ種類のデータ オブジェクトのコレクションがある場合は、 DataTemplate を作成し、トリガーを使用して各データ オブジェクトのプロパティ値に基づいて変更を適用できることを示しました。 ただし、トリガーを使用すると、プロパティ値を適用したり、アニメーションを開始したりできますが、データ オブジェクトの構造を柔軟に再構築することはできません。 一部のシナリオでは、同じ型でプロパティが異なるデータ オブジェクトに対して異なる DataTemplate を作成することが必要になる場合があります。

たとえば、Task オブジェクトに Priority1値がある場合は、自分のアラートとして機能するようにまったく異なる外観を指定できます。 その場合は、優先度の高いDataTemplateオブジェクトを表示するためのTaskを作成します。 リソース セクションに次の 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内の要素によって共有されます。

データ オブジェクトのDataTemplate値に基づいて使用するPriorityを選択するロジックを指定するには、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>

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

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

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

優先度 1 のタスクが赤い枠線で目立つように表示されている [マイ タスク リスト リスト ボックス] を示す [データ テンプレートサンプルの概要] ウィンドウのスクリーンショット。

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

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

ItemsControlは、DataTemplateを使用できる唯一のコントロール型ではありませんが、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型と共に使用するように設計されています。 次の例では、 ListLeagueListLeague オブジェクトの一覧です。 各 League オブジェクトには、 NameDivision オブジェクトのコレクションがあります。 各 Division には NameTeam オブジェクトのコレクションがあり、各 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を使用すると、他のリストを含むリスト データを簡単に表示できることを示しています。 この例のスクリーンショットを次に示します。

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

こちらも参照ください