none
Winformsで利用できる依存プロパティ(双方向データバインド)の実装方法 RRS feed

  • 質問

  • 環境

    VisualStudio2010 Express

    プロジェクト:C# Winforms

     

    画面とコントロールを分離させる設計をしたいとき、MVCやMVVCみたいなデザインテンプレートを実装することがあるかと思います。

    私は上記の環境でMVCを利用したプログラムを実装し始めました。

     

    しかし、C#のBindingクラスのバインドはさほど強力ではないため、期待するような双方向データバインドになりませんでした。

    参考1の例がそれです。双方向データバインドと銘を打っていましたが、残念ながらこれは単方向の緩いデータバインドに思えます。(動き1より)

    自分が想像するデータバインドと比べて、これはあまり便利なバインドではありません。バインドとして期待するのは、動き2のようなものです。

     

    動き1(公開されたプログラムを動かしてみました)

    1.MODELクラスのプロパティ値の1つがVIEWの(例えばテキストボックス)に表示される。

    2.VIEW(UIのみ)をユーザーが操作するとMODELのプロパティ値が更新される。

    問題

    1.MODELのプロパティに値をセットしても、UIの値は更新されない。(初期値は渡されるが、更新チェックがないので双方向になっていない)

    2.コード上でtextBox1.text = "a"としても、MODELのプロパティ値は更新されない。(一方向バインドでもUIのみの制約がある)

     

    動き2

    1.VIEWのテキストボックスのTEXTプロパティ値を更新すると、対応したMODELクラスのプロパティ値が更新される。

    2.MODELクラスのプロパティ値を更新すると、対応したVIEWのテキストボックスなどの値が更新される。

     

    これの解決策として、WPFのプロジェクトではDependencyPropertyなど、よい解決があるのがわかりましたが、

    WinformsのプロジェクトではDependencyPropertyがないように思います。この場合はINotifyPropertyChangedイベントを実装して解決するようになるのでしょうか?

     

    この問題をどのようにして考えるとよいのか解決したらいいのか、いろいろ意見がほしいです。

    よろしくお願いします。

     

    参考1

    とあるコンサルタントのつぶやき

    http://blogs.msdn.com/b/nakama/archive/2009/02/26/9446324.aspx

     

     


    2011年6月6日 1:54

回答

  • INotifyPropertyChangedは名前が示す通り、プロパティに変更があったことを通知することを目的としています。逆に言えばプロパティ経由でバインドしている場合に使用することになります。ちなみにプロパティ経由でバインドしている場合、「プロパティ名+Changed」というイベントを用意し、プロパティに変更があった際にそのイベントを発動させてもプロパティに変更があったことを通知することができますが、INotifyPropertyChangedの方が推薦されています。
    先に上げたRefreshメソッドなどはバインドを管理しているオブジェクト(CurrencyManagerなど)に働きかけ、データソースに変更があったことを知らせています。これによりバインドを管理しているオブジェクトはデータソースを読み直し、バインド先に通知しています。また、これらのメソッドはバインディング全体を表示し直すものであり、INotifyPropertyChangedのように個々のプロパティのみの変更を通知するものではありません。

    Windowsフォームの場合、自動的にCurrencyManagerやPropertyManagerが生成されますから、それが使えるのであればわざわざINotifyPropertyChangedを用意する必要はありません。しかし、INotifyPropertyChangedを用意すれば、データソースのプロパティを変更するだけでバインド先の表示が更新されるという利点があります。この辺りはどのようなポリシーで構築するかでしょう。
    少しWPFの話になりますが、INotifyPropertyChangedが依存関係プロパティよりも好んで使われるのは、MVVMというデザインパターンに基づいて実装されることが多いからです。依存関係プロパティは元々、変更があったことを相手に自動的に伝えたいという目的で用意されました。つまり、UIに適した実装なのです。しかし、MVVMのデザインパターンの一つの構成要素であるModelはUIに最適化されたものではありません。よって、依存関係プロパティではなく通常のプロパティが使用されています。

    デザインパターンなども絡んで全体を誤解無く説明するのは難しいのですが、端的にまとめれば以下のようなことでしょうか。

    プロパティに変更があったことをプロパティを変更するだけでそのプロパティのバインド先に通知したい。そのために.NETで標準で用意されているのがINotifyPropertyChangedであり、それを使うことになる。
    (WPFではこの目的のために依存関係プロパティが用意されたが、Windowsフォームには無い。WPFでもデザインパターンによってはINotifyPropertyChangedを使う。)

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク めめ 2011年6月8日 12:33
    2011年6月7日 1:50
    モデレータ

