none
ContentTemplateを指定したときと指定していないときでBindingの挙動が変わる RRS feed

  • 質問

  • コレクションをIsSynchronizedWithCurrentItemをTrueに設定したListBoxにバインドした状態で、同じコレクションをContentControlのContentにバインドしたときの挙動に以下のような違いがあります。

    • ContentTemplateを指定していないとき
      コレクションのToStringをした結果が表示される
    • ContentTemplateを指定したとき
      ListBoxで現在選択されている項目が表示される
    この挙動を再現するプログラムは以下のようになります。
    <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; 
        } 
      } 
      public class Person
      {
        public string Name { get; set; }
        public int Age { get; set; }
      }
    }
    
    上記のコードでContentControlからContentTemplateを設定してるときの挙動とContentTemplateを指定していないときの挙動の違いです。ContentControlのBindingのPathに/を指定すると、挙動の差異はなくなるのでContentTemplateを指定した場合は暗黙的にPathに/が設定されるような動きになっています。
    挙動が違うことは理解したのですが、この動きが仕様として明示してある箇所をMSDN内で見つけることができませんでした。どなたか、この挙動に関する情報を持っているかたがいたら教えてください。


    かずき Blog:http://d.hatena.ne.jp/okazuki/
    2011年1月28日 14:27

すべての返信

  • http://msdn.microsoft.com/ja-jp/library/system.windows.data.binding.path(v=VS.90).aspx

    解説
    ソースがコレクション ビューであるときは、現在の項目をスラッシュ (/) で指定できます。たとえば、Path=/ 句は、ビュー内の現在の項目へのバインディングを設定します。ソースがコレクションである場合、この構文は、既定のコレクション ビューの現在の項目を指定します。

    この説明でどうでしょうか?


    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2010/12
    2011年1月30日 7:42
  • 確認ありがとうございます。その説明だと/を省略したときのプロパティの探索順序が不明瞭な気がします。
    実際にはコレクションのプロパティを探索したあとに、規定のコレクションビューの現在の項目のプロパティを探している動きをしています。

    この部分が、どこにも記載されてないように感じてます。

    しかも、ここらへんが曖昧だと結構初学者にとって混乱しそうなポイントになってしまうのかなぁと思います。


    かずき Blog:http://d.hatena.ne.jp/okazuki/
    2011年1月31日 6:06
  • {Binding} の場合は {Binding .} とおなじです。

    http://msdn.microsoft.com/ja-jp/library/system.windows.data.binding.path(v=VS.90).aspx
    解説

    また、ピリオド (.) パスを使用して現在のソースにバインドすることもできます。たとえば、Text=”{Binding}” は Text=”{Binding Path=.}” と同じ意味です。

    このため IEnumerable<Person> に対するコレクションビューをさします。

    ContentTemplate="{StaticResource personViewTemplate}"/> 

    この分ではDataTemplateを示します。なのでコレクションビューの中身のPersonがDataTypeと一致するとわかるのだと思います。


    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2010/12
    2011年1月31日 15:05
  • 色々探してみた結果 "/"をPathの最初で省略したときの挙動が明示されていないというのが問題だと認識しました。
    経験則としては、コレクションビューのプロパティを探索したあとに、コレクションビューのCurrentItemのプロパティを探索するという動きになるのですが、このことがMSDNライブラリ中に書いてないと思います。
    かずき Blog:http://d.hatena.ne.jp/okazuki/
    2011年2月7日 2:41
  • http://msdn.microsoft.com/ja-jp/library/aa970558.aspx


    えムナウ@わんくま同盟 Microsoft MVP Visual Studio C# Since 2005/01-2010/12
    2011年2月7日 16:31