WPFの重要なコントロールの1つにContentControlクラスがあります。このクラスは、Contentプロパティに設定された単一の要素を表示するという機能を提供するコントロールです。「WPFのコンセプト」でも紹介しましたが、このコントロールが、要素を表示する際の詳細なロジックを以下に示します。
- ContentTemplateにDataTemplateが設定されている場合、ContentプロパティにContentTemplateを適用した結果を表示します。
- ContentTemplateSelectorにDataTemplateSelectorが設定されている場合、ContentプロパティにContentTemplateSelectorが返したDataTemplateを適用した結果を表示します。
- Contentプロパティに設定された値の型に紐づけられたDataTemplateがある場合、そのDataTemplateを適用した結果を表示します。
- ContentプロパティがUIElement型の場合、そのまま表示されます。(UIElementにすでに親がいる場合は例外が出ます)
- Contentプロパティに設定された値の型に紐づけられたTypeConverterでUIElementに変換するものがある場合は、変換した結果を表示します。
- Contentプロパティに設定された値の型に紐づけられたTypeConverterでString型に変換するものがある場合はString型に変換してTextBlockにラップして表示します。
- Contentプロパティに設定された値の型がXmlElementの場合は、InnerTextプロパティの値をTextBlockにラップして表示します。
- Contentプロパティに設定された値をToStringした結果をTextBlockにラップして表示します。
複雑なロジックですが、端的にいうと、可能な限りUIElementに変換できるか試した後に、ダメだったら文字列型にしてTextBlockに格納して表示するというロジックになります。このような処理をContentControlが行ってくれるおかげで、ContentControlを継承するButtonクラスやLabelクラスやListBoxItemクラスで、以下のような直観的なプログラミングが可能になっています。
Buttonクラスに文字列を表示する場合は以下のように文字列を設定できます。
this.button.Content = "こんにちは世界";
Buttonクラス内にButtonを表示する場合も以下のように直接Buttonを設定できます。
this.button.Content = new Button { Content = "ボタンの中のボタン" };
DataTemplate
ContentControlクラスのContentTemplateプロパティに設定可能なDataTemplateについて説明します。DataTemplateは、主にContentプロパティにオブジェクトが設定されている場合に、どのようにそのオブジェクトを表示するかを定義します。以下にListBoxクラスのItemTemplate(ListBoxItemのContentプロパティに適用されるDataTemplate)を使ったプログラム例を示します。
<Window x:Class="DataTemplateSample01.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DataTemplateSample01" Title="MainWindow" Height="350" Width="525"> <Grid> <ListBox x:Name="listBox"> <ListBox.ItemTemplate> <DataTemplate DataType="{x:Type local:Person}"> <Border BorderBrush="Red" BorderThickness="1" Padding="5"> <StackPanel Orientation="Horizontal"> <Label Content="Name" /> <TextBlock Text="{Binding Name}" VerticalAlignment="Center"/> <Label Content="Age" /> <TextBlock Text="{Binding Age}" VerticalAlignment="Center"/> </StackPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Window>
ListBoxのItemsSourceプロパティに設定するオブジェクトは以下のように定義しています。
namespace DataTemplateSample01 { public class Person { public string Name { get; set; } public int Age { get; set; } } }
ListBoxのItemsSourceプロパティに適当な値を設定して実行すると以下のようになります。
この例では、ListBoxの中にDataTemplateを定義していますが、通常はWindowやApp.xamlの中のResourcesにDataTemplateを定義します。こうすることで複数個所で同一のオブジェクトの見た目を再利用することができるようになります。DataTemplateの定義をWindowのResourcesに移動させた場合のコード例を以下に示します。
<Window x:Class="DataTemplateSample01.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DataTemplateSample01" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <DataTemplate x:Key="PersonTemplate" DataType="{x:Type local:Person}"> ...省略... </DataTemplate> </Window.Resources> <Grid> <ListBox x:Name="listBox" ItemTemplate="{StaticResource PersonTemplate}" /> </Grid> </Window>
DataTemplateをResourcesに移動して、Resourcesのオブジェクトを参照するためのStaticResourceマークアップ拡張でItemTempalteにDataTemplateを設定しています。Resourcesに定義されたDataTemplateは、x:Keyを指定せずにDataTypeだけ設定したときに、デフォルトでその型のDataTemplateとして使われるという動きをします。そのため、上記の記述はx:Keyを使わずに以下のように書くことも出来ます。
<Window x:Class="DataTemplateSample01.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DataTemplateSample01" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <DataTemplate DataType="{x:Type local:Person}"> ...省略... </DataTemplate> </Window.Resources> <Grid> <ListBox x:Name="listBox" /> </Grid> </Window>
DataTrigger
DataTemplateには、データの値に応じて表示の見た目を切り替えるロジックを書くことが出来ます。例えばPersonクラスを拡張して40歳以上の場合trueを返すプロパティを追加します。
namespace DataTemplateSample01 { public class Person { public string Name { get; set; } public int Age { get; set; } public bool IsOver40 { get { return this.Age >= 40; } } } }
このIsOver40プロパティがtrueの時は、枠線の色を青色にするというようなことがDataTriggerを使って実現できます。DataTriggerはDataTemplateのTriggersプロパティに設定できて以下のように記述します。
<DataTemplate DataType="{x:Type local:Person}"> <Border x:Name="border" BorderBrush="Red" BorderThickness="1" Padding="5"> <StackPanel Orientation="Horizontal"> <Label Content="Name" /> <TextBlock Text="{Binding Name}" VerticalAlignment="Center"/> <Label Content="Age" /> <TextBlock Text="{Binding Age}" VerticalAlignment="Center"/> </StackPanel> </Border> <DataTemplate.Triggers> <DataTrigger Binding="{Binding IsOver40}" Value="True"> <Setter TargetName="border" Property="BorderBrush" Value="Blue" /> </DataTrigger> </DataTemplate.Triggers> </DataTemplate>
DataTriggerのBindingの値とValueの値が等しいとき、DataTriggerの中に定義したSetterが実行されます。Setterはこの例では1つだけしか設定していませんが、複数指定できます。
実行するとAgeプロパティが40を超えると枠線が青色になっていることが確認できます。
DateTemplateSelector
DataTemplateSelectorは、条件に応じてDataTemplateを切り替える仕組みです。DataTemplateSelectorは、C#でDataTemplateSelectorクラスを継承して作成します。DataTemplateSelectorクラスを継承して、SelectTemplateメソッドで、状況に応じて適切なDataTemplateを返します。
以下の例では、PersonクラスのAgeプロパティが40より小さい場合はResourcesからPersonTemplate1を検索して返して、Ageプロパティが40以上の場合はResourcesからPersonTemplate2を検索して返すという処理を行っています。
using System.Windows; using System.Windows.Controls; namespace DataTemplateSample02 { public class PersonDataTemplateSelector : DataTemplateSelector { public override DataTemplate SelectTemplate(object item, DependencyObject container) { var p = (Person)item; if (p.Age < 40) { // Ageが40より小さければPersonTemplate1 return (DataTemplate)((FrameworkElement)container).FindResource("PersonTemplate1"); } else { // Ageが40以上ならPersonTemplate2 return (DataTemplate)((FrameworkElement)container).FindResource("PersonTemplate2"); } } } }
PersonTemplate1と2は、WindowのResourcesに以下のように定義しています。
<!-- NameとAgeを表示 --> <DataTemplate x:Key="PersonTemplate1" DataType="{x:Type local:Person}"> <StackPanel Orientation="Horizontal"> <Label Content="Name" /> <TextBlock Text="{Binding Name}" VerticalAlignment="Center"/> <Label Content="Age" /> <TextBlock Text="{Binding Age}" VerticalAlignment="Center"/> </StackPanel> </DataTemplate> <!-- Nameだけ表示 --> <DataTemplate x:Key="PersonTemplate2" DataType="{x:Type local:Person}"> <StackPanel Orientation="Horizontal"> <Label Content="Name" /> <TextBlock Text="{Binding Name}" VerticalAlignment="Center"/> </StackPanel> </DataTemplate>
DataTemplateSelectorは、ListBoxではItemTemplateSelectorプロパティに設定します。ButtonクラスのようなContentControlを継承しているクラスに指定する場合はContentTemplateSelectorプロパティを使用します。
<ListBox x:Name="listBox"> <ListBox.ItemTemplateSelector> <local:PersonDataTemplateSelector /> </ListBox.ItemTemplateSelector> </ListBox>
ListBoxに適当なPersonクラスのリストを設定して実行した結果は以下のようになります。適用されているテンプレートが変わっていることが確認できます。
過去記事
- WPF4.5入門 その1 「はじめに」
- WPF4.5入門 その2 「WPFとは」
- WPF4.5入門 その3 「Hello world」
- WPF4.5入門 その4 「Mainメソッドはどこにいった?」
- WPF4.5入門 その5 「全てC#でHello world」
- WPF4.5入門 その6 「WPFを構成するものを考えてみる」
- WPF4.5入門 その7 「XAMLのオブジェクト要素と名前空間」
- WPF4.5入門 その8 「オブジェクト要素のプロパティ」
- WPF4.5入門 その9 「コレクション構文」
- WPF4.5入門 その10 「コンテンツ構文」
- WPF4.5入門 その11 「マークアップ拡張」
- WPF4.5入門 その12 「その他のXAMLの機能」
- WPF4.5入門 その13 「簡単なレイアウトを行うコントロール」
- WPF4.5入門 その14 「レイアウトコントロールのCanvasとStackPanel」
- WPF4.5入門 その15 「レイアウトコントロールのDockPanelとWrapPanel」
- WPF4.5入門 その16 「ViewBoxコントロール」
- WPF4.5入門 その17 「ScrollViewerコントロール」
- WPF4.5入門 その18 「Gridコントロール part 1」
- WPF4.5入門 その19 「Gridコントロール part 2」
- WPF4.5入門 その20 「レイアウトに影響を与えるプロパティ」
- WPF4.5入門 その21 「WPFのコンセプトと重要な機能つまみ食い」
- WPF4.5入門 その22 「Buttonコントロール」
- WPF4.5入門 その23 「DataGridコントロール その1」
- WPF4.5入門 その24 「DataGridコントロール その2」
- WPF4.5入門 その25 「TreeViewコントロール その1」
- WPF4.5入門 その26 「TreeViewコントロール その2」
- WPF4.5入門 その28 「Calendarコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その29 「ContextMenuコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その30「Menuコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その31 「ToolBarコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その32 「CheckBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その33 「ComboBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その34 「ListBoxコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その35 「RadioButtonコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その36 「Sliderコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その37 「TabControl」 - かずきのBlog@hatena
- WPF4.5入門 その38 「ファイルダイアログ」 - かずきのBlog@hatena
- WPF4.5入門 その39 「情報を表示するコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その40 「Popup、ToolTip、TextBox、Image、MediaElementコントロール」 - かずきのBlog@hatena
- WPF4.5入門 その41 「DispatcherObject」 - かずきのBlog@hatena
- WPF4.5入門 その42 「WPFのプロパティシステム」 - かずきのBlog@hatena
- WPF4.5入門 その43 「読み取り専用の依存関係プロパティ」 - かずきのBlog@hatena
- 拡張されたプロパティメタデータ - かずきのBlog@hatena
- WPF4.5入門 その45 「添付プロパティ」 - かずきのBlog@hatena
- WPF4.5入門 その46 「WPFのイベントシステム」 - かずきのBlog@hatena