2009-08-26 8 views
16

この質問は、実装時に/ INotifyPropertyChangedの使用期待されるふるまいの理解の私の欠如を示すことが起こっている:INotifyPropertyChangedを実装するネストプロパティは、親オブジェクトのpropogateを変更する必要がありますか?

質問です - あなたは自身がINotifyPropertyChangedのを実装するクラスを持っている場合、入れ子にしたこと、期待通りに動作するように結合するためにINotifyPropertyChanged型のプロパティは、これらのプロパティの通知を変更するために内部的にサブスクライブし、通知をプロポーザルする予定ですか?あるいは、拘束力のあるインフラストラクチャーにこれを不要にする賢明さが期待されていますか?例えば

(このコードは完全ではありません注意してください - ちょうど質問を説明するためのもの):

public class Address : INotifyPropertyChanged 
    { 
     string m_street 
     string m_city; 

     public string Street 
     { 
      get { return m_street; } 
      set 
      { 
      m_street = value; 
      NotifyPropertyChanged(new PropertyChangedEventArgs("Street")); 
      } 
     } 

     public string City 
     { 
      get { return m_city; } 
      set 
      { 
      m_city = value; 
      NotifyPropertyChanged(new PropertyChangedEventArgs("City")); 
      } 
     } 

    public class Person : INotifyPropertyChanged 
    { 
     Address m_address; 

     public Address 
     { 
      get { return m_address = value; } 
      set 
      { 
      m_address = value; 
      NotifyPropertyChanged(new PropertyChangedEventArgs("Address")); 
      } 
     } 
    } 

ので、この例では、我々は、PersonオブジェクトにネストされたAddressオブジェクトを持っています。いずれもINotifyPropertyChangedを実装しているため、プロパティの変更によってプロパティ変更通知がサブスクライバに送信されます。

バインドを使用すると、Personオブジェクトの通知を変更するサブスクリプションを使用していて、Addressプロパティの変更を「リッスン」しているとします。 Addressプロパティ自体が変更された場合(別のAddressオブジェクトが割り当てられている場合)、ネストされたアドレスオブジェクト(都市または通り)に含まれるデータが変更された場合、通知を受け取らないことになります。

これは、これを処理することが期待されるバインディングインフラストラクチャか、Personの実装でアドレスオブジェクトの通知を変更して「アドレス」の変更として伝播する必要がありますかという疑問があります。

この点をお持ちの場合は、この長い質問を読んでいただきありがとうございます。

コメントありがとうございました!あなたが...誰かが

されていることを、誰かが が Personオブジェクトに通知を変更するためにサブスクライブされたバインディングを使用言う

を言ったとき

フィル

+0

私はグーグルグーグルでこの問題を発見しました。私には、子供のPropertyChangedイベントを手動で購読してWPFバインディングで動作するようにバブルする必要があるようです。 – loraderon

+1

ロラデロン、私はそれが事実ではないと確信しています - 少なくとも私のテストでは、それは事実であることが判明しています。そして、そうでないと述べる情報はありません。この情報を提供できる情報へのリンクはありますか?ありがとう。 Phil – Phil

+0

私はリンクもありません。私の現在のプロジェクトでは、PropertyChangedイベントをバブルして機能させる必要がありました。私はWPFとMVVMの初心者ですので、私のプロジェクトで特別なことになるかもしれません。 – loraderon

答えて

1

あなたはこの質問に答えPersonに加入し、Addressが変更されたかどうかを知る方法がありません。 あなたはこの状況を自分で処理しなければなりません(これは実装が簡単です)。

+1

本当にそうですか? WPFの例では、私はこの <ラベルの内容= "ストリート" /> <ラベルの内容は= "市" /> {Address.Cityをバインド}私が正しいとすれば、バインディングインフラストラクチャは、Addressプロパティが変更された場合、またはStreet/City変更。 – Phil

+0

申し訳ありませんが、xamlはコメントにあまり出てこなかったのです。とにかく私が言っていることは、発信者(Personオブジェクトを使っているエンティティ)がpersonオブジェクトと、呼び出し元が使用するネストされたプロパティオブジェクトの両方に変更通知を登録するための要件である可能性があるということです*。 私はこれが当てはまると言っているのではありません!...それは、私が2つのデザイン(ユーザまたは実装者に責任を負わせること)が可能であると考えているからです。 私はMSのドキュメントを見てみましたが、決定的なものは見つかりませんでした。 乾杯! – Phil

+0

申し訳ありませんが、Winfomrsを使用していることを前提としています。 WPFについて多くの知識はありませんが、私の推測ではWPFでもまったく同じように動作します。 WPFにはバブルアップするイベントの概念があり、おそらくその事実を利用する必要があります。 この記事を見ると、カスタムルーティングイベントを作成する方法について説明しています http://msdn.microsoft.com/en-us/library/ms742806.aspx –

2

それを行うための最も簡単な方法の一つはm_addressオブジェクトからの通知イベントを処理する人にイベントハンドラを追加することです:彼らはの一部であるかのようにあなたが子供にしたい場合は

public class Person : INotifyPropertyChanged 
{ 
    Address m_address; 

    public Address 
    { 
     get { return m_address = value; } 
     set 
     { 
     m_address = value; 
     NotifyPropertyChanged(new PropertyChangedEventArgs("Address")); 
     m_address.PropertyChanged += new PropertyChangedEventHandler(AddressPropertyChanged); 
     } 
    } 
    void AddressPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     NotifyPropertyChanged(new PropertyChangedEventArgs("Address")) 
    } 
} 
+2

