前回:http://blogs.wankuma.com/kazuki/archive/2008/02/03/120669.aspx
前回は、簡単なDataTemplateを定義して画面に人の情報を表示させた。
これをちょこっといじってListBoxにデータを表示するようにしてみようと思う。
そのために、今回はDataContextというプロパティに手を出してみようと思う。
Bindingをするときに明示的にSourceを指定しなかったらDataContextが使われるようになるみたい。
ということで、Windowをちょちょいと手を入れてListBoxとContentControlの2つにしてみた。
<Window x:Class="WpfTemplate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfTemplate="clr-namespace:WpfTemplate"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="personViewTemplate" DataType="{x:Type WpfTemplate:Person}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="さん " />
<TextBlock Text="{Binding Age}"/>
<TextBlock Text="歳" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox
Grid.Row="0"
ItemsSource="{Binding}"/>
<ContentControl
Grid.Row="1"
Content="まだ無い"/>
</Grid>
</Window>
ListBoxの方は、ItemsSourceにDataContextに設定されているものをバインドするようにしてみた。
コードの方からWindowのDataContextにデータを渡してみようと思う。
using System.Linq;
using System.Windows;
namespace WpfTemplate
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
var people = from age in Enumerable.Range(10, 10)
select new Person { Name = "太郎" + age, Age = age };
this.DataContext = people;
}
}
}
いろんな年齢の太郎さんのダミーデータをこさえてDataContextにセットしている。LINQに慣れて無い人には見づらいかもしれないけど、それ以外は普通のコードです。
これを実行すると下のような感じになる。
ListBoxにデータが表示されてる。前回ContentControlに最初にPersonオブジェクトを表示させたのと同じようにToStringした結果が表示されてるのがわかる。
ということで同じようにテンプレートを当ててみようと思う。
ListBoxのItemTemplateプロパティが要素に適用するプロパティということなので、さくっと適用してみる。
<Window x:Class="WpfTemplate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfTemplate="clr-namespace:WpfTemplate"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="personViewTemplate" DataType="{x:Type WpfTemplate:Person}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="さん " />
<TextBlock Text="{Binding Age}"/>
<TextBlock Text="歳" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox
Grid.Row="0"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource personViewTemplate}"/>
<ContentControl
Grid.Row="1"
Content="まだ無い"/>
</Grid>
</Window>
24行目が、てんぷれーとを当ててるところになる。実行すると下のようになる。ちゃんとテンプレートがあたってるのがわかると思う。
最後に、ウィンドウの下のほうにあるContentControlにListBoxで選択されたものを表示するようにしてみようと思います。
とりあえず、何も考えずにContentにバインドしてみると…
<Window x:Class="WpfTemplate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfTemplate="clr-namespace:WpfTemplate"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="personViewTemplate" DataType="{x:Type WpfTemplate:Person}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="さん " />
<TextBlock Text="{Binding Age}"/>
<TextBlock Text="歳" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox
Grid.Row="0"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource personViewTemplate}"/>
<ContentControl
Grid.Row="1"
Content="{Binding}"/>
</Grid>
</Window>
狙った通りにはならない。まぁコレクションをバインドしてるから当然といえば当然かぁ。
ただ、どういう仕組みか理解はしてないけど、BindingにPathを明示的に与えるとちゃんと狙った値が表示される。
<ContentControl
Grid.Row="1"
Content="{Binding Name}"/>
BindingにNameを指定してる。実行すると名前が表示されるようになる。
でも、まだ期待した感じじゃない。太郎16さんを選択してるから、太郎16が表示されるのを期待してる。
これは、ListBoxのIsSynchronizedWithCurrentItemプロパティをTrueにすることで解決する。
<ListBox
Grid.Row="0"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource personViewTemplate}"
IsSynchronizedWithCurrentItem="True"/>
実行すると、ちゃんと連動するようになる。
最後に、ContentControlにもテンプレートをあててみようと思う。
これで完成!
<ContentControl
Grid.Row="1"
Content="{Binding}"
ContentTemplate="{StaticResource personViewTemplate}"/>
実行するとこんな感じ。
XAMLとコードの全体は下の通りです。
<Window x:Class="WpfTemplate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfTemplate="clr-namespace:WpfTemplate"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="personViewTemplate" DataType="{x:Type WpfTemplate:Person}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="さん " />
<TextBlock Text="{Binding Age}"/>
<TextBlock Text="歳" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox
Grid.Row="0"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource personViewTemplate}"
IsSynchronizedWithCurrentItem="True"/>
<ContentControl
Grid.Row="1"
Content="{Binding}"
ContentTemplate="{StaticResource personViewTemplate}"/>
</Grid>
</Window>
using System.Linq;
using System.Windows;
namespace WpfTemplate
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
var people = from age in Enumerable.Range(10, 10)
select new Person { Name = "太郎" + age, Age = age };
this.DataContext = people;
}
}
}