かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

コレクションをバインドした時に何が起きているか

かなり前の記事になりますが以下のようなListBoxでの選択項目と、ContentControlに表示される項目の同期をとるようなプログラムを書きました。

もうすぐ、3年になるんですね上の記事を書いてから。時間ってたつの早いですねぇ。とまぁ、この記事を見てTwitterで質問が飛んできました。

因みにコードはこんな感じです。

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

Personクラスの定義はAgeとNameプロパティを持つだけのシンプルなものなので割愛します。


まぁ言われてみればキモイですね。このContentControlがBindingしているものはDataContextに入っているPersonクラスのコレクションになります。コレクション自体をバインドしてるのに、何故かListBoxで現在選択されてる要素が表示されてる。


コレクションをバインドすると、暗黙的にCollectionViewが作られたりするのだろうか?とか思ったりもしたのですが、色々試しているうちに以下のような挙動をすることがわかりました。

  • ContentTemplateを指定しないと、バインドしているコレクションのToStringの結果が表示される
  • ContentTemplateを指定すると、現在選択されている要素が表示される

これキモイですね。因みに明示的にカレントのアイテムを指定した場合はContentControlのBindingのPathに/を指定すれば問題ないです。

<ContentControl   
    Grid.Row="1"   
    Content="{Binding Path=/}"  
    ContentTemplate="{StaticResource personViewTemplate}"/>

こうすると、ContentTemplateの指定の有無に関わらずに、同じ動きをします。誰かこの挙動について記述してあるドキュメントを知らないでしょうか?と聞いてみるテスト。