tkola、私は子供のためのプロパティ変更通知を実装する方法を知っています。問題は、データバインディングが正しく機能するために必要なことです。私が見たことから、答えは「いいえ」のように見えます。子オブジェクトの変更時に変更通知を行う必要はありません。バインディングインフラストラクチャ(少なくともWPFの場合)によって効果的に処理されています。 – Phil

+10

古いm_addressのPropertyChangedイベント、m_addressをAddress setの新しい値に設定する –

+0

AddressがDependencyPropertyの場合はどうなりますか? – tofutim

0

は見にオブジェクト彼らの親はあなた自身をバブリングする必要があります。

たとえば、ビュー内で 'Address.Street'にバインドするので、その文字列を含むnotifypropertychangedをバブリングする必要があります。

私はこれを簡単に手助けしました。親ビューモデルのコンストラクタでBubblePropertyChanged(x => x.BestFriend)を呼び出すだけです。 n.b.あなたの親にはNotifyPropertyChangedというメソッドがあると仮定していますが、それを適合させることができます。

 /// <summary> 
    /// Bubbles up property changed events from a child viewmodel that implements {INotifyPropertyChanged} to the parent keeping 
    /// the naming hierarchy in place. 
    /// This is useful for nested view models. 
    /// </summary> 
    /// <param name="property">Child property that is a viewmodel implementing INotifyPropertyChanged.</param> 
    /// <returns></returns> 
    public IDisposable BubblePropertyChanged(Expression<Func<INotifyPropertyChanged>> property) 
    { 
     // This step is relatively expensive but only called once during setup. 
     MemberExpression body = (MemberExpression)property.Body; 
     var prefix = body.Member.Name + "."; 

     INotifyPropertyChanged child = property.Compile().Invoke(); 

     PropertyChangedEventHandler handler = (sender, e) => 
     { 
      this.NotifyPropertyChanged(prefix + e.PropertyName); 
     }; 

     child.PropertyChanged += handler; 

     return Disposable.Create(() => { child.PropertyChanged -= handler; }); 
    } 
0

古い質問が、それにもかかわらず...

私の独創的なアプローチは、親に変更された子プロパティを添付しました。これには利点があり、親のイベントを消費するのは簡単です。親に申し込むだけです。

public class NotifyChangedBase : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 
    readonly Dictionary<string, AttachedNotifyHandler> attachedHandlers = new Dictionary<string, AttachedNotifyHandler>(); 

    [NotifyPropertyChangedInvocator] 
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    protected void AttachPropertyChanged(INotifyPropertyChanged notifyPropertyChanged, 
     [CallerMemberName] string propertyName = null) 
    { 
     if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); 
     // ReSharper disable once ExplicitCallerInfoArgument 
     DetachCurrentPropertyChanged(propertyName); 
     if (notifyPropertyChanged != null) 
     { 
      attachedHandlers.Add(propertyName, new AttachedNotifyHandler(propertyName, this, notifyPropertyChanged)); 
     } 
    } 

    protected void DetachCurrentPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); 
     AttachedNotifyHandler handler; 
     if (attachedHandlers.TryGetValue(propertyName, out handler)) 
     { 
      handler.Dispose(); 
      attachedHandlers.Remove(propertyName); 
     } 
    } 

    sealed class AttachedNotifyHandler : IDisposable 
    { 
     readonly string propertyName; 
     readonly NotifyChangedBase currentObject; 
     readonly INotifyPropertyChanged attachedObject; 

     public AttachedNotifyHandler(
      [NotNull] string propertyName, 
      [NotNull] NotifyChangedBase currentObject, 
      [NotNull] INotifyPropertyChanged attachedObject) 
     { 
      if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); 
      if (currentObject == null) throw new ArgumentNullException(nameof(currentObject)); 
      if (attachedObject == null) throw new ArgumentNullException(nameof(attachedObject)); 
      this.propertyName = propertyName; 
      this.currentObject = currentObject; 
      this.attachedObject = attachedObject; 

      attachedObject.PropertyChanged += TrackedObjectOnPropertyChanged; 
     } 

     public void Dispose() 
     { 
      attachedObject.PropertyChanged -= TrackedObjectOnPropertyChanged; 
     } 

     void TrackedObjectOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs) 
     { 
      currentObject.OnPropertyChanged(propertyName); 
     } 
    } 
} 

使い方は簡単です:

public class Foo : NotifyChangedBase 
{ 
    Bar bar; 

    public Bar Bar 
    { 
     get { return bar; } 
     set 
     { 
      if (Equals(value, bar)) return; 
      bar = value; 
      AttachPropertyChanged(bar); 
      OnPropertyChanged(); 
     } 
    } 
} 

public class Bar : NotifyChangedBase 
{ 
    string prop; 

    public string Prop 
    { 
     get { return prop; } 
     set 
     { 
      if (value == prop) return; 
      prop = value; 
      OnPropertyChanged(); 
     } 
    } 
} 

しかし、このアプローチは非常に柔軟ではなく、それに対する制御は、少なくとも追加の複雑なエンジニアリングなしに、ありません。サブスクライブするシステムにネストされたデータ構造をトラバースする柔軟性がある場合、その適用範囲は第1レベルの子に限定されます。

使用方法に応じて警告が受け入れられるかもしれませんが、データ構造が最終的にどのように使用されるのかは決してわからないので、私はこのアプローチから離れました。現在、この1などのソリューションを好む:

https://github.com/buunguyen/notify

方法にも複雑なデータ構造がシンプルかつ予測可能であることを、それが結合エンジンの機能とよく果たしている、サブスクライブするとどのように反応するか、加入者の制御下にあります。

関連する問題