すべての返信

  • WPFでも必ずしもDependencyPropertyで実装するわけではありませんので、INotifyPropertyChangedインターフェースは一般的に使用されています。WindformsでもINotifyPropertyChangedインターフェースを使用されれば良いと思います。以下などが参考になると思います。

    データバインディングのおべんきょ。その7。
    http://blogs.wankuma.com/torikobito/archive/2007/05/30/78833.aspx

    Use INotifyPropertyChanged with BindingList
    http://www.gavaghan.org/blog/2007/07/17/use-inotifypropertychanged-with-bindinglist/

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年6月6日 2:34
    モデレータ
  • 返信ありがとうございます。

    やはり、INotifyPropertyChangedインターフェースでの解決ですか。

     

    自分が気がかりになっているのは、以下のMSDNの回答と、記述するコードがちょっと多めかな、という点の2つです。

     

    パフォーマンスの最適化

    http://msdn.microsoft.com/ja-jp/library/bb613546.aspx

     

    この資料はWPFのものですが、リフレクションが発生してしまうINotifyPropertyChangedよりも DependencyPropertyを利用した方が最適としているようです。

    これがあるので、INotifyPropertyChangedの解決策はベストだと判断できないでいます。


    また、コード量が多いというのは、あくまでDependencyPropertyや他言語と比べた場合で、とても多いとは思ってはいませんが、少し不便に思っています。

     

    Winformsの解決方法では、INotifyPropertyChangedが最善の答えなのでしょうか?

     


    2011年6月6日 3:06
  • ベストな方法は第三者には不明なので、各自で決めるべきかと思います。

    たとえば、リフレクションとパフォーマンスの関係を気にされているようですが、入力画面の更新通知にパフォーマンスが求められることは少ないので、パフォーマンスを落としてでも簡潔な不具合のでにくいコーディングができることはベターな選択肢になりえます。(ただし、データバインディング全体にリフレクションが絡まないように注意しないと、初期化等のパフォーマンスの影響がでる部分まで影響することは多いです)

    どのような要件があって、なにを重視されているか、いくつかの選択肢からベストを探すのは第三者には難しいので、他人にベストやベターの判断をしてもらうような言い回しはやめたほうが多くの意見がもらいやすいでしょう。

    2011年6月6日 4:00
  • この資料はWPFのものですが、リフレクションが発生してしまうINotifyPropertyChangedよりも DependencyPropertyを利用した方が最適としているようです。

    これがあるので、INotifyPropertyChangedの解決策はベストだと判断できないでいます。


    それでもWPFではINotifyPropertyChangedが多く使われています。K.Takaokaさんも言われていますが、ベストの基準はそれぞれだからです。したがって、INotifyPropertyChangedが最善かと問われれば、最善になることもあり得るという答え方しかできないのが現実だと思います。
    INotifyPropertyChanged以外ではCurrencyManagerのRefreshメソッド、PropertyManagerのResumeBindingメソッド、BindingSourceのResetBindingsメソッドなどの方法があります。何をどういうデザインパターンでバインディングするか、エラーチェックやエラー表示はどうするかなどを総合的に検討されてみてはいかがでしょうか? パフォーマンスは良いに越したことはありませんが、必要以上のパフォーマンスを求めることが最優先ではないと思います。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年6月6日 6:25
    モデレータ
  • 返信ありがとうございます。

     

    >他人にベストやベターの判断をしてもらうような言い回しはやめたほうが多くの意見がもらいやすいでしょう。

    なるほど。もともといろいろな意見が聞きたいというものだったので、微妙にズレていました。

     

    >リフレクションとパフォーマンスの関係を気にされている

    MSDNは、パフォーマンスの場合に最適なのはどちらである言っていたので、WinFormsで開発される方も同じようになにかの最適な実装方法のノウハウがあるのかと思いました。

    自分が考えていたよりも、簡潔な双方向データバインドの実装方法がサポートされていないのがわかってきて少し混乱しています。

    (自分の参考の1にしても、うっかり単純な初期化が抜けているだけなのかとも思っていました)

     

    2011年6月6日 8:41
  • 返信ありがとうございます。

    なるほど、いろいろなメソッドが提供されているのですね。自分でうまくラップしてあげないとうまく動作しないという結果にちょっと驚いています。

    ベストな基準がそれぞれというのはちょっと困った結果ですが、いろいろ試してみようと思います。

     

    >INotifyPropertyChangedが最善かと問われれば、最善になることもあり得るという答え方しかできないのが現実

    ここがうまく理解できないです。例えば、どういった場合でその他で挙げて頂いたメソッドと使い分けを考えるのでしょうか?

    どんな時に何を正とするために、結果としてINotifyPropertyChangedが多く選ばれるのでしょうか?

     

    2011年6月6日 8:50
  • INotifyPropertyChangedは名前が示す通り、プロパティに変更があったことを通知することを目的としています。逆に言えばプロパティ経由でバインドしている場合に使用することになります。ちなみにプロパティ経由でバインドしている場合、「プロパティ名+Changed」というイベントを用意し、プロパティに変更があった際にそのイベントを発動させてもプロパティに変更があったことを通知することができますが、INotifyPropertyChangedの方が推薦されています。
    先に上げたRefreshメソッドなどはバインドを管理しているオブジェクト(CurrencyManagerなど)に働きかけ、データソースに変更があったことを知らせています。これによりバインドを管理しているオブジェクトはデータソースを読み直し、バインド先に通知しています。また、これらのメソッドはバインディング全体を表示し直すものであり、INotifyPropertyChangedのように個々のプロパティのみの変更を通知するものではありません。

    Windowsフォームの場合、自動的にCurrencyManagerやPropertyManagerが生成されますから、それが使えるのであればわざわざINotifyPropertyChangedを用意する必要はありません。しかし、INotifyPropertyChangedを用意すれば、データソースのプロパティを変更するだけでバインド先の表示が更新されるという利点があります。この辺りはどのようなポリシーで構築するかでしょう。
    少しWPFの話になりますが、INotifyPropertyChangedが依存関係プロパティよりも好んで使われるのは、MVVMというデザインパターンに基づいて実装されることが多いからです。依存関係プロパティは元々、変更があったことを相手に自動的に伝えたいという目的で用意されました。つまり、UIに適した実装なのです。しかし、MVVMのデザインパターンの一つの構成要素であるModelはUIに最適化されたものではありません。よって、依存関係プロパティではなく通常のプロパティが使用されています。

    デザインパターンなども絡んで全体を誤解無く説明するのは難しいのですが、端的にまとめれば以下のようなことでしょうか。

    プロパティに変更があったことをプロパティを変更するだけでそのプロパティのバインド先に通知したい。そのために.NETで標準で用意されているのがINotifyPropertyChangedであり、それを使うことになる。
    (WPFではこの目的のために依存関係プロパティが用意されたが、Windowsフォームには無い。WPFでもデザインパターンによってはINotifyPropertyChangedを使う。)

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク めめ 2011年6月8日 12:33
    2011年6月7日 1:50
    モデレータ
  • > 簡潔な双方向データバインドの実装方法がサポートされていないのがわかってきて

    勘違いされていそうなのですが、双方向のデータバインドそのものは非常に簡潔な仕組みですよ。

    • データソースから UI へのデータを反映する方法
    • UI からデータソースへデータを反映する方法
    • データソースから UI へデータの変更を通知する方法
    • UI からデータソースへデータの変更を通知する方法

    といった要素ぐらいです。これは WinForms でも WPF でも同じです。(Binding クラスのサポートする範囲やその周辺クラスに要求する範囲が異なる程度です) データのやりとりはおいといて、WinForms では変更通知は Validated と CurrencyManager などのデータソース サポートクラスによって実現されている形になりますが、これらを使えば概ね事足りるのではないかと思います。そこに、そういった簡潔な仕組みを捨てて MVC 的な構造を作り上げようとする作業が複雑だといわれているだけじゃないでしょうか。

     

    2011年6月7日 3:46
  • >プロパティに変更があったことをプロパティを変更するだけでそのプロパティのバインド先に通知したい。そのために.NETで標準で用意されているのがINotifyPropertyChangedであり、それを使うことになる。
    >(WPFではこの目的のために依存関係プロパティが用意されたが、Windowsフォームには無い。WPFでもデザインパターンによってはINotifyPropertyChangedを使う。)

     

    まとまっていて分かりやすい内容でした。

    INotifyPropertyChangedが個々のプロパティの変更を通知するため適当になることが多い。結果、よく利用される、というようなことですね。納得です。

     

    2011年6月8日 12:13
  • バインドの実装方法が全くない、というようなことを言いたいのではなく、

    簡潔にそれを実装する方法が提供されていないということですね。言葉足らずになって申し訳ない。

     

    イベントも確かに楽かもしれませんが、@マーク1つで双方向バインディングを実装できる言語もあるので、

    めんどくさがってというのか、イベントでわざわざ拵えるほどのものなのかなぁ、と妙な勘繰りをしていた感じです。

    また、例えばC#4でこういうスマートなやり方が追加された、とかいう場合もあればいいなぁ、とも思っていました。

     

    >簡潔な仕組みを捨てて MVC 的な構造を作り上げようとする作業が複雑だといわれているだけ

    MVCパターンで開発するのに慣れていたので、とりあえずテストをしたいと思いいろいろ試しています。確かに、MVCの連結に必要なソースがちょっと多い分、複雑に見えそうな印象を受けました。

    WinFormsにおけるWPFでのMVVCのような主流のパターンがあるなら、とりあえずそれも調べて試してみたいと思います。

     

    2011年6月8日 12:32