21

この質問は、INotifyPropertyChangedを実装/使用するときに予想される動作についての私の理解の欠如を示しています。

問題は、バインディングが期待どおりに機能するために、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の実装内で、アドレスオブジェクトの変更通知をサブスクライブしてから、それらを「アドレス」への変更として伝播する必要がありますか?

この点に到達した場合は、この長く曲がりくねった質問を読むのに時間を割いていただきありがとうございます。

4

4 に答える 4

3

これを行う最も簡単な方法の 1 つは、m_address オブジェクトからの通知イベントを処理するイベント ハンドラーを Person に追加することです。

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"))
   }
}
于 2010-08-01T22:22:16.630 に答える
1

あなたが言ったときにあなたはこの質問に答えました

...誰かをバインドすることを使用して、Personオブジェクトの通知を変更するようにサブスクライブしているとします。

誰かがPersonにサブスクライブしていて、アドレスが変更されたかどうかを知る方法がないこと。したがって、この状況を自分で処理する必要があります(これは非常に簡単に実装できます)。

于 2009-08-26T18:26:59.260 に答える
0

子オブジェクトを直接親の一部であるかのように表示したい場合は、自分でバブリングを行う必要があります。

あなたの例では、ビューで「Address.Street」にバインドするため、その文字列を含む notifypropertychanged をバブルする必要があります。

これを行うための簡単なヘルパーを書きました。親ビュー モデル コンストラクターで BubblePropertyChanged(x => x.BestFriend) を呼び出すだけです。nb 親に 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; });
    }
于 2012-08-21T17:37:24.797 に答える