INotifyPropertyChangedとViewModelのDependencyProperty


353

Model-View-ViewModelアーキテクチャのWPFアプリケーションにViewModelを実装する場合、データバインド可能にする方法は主に2つあるようです。DependencyPropertyViewがバインドするプロパティに使用する実装を見てきましたが、INotifyPropertyChanged代わりにViewModel実装を見てきました。

私の質問は、どちらを優先するかです。パフォーマンスに違いはありますか?ViewModelの依存関係をWPFに与えることは本当に良い考えですか?設計を決定するとき、他に何を考慮する必要がありますか?


11
INotifyPropertyChangedを実装するコンパイラチェック方法については、stackoverflow.com / questions / 1329138 /…を参照してください。プロパティ名をマジックストリングとして使用しないようにします。
イアンリングローズ

10
一般に、INotifyPropertyChangedを実装するクラスでは、依存関係プロパティと通常のプロパティに大きな違いがあります。依存関係プロパティはデータバインディングのソースまたはターゲットにすることができますが、INotifyPropertyChangedをサポートする通常のプロパティはソースとしてのみ使用できます。したがって、これらのソリューションは完全には互換性がありません。データバインディングインフラストラクチャでは、動作するターゲットとしてDPが必要ですが、ソースはINotifyPropertyChangedをサポートする通常のプロパティか、一般的なDPのいずれかです。
モスタファRezaei

4
.net 4.5の実装方法については、stackoverflow.com / a / 10595688/200442を参照してくださいINotifyPropertyChanged
ダニエル・リトル

ここで最もよく説明されているのは、 stackoverflow.com
a /

回答:


214

Kentがこのトピックについて興味深いブログを書いています:モデルの表示:POCOとDependencyObjectsの比較

短い要約:

  1. DependencyObjectsはシリアル化可能としてマークされていません
  2. DependencyObjectクラスは、Equals()メソッドとGetHashCode()メソッドをオーバーライドしてシールします。
  3. DependencyObjectにはスレッドアフィニティがあり、作成されたスレッドでのみアクセスできます。

私はPOCOアプローチを好みます。INotifyPropertyChangedインターフェースを実装するPresentationModel(別名ViewModel)の基本クラスは、http//compositeextensions.codeplex.comにあります。


24
DependencyObjectもWPFライブラリへの依存関係を取得しますが、POCOは依存関係を取得しないため、ビューモデルはWPFが利用できない他のUIスタックを駆動できます(コンパクトフレームワーク、Mono)。
codekaizen

26
そのため、Dependecyプロパティはビジネスレイヤーではなく、UI専用に構築されていることは明らかです。
AndreiRînea10年

11
依存関係プロパティには、DependencyObject親も必要です。ViewModelはDependencyObjectから継承すべきではありません。
Gusdor

38

WPFパフォーマンスガイドによると、DependencyObjectsはINotifyPropertyChangedを実装するPOCOよりも確実に優れています。

http://msdn.microsoft.com/en-us/library/bb613546.aspx


1
:私は、その1つの;-)に同意しなければなりませんblog.lexique-du-net.com/index.php?post/2010/02/24/...
Jonatha ANTOINE

.NET Frameworkバージョン4を選択した場合でも、リンクは機能します。「現在のバージョン」では利用できません。
doubleYou 2016年

これを指摘していただきありがとうございます。開発者がINotifyPropertyChangedはDPよりも高速であるか、オーバーヘッドが少ないという根本的な主張をしているスキャンダラスな誤報がたくさんありますが、根拠はありません。DPは、仮想(データ)ツリーを構造的に定義するための高速でエレガントで強力な方法です。
tpartee 2017

DependencyObjectsには隠れた悪があります。それらは、それらにバインドするコントロールと同じスレッドで作成する必要があります。それはGUIスレッドを意味します。つまり、そのスレッドに作成をディスパッチする必要があります。たとえば、DBからバックグラウンドスレッドにそれらをロードして作成することはできません。作品を発送しない限り。めちゃくちゃ。
ed22

28

選択は、ビジネスロジックとUI抽象化レベルに完全に基づいています。あなたが良い分離を望まないなら、DPはあなたのために働くでしょう。

DependencyPropertiesは主にVisualElementsレベルで適用できるため、ビジネス要件ごとに多数のDPを作成することはお勧めできません。また、DPにはINotifyPropertyChangedよりもコストがかかります。WPF / Silverlightを設計するときは、UIとViewModelを完全に別々に設計して、いつでもレイアウトとUIコントロールを変更できるようにします(テーマとスタイルに基づく)

この投稿も参照してください-/programming/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel。リンクには、Model-View-ViewModelパターンへの多くの参照があります。これは、この説明に非常に関連しています。


9
jbeによる投稿は、違いをより正確に答えます。VM(またはPresenter)がDependencyObjectから継承するからといって、スタイルを設定できない、またはビューから論理的に分離されていないという意味ではなく、プロパティ値のストレージが、 POCOスタイル。とはいえ、シリアル化、論理的等価性、およびスレッドアフィニティは、DepedencyObjectベースのVMが処理しなければならない実際の問題です。
ミカタン2009年

