Q092. Binding.RelativeSource の使い方がよくわからない

A.Binding.RelativeSource の使い方がいまいちよくわからなかったので、サンプルを使ってまとめてみました。



MSDN の説明によると

バインディング ターゲットの位置に対して相対的な位置を指定することにより、バインディング ソースを取得または設定します。

とあるとおり、相対的な位置関係にある要素を参照しプロパティをバインドします。例えば {RelativeSource Self} は自身を参照し、{RelativeSource FindAncestor} では自身の先祖・・・いわゆる上位の要素を参照することになります。


で、理解を助けるため、サンプルを書いてみました。


<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="305" Width="300"
    Background="Red">
  <Grid Background="Blue" >
    <Grid Margin="10" Background="Aqua" >
      <Border CornerRadius="30" Background="LightGray" Margin="10">
        <StackPanel Margin="10" Background="Green">
          <TextBox Margin="8" BorderBrush="Red" 
               Text="{Binding BorderBrush, 
                RelativeSource={RelativeSource Self}}"
               Foreground="{Binding Path=BorderBrush, 
                RelativeSource={RelativeSource Self}}" />
          <TextBox Margin="8"  
               Text="{Binding Background, 
                RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}"
               Foreground="{Binding Path=Background, 
                RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}" />
          <TextBox Margin="8"  
               Text="{Binding Background, 
                RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}}}"
               Foreground="{Binding Path=Background, 
                RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}}}" />
          <TextBox Margin="8"  
               Text="{Binding Background, 
                RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Grid}}}"
               Foreground="{Binding Path=Background, 
                RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Grid}}}" />
          <TextBox Margin="8"  
               Text="{Binding Background, 
                RelativeSource={RelativeSource FindAncestor, AncestorLevel=2, AncestorType={x:Type Grid}}}"
               Foreground="{Binding Path=Background, 
                RelativeSource={RelativeSource FindAncestor, AncestorLevel=2, AncestorType={x:Type Grid}}}" />
        </StackPanel>
      </Border>
    </Grid>
  </Grid>
</Window>


このサンプルの要素ツリー、図にすると以下のようになります。


1番目のTextBox では、{RelativeSource Self} を使って自身を参照、Text プロパティとForegroundプロパティを自身の BorderBrush プロパティ とバインドさせてます。

<TextBox Margin="8" BorderBrush="Red" 
     Text="{Binding BorderBrush, RelativeSource={RelativeSource Self}}"
     Foreground="{Binding Path=BorderBrush, RelativeSource={RelativeSource Self}}" />


2番目のTextBox では、{RelativeSource FindAncestor} を使い、上位要素(先祖要素)の型 StackPanel を検索し、Text プロパティとForegroundプロパティを StackPanel の Background プロパティ とバインドさせてます。

<TextBox Margin="8"  
     Text="{Binding Background, 
         RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}"
     Foreground="{Binding Path=Background, 
         RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}" />


3番目のTextBox も同じく {RelativeSource FindAncestor} を使い、上位要素(先祖要素)の型 Border を参照し、Text プロパティとForegroundプロパティを Border の Background プロパティ とバインドさせてます。

<TextBox Margin="8"  
     Text="{Binding Background, 
         RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}}}"
     Foreground="{Binding Path=Background, 
         RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}}}" />


4番目のTextBox も {RelativeSource FindAncestor} を使い、上位要素(先祖要素)の型 Grid を参照し、Text プロパティとForegroundプロパティを Grid の Background プロパティとバインドさせてます。ただし上位の要素には Grid が二つ存在します。そこで、AncestorLevel に 1 を設定し、自身を基点にして1番目に見つかった Grid を指定します。

<TextBox Margin="8"  
     Text="{Binding Background, 
         RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Grid}}}"
     Foreground="{Binding Path=Background, 
         RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type Grid}}}" />


5番目のTextBox も {RelativeSource FindAncestor} を使い、上位要素(先祖要素)の型 Grid を参照し、Text プロパティとForegroundプロパティを Grid の Background プロパティとバインドさせてます。ただし上位要素には Grid が二つ存在するので、AncestorLevel に 2 を設定し、自身を基点にして2番目に見つかった Grid を指定します。

<TextBox Margin="8"  
     Text="{Binding Background, 
         RelativeSource={RelativeSource FindAncestor, AncestorLevel=2, AncestorType={x:Type Grid}}}"
     Foreground="{Binding Path=Background, 
         RelativeSource={RelativeSource FindAncestor, AncestorLevel=2, AncestorType={x:Type Grid}}}" />


参考記事RelativeSource のマークアップ拡張機能
参考記事第5回 WPFの「データ・バインディング」を理解する
参考記事BindingのRelativeSourceの設定色々


WPF FAQ の目次に戻る