HIDARI日記(右)

そのときどき興味ある技術を中心にだらだら書いてます。内容は個人の見解であり、所属する企業を代表するものではありません。

WPFのRelativeSourceのはなし

WPFバインディングソースを指定するのに使うRelativeSource.その中で使う「関係」についてよくわかってなかったので,@Posauneさんとこの【WPF】RelativeSource - 亀岡的プログラマ日記で勉強したのを整理がてら書いてみました.

Pro Wpf 4.5 in C#: Windows Presentation Foundation in .net 4.5 (Professional Apress)

Pro Wpf 4.5 in C#: Windows Presentation Foundation in .net 4.5 (Professional Apress)

「関係」は以下の4つが指定できるけど,今回はその中でもSelfFindAncestorをコードを書きながら.

  • Self
  • FindAncestor
  • PreviousData
  • TemplateParent

Self

Selfは自分自身をソースとして使いたいときに使う.そのクラス自身が持つプロパティをバインドできる.

<Grid Background="Coral">
    <StackPanel Margin="20"
                Background="LightGreen">
        <TextBlock Text="{Binding RelativeSource={RelativeSource Self}, Path=HorizontalAlignment}" 
                   HorizontalAlignment="Center" 
                   Background="Bisque" 
                   Margin="10"
                   Padding="15"/>
    </StackPanel>
</Grid>

上記の場合,Textプロパティには自身(つまりTextBlock)のHorizontalAlignmentプロパティをバインドしているため,その値である「Center」が表示される.

f:id:hidari-yori:20140330114459j:plain

他に,例えば,このようにSelfを指定した状態で Path=Padding にするとこんな感じになります.

f:id:hidari-yori:20140330114525j:plain

FindAncestor

FindAncestorは要素をさかのぼって先祖要素をソースとして指定することができる. このFindAncestorには以下の2つのオプション?を指定することができる.

  • AncestorType
    • 参照したい要素の型を指定する.
  • AncestorLevel
    • 先祖要素を遡ったとき,何番目に見つかった要素を使うかを指定する.

オプションとは書いたけど,AncestorTypeは必ず指定する必要がある模様. 一方で,FindAncestorそれ自体は省略可能だったりする.そのせいか,FindAncestorってインテリセンスで出てこないんですよね…まあVS2013使ってると,省略した時に警告を示す下線が出てくることがあって鬱陶しいから書いたらいいんじゃないでしょうか.

<Grid>
    <StackPanel Background="Coral">
        <ContentControl Margin="10" 
                        Background="DarkOrchid">
            <Border BorderBrush="Aquamarine" 
                    BorderThickness="5" 
                    Background="Indigo" 
                    Margin="10">
                <StackPanel Margin="15">
                    <TextBlock HorizontalAlignment="Center"
                               Background="PaleGreen"
                               Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}, Path=Margin}"/>
                </StackPanel>
            </Border>
        </ContentControl>
    </StackPanel>
</Grid>

上記の例のようにAncestorLevelを指定しなかった場合には,デフォルトでAncestorLevel = 1が適用される.

f:id:hidari-yori:20140330115258j:plain

で,以下のコードではAncestorLevelを明示的に2を示した場合を示している.

<Grid>
    <StackPanel Background="Coral" Margin="1">
        <ContentControl Margin="10" 
                        Background="DarkOrchid">
            <Border BorderBrush="Aquamarine" 
                    BorderThickness="5" 
                    Background="Indigo" 
                    Margin="10">
                <StackPanel Margin="15">
                    <TextBlock HorizontalAlignment="Center"
                               Background="PaleGreen"
                               Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}, AncestorLevel=2}, Path=Margin}"/>
                </StackPanel>
            </Border>
        </ContentControl>
    </StackPanel>
</Grid>

TextBlockには先祖要素を遡って行き,2番目に現れたStackPanelのMarginがバインドされていることが確認できる.

f:id:hidari-yori:20140330115407j:plain

おわりに

細かい仕様なんかは未だに理解できたとは言えないけど,ある程度の動きはわかってきたように思います.残りの「関係」についてはまたそのうち……汗

あ,何か間違いありましたらご一報頂けると幸いです.