2014年5月14日水曜日

wpf : Bindingソースのパスをリファクターできるようにするためのコード

xamlではなく、C#のコードでのお話です。 以前の投稿でINotifyPropertyChangedでの通知対象のプロパティ名をリファクターできるようにするためのコードを書きました。 今回はそれを元に、Bindingするときのプロパティ名をリファクターできるようにしました。

考えるキッカケになったのはこんなコード。

C#のコード上でBindingをいくつか作ることになったので書きました。 このコードのpathの部分が問題です。 pathは文字列なのでVisualStudioのリファクターの対象にはなりません。 バインディングソースが自作クラスなら、プロパティ名を変更したときに変更もれがあってバグになってしまう可能性があります。

以前のコードを応用してリファクターが効くようにするとこうなります。 (IValueConverterとかBindingModeとかはネタに関係ないので省略。)

使う側のコードは、

こうすれば、リファクターを使ってプロパティ名を変えれば変更もれでバグになることはありません(xamlを除く)。 短いコードで済むし、お勧めです。

しかし、ちょっと気になるところもあります。 引数にinstanceが2回出てくるのがスッキリしません。 「なんとかできないもんかなぁ」ってことでデバッガの表示を眺めながら適当に試行錯誤してたら、なんかできました。 こんなふうになりました。

「 () => instance.Property という形のラムダ式を解釈してinstanceの部分とProperty名を取り出そう」ってのが趣旨なんですが、instanceを格納した変数の種類がthisとかstaticとか「プロパティかフィールドか?」とかで場合分けするハメになってこの長さに。 コード上の見た目は同じようでもinstanceを格納した変数の宣言のされかたで色々違うんですね。 軽いネタのつもりだったのに疲れました。 自分の使いそうなコードは書いたけど、多分網羅はしてないんだろうな...

Expressionを使った時点で重そうな気配はしてるんですが、実際どうなんでしょうね? リファクタリングでより重くなってますし...  例えば、ListViewに数千項目のListViewItemを登録するときとか、重くて使い物にならなかったりして?

そいういうときはExpression自体使わずに普通に文字列でプロパティ名を指定するしかないんでしょうねぇ。 そういうときでもExpressionでプロパティ名を得るこのTipsは、Debug.Assertとかに仕込めば使えるっていえば使えるのかな? 例えばINotifyPropertyChangedでの例ですが、こんなふうに使えば名前の変換し忘れがあったらアサーションに失敗します。

性能テストをするときだけPROPERTY_NAME_ASSERTIONを無効にするとか、そういうやり方なら少なくともExpressionのせいで性能問題にはなりません。


オマケです。 添付プロパティのBindingを作る場合について、そのときはExpressionとかは要りません。

既存の添付プロパティの場合はこれだけでいいんですが、自作する場合は登録する部分でプロパティ名の文字列が必要になります。 そんなときはコレ。


一応、動作確認用のコードを載せておきます。 コントロールをタイマー&バインディングで動かす簡単なサンプルです。 MainWindow.xamlです。

MainWindow.xaml.csです。

MainWindow自体にValue1プロパティを作ってそれに関するラムダ式を書いた場合のチェックと、DataクラスにValue2プロパティを作ってそれに関するラムダ式を書いた場合のチェックをします。 あとは、プロパティの場合/フィールドの場合と静的メンバーの場合/インスタンスメンバーの場合のチェックがあります。

Data.csはこちら。