次の方法で共有


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

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

前提 条件

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

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

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

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

DataTemplate が重要である理由を示すために、データ バインディングの例を見てみましょう。 この例では、ListBoxTask オブジェクトのリストにバインドされています。 各 Task オブジェクトには、TaskName (文字列)、Description (文字列)、Priority (int)、および値が HomeWorkを持つ 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 は次のようになります。

各ソース オブジェクトの文字列表現 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 は次のようになります。

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

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

単純な DataTemplate の定義

解決策は、DataTemplateを定義することです。 これを行う方法の 1 つは、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 は次のようになります。

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

データ型 プロパティ

DataTemplate クラスには、Style クラスの TargetType プロパティによく似た DataType プロパティがあります。 そのため、上記の例で DataTemplatex: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 のバインドでは、コレクション全体にバインドするか、個々のオブジェクトにバインドするかを区別するために、より多くの情報が必要になるためです。 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 を示しています。

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

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

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

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

画面が水平方向に収まるように引き伸ばされた [My Task List ListBox] が表示されている [Introduction to Data Templating Sample]\(データ テンプレートサンプルの概要\) ウィンドウのスクリーンショット。

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

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

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

<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 クラス用の Style を使用してプロパティを設定する方が適している場合があります (バインドするコントロールが ListBoxの場合)。 たとえば、マウスで項目をポイントしたときに Trigger で項目の Opacity 値をアニメーション化する場合は、ListBoxItem スタイル内でトリガーを定義します。 例としては、「スタイル設定とテンプレートのサンプル の概要」を参照してください。

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

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

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

たとえば、Task オブジェクトに 1Priority 値がある場合は、自分のアラートとして機能するようにまったく異なる外観を指定できます。 その場合は、優先度の高い 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>

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

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

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

優先度 1 のタスクが赤い枠線で目立つように表示されている [My Task List ListBox] を示す [Introduction to Data Templating Sample]\(データ テンプレートサンプルの概要\) ウィンドウのスクリーンショット。

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

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

ItemsControl は、DataTemplate を使用できる唯一のコントロール型ではありませんが、ItemsControl をコレクションにバインドすることは非常に一般的なシナリオです。 「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を使用すると、他のリストを含むリスト データを簡単に表示できることを示しています。 この例のスクリーンショットを次に示します。

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

相關聯項目