【WPF】読める!xamlマークアップ拡張【入れ子でも怖くない】

C#

こんにちは、働くC#プログラマーのさんさめです。

WPFでxamlを扱うとき
たびたびでてくる「{Binding Hoge}」といった記述。

これは、マークアップ拡張と呼ばれるものです。

こう書くとBindingできるって言われたから書いてるけど…
これって結局どういう意味なの?

この記事は、そんな方に向けて、
マークアップ拡張の読み方を、
Bindingを題材に解説します。

なぜBindingを題材に扱うかというと、
マークアップ拡張が入れ子構造になりやすいため、
仕様を知らないと非常に読みづらいからです。
(少なくとも昔のさんさめは、
割とおまじないチックに見様見真似で書いていました)

↓こういうのとか…

<Button Command="{Binding DataContext.RemoveCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}"
        Content="この行を削除"/>

この記事を読み終えるころには、
(少なくともBindingの)
マークアップ拡張が読めるようになり、
知らないマークアップ拡張に遭遇した時も、
意味を類推できますし、
適切に調べることができるようになります。

スポンサーリンク

マークアップ拡張を読むときに抑えたい基本

マークアップ拡張とは、
非常にざっくり言ってしまうと
クラスインスタンスの生成をするためのものです。

つまり、

<TextBlock Text="{Binding Hoge}"/>

と書いたならば、
xamlが読み込まれた瞬間に
裏ではBindingクラスのインスタンスが生成されています。

これはつまり、

var binding = new Binding("Hoge");

と書いているのとほぼ同義です。

また、

<TextBlock Text="{Binding Hoge, Mode=OneWay}"/>

ならば、

var binding = new Binding("Hoge")
{
    Mode = BindingMode.OneWay,
};

です。

ちなみに、{Binding Hoge}のところは
以下のように書くこともできます。

<TextBlock Text="{Binding Path=Hoge, Mode=OneWay}"/>

この場合は以下のような記述と同じとみなせるでしょう。
(厳密にはPathはstringではないので、これはビルドエラーですが)

var binding = new Binding()
{
    Path = "Hoge",
    Mode = BindingMode.OneWay,
};

とにかく、プロパティに値を代入しているだけです。
そして、特定のプロパティだけは、
プロパティ名の指定を省略できると。
それだけのことなのですね。

さんさめ
さんさめ

あれ、思ったより簡単?

実は、そうなんです。
マークアップ拡張は以下のような
書式で行うインスタンス生成と考えると、
非常に明快に頭に入ってきます。

  • 「{」の直後がクラス名
  • 「プロパティ名=値」をカンマ区切りで書くと、
    プロパティに値をセットできる
  • モノによってはプロパティ名を省略していきなり値を書ける。
    省略可能なプロパティはクラスによって異なる
    • BindingならPath
    • StaticResourceならResourceKey

読み方の基本がお分かりいただけたでしょうか。

マークアップ拡張が入れ子になっても考え方は同じ

冒頭で出したサンプルのように、
マークアップ拡張が入れ子になっていても基本の考え方は一緒です。

<Button Command="{Binding DataContext.RemoveCommand, 
            RelativeSource={RelativeSource FindAncestor, 
                AncestorType={x:Type ListBox}}}"/>

1つ1つ分解してC#コードにしていくと、
次のようになります。

var binding = new Binding("DataContext.RemoveCommand")
{
    RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor)
    {
        AncestorType = typeof(ListBox),
    }
};

対応付けを分かりやすくしてみました。
(※本来マークアップ拡張の中で改行はできないので注意)

こうしてみると、
実はそんなに怖くないことが分かります。

「{」が重なる上に、
途中で改行が挟めない関係上
1文が横に伸びやすいことが、
とっつきにくさの原因なのではないでしょうか。

まとめ

まとめです。

  • マークアップ拡張はとっつきにくい
  • xamlにおけるインスタンスの
    特殊な生成法と考えると分かりやすい
  • 入れ子になっても考え方は同じ
  • 横に長いと読む気が失せるので、
    改行を適宜挟むと読みやすくなる
    (ただしビルドは通らなくなるので理解したら戻す必要あり)

最後までお読みいただきありがとうございました。

コメント

タイトルとURLをコピーしました