「また、INotifyPropertyChangedよりもDPの方がコストが高い」-これに関する証拠のソースはどこですか?多くの開発者がこれをサポートする証拠なしにこの主張をしています。MSDNによると、それは真実ではありません。「UIとViewModelを完全に分離して、いつでもレイアウトとUIコントロールを変更できるように設計してください」-これも、POCO + PropChangeとDO / DPとの関係はまったくありません。どちらかと言えば、DO / DPのReflection and Pathレジストリは、視覚的な側面で作業する能力を向上させます。
tpartee 2017

20

表現力の観点から、私は依存関係プロパティと不気味さを使用することを徹底的に楽しんでいINotifyPropertyChangedます。stringプロパティ名やイベントサブスクリプションによるメモリリークの可能性とは別に、INotifyPropertyChangedより明示的なメカニズムがあります。

依存関係プロパティは、理解しやすい静的メタデータを使用して「これを実行する場合」を意味します。それは優雅さへの私の投票を得る宣言的なアプローチです。


1
文字列部分には、nameof演算子を使用したソリューションがあります。
ニュートピア2017年

@Newtopian:そうです。また、いくつかの興味深いことが可能[CallerMemberName]です。
ブライアンワッツ

DO / DPモデルとPOCOを使用する場合のWPFおよびCLRのプロパティ登録(リフレクション)の豊富な利点は言うまでもありません。
tpartee 2017

16

INotifyPropertyChanged 使用すると、プロパティのゲッターとセッターのコードにロジックを追加することもできます。

DependencyProperty 例:

public static DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( String), typeof( Customer ) );

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}

あなたのゲッターとセッターで---できることは単にSetValueとGetValueをそれぞれ呼び出すだけで、b / cはフレームワークの他の部分ではゲッター/セッターは呼び出されず、代わりに直接SetValue、GetValueを呼び出すため、プロパティロジックは確実に実行されます。

INotifyPropertyChanged、イベントを定義します。

public event PropertyChangedEventHandler PropertyChanged;

そして、単純にコード内の任意の場所にロジックがあれば、次を呼び出します。

// ...
// Something cool...
// ...

if( this.PropertyChanged != null )
{
    PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
}

// More cool stuff that will reliably happen...

これは、getter / setter、または他の場所にある可能性があります。


11
DependencyPropertiesから変更通知を受け取ることもできます。PropertyMetadata.PropertyChangedCallbackを参照してください。例:msdn.microsoft.com/en-us/library/ms745795.aspx
Joe White

2
また、プロパティ内だけでなく、どこからでもSetValueを呼び出すことができます
aL3891

これは誤解を招きやすく、事実ではありません。DPが「内部」で変更された場合でも、DPの変更イベントにフックする方法は複数あります。そのうちの1人はJoe Whiteによって上で指摘されました
tpartee 2017

16

依存関係プロパティは、データバインディングへのソースとしてではなく、UI要素での(ターゲットとしての)バインディングをサポートすることを目的としています。ここで、INotifyPropertyの出番です。純粋な観点からは、ViewModelでDPを使用しないでください。

「バインディングのソースになるために、プロパティは依存関係プロパティである必要はありません。任意のCLRプロパティをバインディングソースとして使用できます。ただし、バインディングのターゲットになるためには、プロパティが依存関係プロパティ。一方向または双方向のバインディングを有効にするには、ソースプロパティが、バインディングシステム、つまりターゲットに伝達される変更通知をサポートする必要があります。カスタムCLRバインディングソースの場合、これは、プロパティがINotifyPropertyChangedをサポートする必要があることを意味します。コレクションはINotifyCollectionChangedをサポートする必要があります。」

