DataTemplate
Templateプロパティ、ItemTemplateプロパティ、HierarchicalDataTemplateプロパティに設定できるテンプレートはControlTemplateの他にDataTemplateがあります。
DataTemplateはContentControl、ItemsControlなどのコントロールに指定された特定オブジェクトのデータ型に合わせたUI表現を定義することができるTemplateになります。
DataTemplateの代表的な利用方法の一つとしてItemsControlのItemTemplateプロパティに定義するシナリオがあります。
これまでご説明したようにItemsControlは任意データのコレクションを指定することができます。DataTemplateを使うとこれらのコレクションの個々のオブジェクトに合わせて表示をカスタマイズできます。
ItemsControlでは実際にItemsSourceに適用されたコレクション内の各オブジェクトはDataTemplateを使って表示用のオブジェクトツリーを作成します。これを表示ツリーと呼びます。
表示ツリー作成されると、最終的にはテンプレート適用先のコントロールが持つDataContextプロパティにコレクションの個々のオブジェクトが設定されます。
例えばTreeViewコントロールの場合、生成されるコントロールがTreeViewItemコントロールとなるので、最終的にはこのコントロールのTemplateプロパティにDataTemplateが設定され、そしてTreeViewItemコントロールのDataContextプロパティに対して個々のオブジェクトが設定されます。
Bindingは明確なソースの指定がされていなければコントロールのDataContextプロパティを参照するため、DataTemplate内ではBindingを利用して個々のオブジェクトへのプロパティパスを定義することで、表示に反映させることが可能です。
それでは、ここまでItemsControlの説明をまとめると以下のようなイメージになります。
それでは、サンプルにTreeViewコントロールのItemTemplateにDataTemplateを設定してみましょう。
まず、データ表示を行う簡単なエンティティクラス、Personクラス定義から行いましょう。
メンバーはstring型のNameプロパティ、int型のAgeプロパティを実装しておきます。
public class Person : INotifyPropertyChanged { private string _name; public string Name { get { return _name; } set { _name = value; RaisePropertyChanged(Name); } } private int _age; public int Age { get { return _age; } set { _age = value; RaisePropertyChanged("Age"); } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }
続けて、TreeViewコントロールのItemTemplateに対してPersonクラスに合わせたDataTemplateを作成します。
<sdk:TreeView Name="treeView1"> <sdk:TreeView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}" Width="100" /> <ProgressBar Minimum="0" Maximum="150" Value="{Binding Age}" Width="400" /> </StackPanel> </DataTemplate> </sdk:TreeView.ItemTemplate> </sdk:TreeView>
そして、UserControlのLoadedイベントでサンプルデータをセットします。
private void UserControl_Loaded(object sender, RoutedEventArgs e) { treeView1.ItemsSource = new ObservableCollection<Person>() { new Person() { Name = "しるば太郎", Age = 40}, new Person() { Name = "しるば次郎", Age = 35}, new Person() { Name = "しるば子太郎", Age = 15}, new Person() { Name = "しるば親次郎", Age = 60}, new Person() { Name = "しるば孫太郎", Age = 5} }; }
実行結果は以下のとおりです。
今回利用したコントロールはTreeViewコントロールですが、データが階層構造でないことや、DataTemplateを指定したことで本来のTreeViewコントロールで表示されていた階層構造などは表現されていません。
階層構造を指定する場合は、HierarchicalDataTemplateというテンプレートを利用します。
HierarchicalDataTemplate
TreeViewコントロールで、階層構造を使った表現をカスタマイズする場合はHeaderedItemsControlに特化したDataTemplate、HierarchicalDataTemplateを使用することで実現できます。
このテンプレートをItemTemplateに定義することで階層構造の表示方法をカスタマイズできます。
HierarchicalDataTemplateはTemplate自体にさらにItemsSource, ItemsContainerStyle, ItemTemplateを指定することで、ヘッダー表示用のTemplate、そしてコレクション表示用のTemplateを指定することができます。
TreeViewコントロールでは、このItemTemplateにHierarchicalDataTemplateを指定することで、階層構造を展開できるような仕組みを提供しています。
それでは実際にHierarchicalDataTemplateを使ってTreeViewコントロールをカスタマイズしてみましょう。
まずは先ほどと同様、エンティティクラスの定義から。 先ほどのPersonクラスを少し拡張して、家族データを保持するための下記のようにFamilyプロパティを追加します。
private ObservableCollection<Person> _family = new ObservableCollection<Person>(); public ObservableCollection<Person> Family { get { return _family; } }
続けてHierarchicalDataTemplateを定義し、TreeViewコントロールのItemTemplateに設定します。
<UserControl x:Class="_03_HierarchicalDataTemplate.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" Loaded="UserControl_Loaded"> <UserControl.Resources> <sdk:HierarchicalDataTemplate x:Key="FamilyTemplate"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}" Width="100" /> <ProgressBar Minimum="0" Maximum="150" Value="{Binding Age}" Width="400" /> </StackPanel> </sdk:HierarchicalDataTemplate> <sdk:HierarchicalDataTemplate x:Key="DetailTemplate" ItemsSource="{Binding Family}" ItemTemplate="{StaticResource FamilyTemplate}"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}" Width="100" /> <ProgressBar Minimum="0" Maximum="100" Value="{Binding Age}" Width="400" /> </StackPanel> </sdk:HierarchicalDataTemplate> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <sdk:TreeView Name="treeView1" ItemTemplate="{StaticResource DetailTemplate}" /> </Grid> </UserControl>
そして、UserControlのLoadedイベントでサンプルデータをセットします。
private void UserControl_Loaded(object sender, RoutedEventArgs e) { treeView1.ItemsSource = new ObservableCollection<Person>() { new Person() { Name = "しるば太郎", Age = 40}, new Person() { Name = "しるば次郎", Age = 35, Family = { new Person() { Name = "しるば兄太郎", Age = 25}, new Person() { Name = "しるば弟太郎", Age = 20}, } }, new Person() { Name = "しるば三郎", Age = 60}, }; }
実行結果は以下のとおりです。