はじめに
こんにちは、入社1年目の吉原です。最近久々に本格的にバスケットボールを再開し、先日公式戦にも出場し、充実した毎日を過ごしています。
一方業務でもWPFのスキルレベル向上のために日々励んでおります。そこで今回はWPFを学習していてここは記事にして理解を促進した方が良いと思ったRelativeSourceについてまとめました。
RelativeSourceとは
データバインディングにおいてバインディングソースを相対的な位置で指定するために使用されるクラスです。簡単にいうと、バインディングソースを自分自身や近くにモノから探して適用するものです。コントロールがどこに配置されても、相対的な関係を使えば特定の親や自分自身をデータソースに指定できるため、柔軟で一貫性を持ったコードを作成できます。
以下にRelativeSourceで用意されている使い方を示します。
RelativeSourceのモード
- Self:バインディング対象の要素自身をソースに指定
- TemplateParent:テンプレートの親コントロールをソースに指定
- FindAncestor:指定した型またはレベルの親要素をソースに指定
- AncestorType:特定の型の親要素を指定するためのプロパティ
- AncestorLevel:指定した型の親要素の階層レベルを指定するためのプロパティ
今回は、この中で、私が理解に苦戦したTemplateParentについてさらに詳しく説明します。
TemplateParentの実装例
<Window.Resources>
<!--ボタンのテンプレート-->
<ControlTemplate x:Key="CustomButtonTemplate" TargetType="Button">
<Grid>
<Rectangle Fill="{TemplateBinding Background}" RadiusX="5" RadiusY="5" />
<!--ボタンに表示するTextをTemplateParentを用いて定義-->
<TextBlock Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="White" />
</Grid>
</ControlTemplate>
</Window.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Content="Click Me!" Width="100" Height="50"
Background="Blue" Template="{StaticResource CustomButtonTemplate}" />
</StackPanel>
上記コードのようにボタンのControlTemplate内で
Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}
のように書くことでTextBlockのTextプロパティがテンプレートの親(Buttonコントロール)のContentプロパティにバインドされます。ButtonのContentプロパティには"Click Me!"が設定されているため、これがTextBlockに表示されます。
このように仕組み自体は簡単です。しかしここでふと私が思ったのは、ほぼ同じことがTemplateBindingでもできるではないかと思い、混乱しました。
そのため、次はTemplateBindingとの違いを見てみます。
TemplateBindingとの比較
まず、TemplateBindingとは、テンプレート内のコントロールに関連するプロパティを参照するための簡単な方法です。「テンプレート内の要素から親コントロール(テンプレートを適用したコントロール)のプロパティを参照する」という点ではRelativeSourceのTemplateParentに似ています。
結論から言うと、TemplateBindingは、依存関係プロパティに特化しているのに対してTemplateParentは、依存関係プロパティに限定することなく、より複雑なプロパティにも適用出来ます。
具体的に複雑なプロパティとは、プロパティがUIによって動的に変化する場合やコンバーターを使って計算した結果を反映させたプロパティ等です。それらを、ControlTemplateによって定義したい場合は、TemplateParentを使うことになります。
ただし、なんでもTemplateParentを使えば良いわけではなく、TemplateBindingで事足りる場合はTemplate Bindingを使用した方が内部的な効率的に実装することができパフォーマンスの向上につながります。
終わりに
今回はRelativeSourceの理解から、その中のモードであるTemplateParentに着目し、それに似たTemplateBindingとの違いを比較しました。
今回の記事作成を通してRelativeSourceの理解がより一層深まりました。今後も学んだことをどんどんアウトプットしていき理解をより確かなものにしていけるように励んでいきたいと思います。