すべての依存関係オブジェクトをシリアル化することはできません(これにより、ViewModelおよびDTO(POCO)の使用が妨げられる可能性があります。

Silverlight内のDPとWPFの間には違いがあります。

http://msdn.microsoft.com/en-us/library/cc221408(v=VS.95).aspx

http://msdn.microsoft.com/en-us/library/cc903933(VS.95).aspx


私は2009年から問題なくシリアル化された依存関係オブジェクトを使用しているので、「すべての依存関係オブジェクトをシリアル化することはできません」と言ったときに何を話しているのかわかりません。:実際には多くのオプションがありますcodeproject.com/Articles/61440/...の emphess.net/2008/11/25/dependencyproperty-serialization そして、私の個人的なお気に入りの一つを:ちょうどあなたのDPのすべてのバッキングストアを提供し、(それらの直列化可能を作りますGoogleで2分間検索しても、すぐに使える簡単な例はありませんでしたが、これが機能することを保証します)。
tpartee 2017

7

私も最近この決定を検討しなければなりませんでした。

INotifyPropertyChangedメカニズムは、状態を複製することなくGUIを既存のビジネスロジックフレームワークに接着できるため、ニーズに適していることがわかりました。私が使用していたフレームワークには独自のオブザーバーパターンがあり、1つのレベルの通知を次のレベルに転送するのは簡単でした。私のビジネスロジックフレームワークからのオブザーバーインターフェイスとINotifyPropertyChangedインターフェイスを実装するクラスがありました。

DPでは、状態を自分で保存するバックエンドを定義することはできません。私がバインドしていたすべての状態のアイテムのコピーを.netにキャッシュさせなければならなかっただろう。これは不要なオーバーヘッドのように見えました-私の状態は大きく複雑です。

そのため、ここでは、ビジネスロジックからGUIにプロパティを公開するためのINotifyPropertyChangedの方が優れていることがわかりました。

プロパティを公開し、そのプロパティを変更して他のGUIウィジェットに影響を与えるためにカスタムGUIウィジェットが必要なところは、DPがシンプルなソリューションであることを証明しています。

そこで、DPはGUIからGUIへの通知に役立つことがわかりました。


6

ViewModelの依存関係をWPFに与えることは本当に良い考えですか?

.NET 4.0にはSystem.Xaml.dllがあるため、それを利用するために任意のフレームワークに依存する必要はありません。PDCセッションに関するRob Relyeaの投稿を参照してください。

私の見解

XAMLはオブジェクトを記述するための言語であり、WPFは記述されたオブジェクトがUI要素であるフレームワークです。

それらの関係は、ロジックを記述するための言語であるC#と、特定の種類のロジックを実装するフレームワークである.NETに似ています。

XAMLの目的は、宣言型オブジェクトグラフです。W * Fテクノロジはこのパラダイムの優れた候補ですが、XAMLはそれらとは独立して存在します。

XAMLと依存関係システム全体は、WFとWPFの個別のスタックとして実装されました。おそらく、異なるチーム間の経験を活用するために、それらの間に依存関係(しゃれはありません)を作成することはありません。


答えると、bitbonkはXAMLとWPFが同じであると見なしていると想定しているようです。ViewModelは、論理的な分離を増やすのではなく、コードの複雑さを減らし、ユーザーコントロールのコードビハインドでロジックを単に記述することに関連するすべての問題を回避するために、できるだけ少ないWPF依存関係を持つ必要があります。ICommandのようなWPFの概念を必然的に実装し、WPF / Silverlightのみが簡単にラップできる動作を提示します。ビューモデルでのプレゼンテーションスレッドの懸念は、CollectionViewsとObservableCollectionのみです。
Gusdor 2011年

6

依存関係プロパティは、カスタムコントロール作成の接着剤です。Intelli-senseを使用してXAML設計時にプロパティウィンドウにプロパティを表示することに関心がある場合は、依存関係プロパティを使用する必要があります。INPCは、設計時にプロパティウィンドウにプロパティを表示しません。


4

依存関係プロパティは、ボタンなど、作成するコントロールで使用する必要があるようです。XAMLでプロパティを使用し、すべてのWPF機能を使用するには、それらのプロパティが依存関係プロパティである必要があります。

ただし、ViewModelはINotifyPropertyChangedを使用する方が適切です。INotifyPropertyChangedを使用すると、必要に応じてゲッター/セッターロジックを使用できます。

INotifyPropertyChangedを既に実装しているViewModelの基本クラスのJosh Smithのバージョンを確認することをお勧めします。

http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/

これは、ViewModelを実行する方法の優れた例だと思います。


4

DependencyPropertyとINotifyPropertyChangedは、Bindingの2つの異なる目的で使用されると思います。1つはプロパティをバインディングのターゲットにして、別のプロパティから入力を受け取るためです({Binding ...}を使用してプロパティを設定します)。プロパティの値をバインディングのソースとして使用する場合(バインディングパス式の名前)。したがって、選択は単に技術的なものです。


2
INotifyPropertyChangedはどちらの場合でも使用できます。TwoWayをバインドできます。DependencyPropertyは、Viewオブジェクトで実行される一部のアクション(たとえば、XAMLでViewオブジェクトをインスタンス化するときにいくつかのプロパティを設定する)の技術的な理由でのみ必要です。DependencyPropertyは、ViewModelには必要ありません。
oillio

3

私は、INotifyPropertyChangedを使用しないプレゼンテーションモデルでブログで説明した、より直接的なアプローチを好みます。データバインディングの代替手段を使用すると、簿記コードなしでCLRプロパティに直接バインドできます。ビューモデルで古い.NETコードを記述するだけで、データモデルが変更されると更新されます。


なしINotifyPropertyChangedPropertyDescriptor使用すると、メモリリークが発生する
Tilak

そのブログ投稿で紹介している更新コントロールライブラリは、プロパティ記述子ではなく弱参照を使用しています。メモリをリークしません。
マイケルLペリー

1
マイケル、あなたのライブラリはたくさんのコードを生成します。メリットはありません。生成されたPropertyChangedイベント呼び出しでモデルラッパーを生成することで、同じことを実現できます。
Der_Meister 2016年

3

優先する理由は1つだけありますDependencyObject-バインディングがよりうまく機能します。ただで例を試すListBoxTextBox、からデータを取り込むリストINotifyPropertyChangedプロパティの対DependencyPropertyからと編集現在の項目をTextBox...


1
コードサンプルをお願いします
Hassan Tareq '12

1

プロパティを他のコントロールに公開する場合は、依存関係プロパティを使用する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.