こんにちは、働くC#プログラマーのさんさめです。
WPFにおいてBindingは
「データとビューの疎結合化」
「コード記述の省力化」
などなど非常に重要な役割を果たします。
(後者は慣れないと恩恵を感じにくいですが)
その一方で、
何が起きているのか分かりにくいため、
一度Bindingでハマってしまうと
「まずどこから調べればいいのか分からない」
ということになりがちです。
そこで、私自身の知識の整理も兼ねて
Bindingについてまとめることにしました。
今回は第4回という事になっていますが、
やや番外編となります。
WPFにおいて、
いくつかのコントロールを組み合わせて
1つの独立したコントロールとして切り出すとき、
最初に検討すべきなのが
ユーザーコントロール(UserControl)です。
しかし、
使いまわそうと思った時に
ただベタに使うことは稀です。
大抵の場合は汎用化のために、
プロパティを公開して、
その設定値に応じて挙動を変化させたくなります。
例えば、以下のように独自のプロパティを追加して、
かつxamlで設定したい、なんならBindingもしたい…。
という要求になっていくのは至極当然のことでしょう。
<local:UserButtonWithComment ButtonText="終了"
Comment="{Binding EndComment}"/>
そんな時には、
「依存関係プロパティ(DependencyProperty)」
を使います。
依存関係プロパティという形で、
プロパティを作ることで、
そのユーザーコントロールを使う側が、
さらにBindingを行ったり、
Styleを適用させたりといったことができるようになります。
本記事では依存関係プロパティの
具体的な作り方・使い方について解説します。
依存関係プロパティは気軽に使えるものである。
そう思えるようになることが目標です。
依存関係プロパティの作り方
それでは早速、
依存関係プロパティを持った
ユーザーコントロールを作ってみましょう。
まずは、プロジェクトを右クリックし、
「追加」→「新しい項目」を選びます。
ユーザーコントロール(WPF)を選びます。
ユーザーコントロール(Windowsフォーム)
の方ではありませんよ。
名前を付けたら、
「追加」を押します。
作り終わったら、いったんxamlは無視して、
xaml.csを開きましょう。
コードビハインドが開きますね。
そしたらば、
コンストラクタから1行あけて、
「propdp」と入力してTabキーを2回押します。
すると、スニペットが起動して、
依存関係プロパティ実装に必要な記述が出現します。
この状態でTabキーを押していくと、
以下の必要な項目を順番に入力できます。
- 型
- プロパティ名
- プロパティを持っているクラス
(普通は作ったユーザーコントロール名を入力します) - 既定値(初期値)
実際に入力してみます。
今回は、string型のプロパティを作ってみます。
Tabキーで入力箇所が飛んで、
かつプロパティ名が使いまわされるところは、
同時に文字が変わっていることが確認できます。
スニペットがあるのはとてもありがたいですね。
さて、これで依存関係プロパティの宣言は完了です。
簡単ですね。
依存関係プロパティを配置コントロールにBindingする
さて、次は
作った依存関係プロパティの表示場所を
決めなくてはなりません。
以下のようなxamlを作ったとします。
作った依存関係プロパティは、
TextBlockのTextプロパティに繋ぎたいです。
<UserControl
x:Class="UserControlCreateSample.DockCheckBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UserControlCreateSample">
<DockPanel>
<TextBlock DockPanel.Dock="Left" />
<CheckBox />
</DockPanel>
</UserControl>
その場合は、Textプロパティにこのように書きます。
<TextBlock DockPanel.Dock="Left"
Text="{Binding CheckBoxComment, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:DockCheckBox}}}" />
とっても横に長いですね。
RelativeSourceでAncestorTypeに
自分自身を指定することで、
自身のプロパティとのBindingを実現しています。
(※「こんな長い記述、ちんぷんかんぷん!」
という方は、ぜひ
【WPF】読める!xamlマークアップ拡張【入れ子でも怖くない】
をご覧ください)
実際にこのユーザーコントロールを配置してみると、
ちゃんと独自プロパティの情報が、
表示されていることが分かります。
もちろん、
CheckBoxComment=”{Binding Hoge}”
みたいに書くことでViewModelとのBindingも実現できます。
xamlではなくコードビハインドでBindingを繋ぐ
さて、これで、
外部公開したプロパティと
内部配置したコントロールのプロパティを
繋ぐことができました。
しかし、前述したように
xamlに書くととにかく横に長いですね。
複数のプロパティを繋ぎたいと思ったとき、
これでは辟易してしまいます。
そこで、
もう少しだけ楽なやり方として、
コードビハインドでBindingを繋ぐ方法があります。
まず、コードビハインドから目的のコントロールに
アクセスできるようにするために、
TextBlockに名前を付けます。
そして、
コンストラクタのInitializeComponentの下で、
Bindingを設定します。
public DockCheckBox()
{
InitializeComponent();
CommentTextBlock.SetBinding(TextBlock.TextProperty,
new Binding(nameof(CheckBoxComment))
{
Source = this,
});
}
SetBindingメソッドを呼ぶことで、
任意の依存関係プロパティにBindingを設定できます。
ポイントは、
Source = this
の部分です。
xamlではRelativeSourceでしか書けなかった
自分自身の設定が、
コードビハインドならこれだけで済みます。
その分、xamlで名前を付けなくてはいけない、
という制約は生まれますが、
Bindingを繋ぐプロパティが多い場合には、
こっちの方が見やすいという考え方も
できるのではないでしょうか。
また、Bindingパスの記述に、
nameof演算子が使える点も見逃せません。
プロパティ名を変えたは良いが
うっかりxamlの記述を追従しそびれていた、
というのはWPFミスあるあるの1つです。
まとめ
まとめです。
- 汎用的なユーザーコントロールを作るなら、
外向けに公開するプロパティは
依存関係プロパティで作るべき- Bindingなどが使える
- 依存関係プロパティを
配置した内部のコントロールとつなぐ場合
xamlでRelativeSourceを使う - コードビハインドからSetBindingを使えば、
記述量やケアレスミスを防ぐことができる
最後までお読みいただき、ありがとうございました。
シリーズ記事
Binding入門はシリーズ記事となっております。
全ての記事に以下からアクセスできます
コメント
なぜ動画にしてしまったのか、めちゃくちゃ分かりにくいっす。
コメントありがとうございます。
入力してる様子を見せた方が分かりやすいかと思っていたのですが、
かえってわかりにくかったということですね
差し支えなければ、何要因でわかりにくかったのかご教授いただけますでしょうか
今後の参考にいたします
(画像が荒い、開始と終了の切れ目が分からないなどですかね…)