19

私は Person クラスを持っています:

public class Person : INotifyPropertyChanged
{
     private string _name;
     public string Name{
     get { return _name; }
     set {
           if ( _name != value ) {
             _name = value;
             OnPropertyChanged( "Name" );
           }
     }

     private Address _primaryAddress;
     public Address PrimaryAddress {
     get { return _primaryAddress; }
     set {
           if ( _primaryAddress != value ) {
             _primaryAddress = value;
             OnPropertyChanged( "PrimaryAddress" );
           }
     }

     //OnPropertyChanged code goes here
}

私は Address クラスを持っています:

public class Address : INotifyPropertyChanged
{
     private string _streetone;
     public string StreetOne{
     get { return _streetone; }
     set {
           if ( _streetone != value ) {
             _streetone = value;
             OnPropertyChanged( "StreetOne" );
           }
     }

     //Other fields here

     //OnPropertyChanged code goes here
}

私はビューモデルを持っています:

public class MyViewModel
{
   //constructor and other stuff here

     private Person _person;
     public Person Person{
     get { return _person; }
     set {
           if ( _person != value ) {
             _person = value;
             OnPropertyChanged( "Person" );
           }
     }

}

次の行を持つビューがあります。

<TextBox  Text="{Binding Person.Name, Mode=TwoWay,   
    UpdateSourceTrigger=PropertyChanged />

<TextBox  Text="{Binding Person.Address.StreetOne, Mode=TwoWay,   
    UpdateSourceTrigger=PropertyChanged />

ビューが読み込まれると、両方の値がテキスト ボックスに表示されます。

最初のテキスト ボックスへの変更はOnPropertyChanged( "Person" )、MyViewModel で発生します。偉大な。

2 番目のテキスト ボックスへの変更は、MyViewModel 内で("Person.Address.StreetOne")は発生しません。OnPropertyChanged( "Person" )Person オブジェクトの SET メソッドを呼び出さないことを意味します。良くない。興味深いことに、Address クラス内の StreetOne の SET メソッドが呼び出されます。

ViewModel 内の Person オブジェクトの SET メソッドが変更されたときに呼び出されるようにするにはどうすればよいですPerson.Address.StreetOneか?

SteetOne が Address ではなく Person 内にあるように、データを平坦化する必要がありますか??

ありがとう!

4

5 に答える 5

18

ViewModel に「パススルー」プロパティを追加することは優れたソリューションですが、すぐに受け入れられなくなる可能性があります。標準的な代替手段は、以下のように変更を伝達することです。

  public Address PrimaryAddress {
     get => _primaryAddress;
     set {
           if ( _primaryAddress != value ) 
           {
             //Clean-up old event handler:
             if(_primaryAddress != null)
               _primaryAddress.PropertyChanged -= AddressChanged;

             _primaryAddress = value;

             if (_primaryAddress != null)
               _primaryAddress.PropertyChanged += AddressChanged;

             OnPropertyChanged( "PrimaryAddress" );
           }

           void AddressChanged(object sender, PropertyChangedEventArgs args) 
               => OnPropertyChanged("PrimaryAddress");
        }
  }

現在、変更通知は Address から person に伝播されます。

編集:ハンドラーを c# 7 ローカル関数に移動しました。

于 2016-03-11T17:04:10.827 に答える
10

ビューモデルSE​​Tを呼び出す場合は、ストリートプロパティを作成できます

public class MyViewModel
{
  //constructor and other stuff here
  public string Street{
    get { return this.Person.PrimaryAddress.StreetOne; }
    set {
       if ( this.Person.PrimaryAddress.StreetOne!= value ) {
         this.Person.PrimaryAddress.StreetOne = value;
         OnPropertyChanged( "Street" );
       }
   }

 }

xaml

<TextBox  Text="{Binding Street, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged />

しかし、このソリューションには欠点があります。私は自分のプロジェクトでReedsの答えを使います

于 2012-08-09T05:21:03.057 に答える
6

Person.Address.StreetOne が変更されたときに ViewModel 内の Person オブジェクトの SET メソッドを呼び出すにはどうすればよいですか?

なぜこれをしたいのですか?StreetOneこれは必須ではありません。プロパティ変更イベントを発生させるだけで十分です。

SteetOne が Address ではなく Person 内にあるように、データを平坦化する必要がありますか??

実際にこれをトリガーしたい場合は、フラット化する必要はありません (オプションですが)。Person クラス内でAddressのイベントをサブスクライブし、変更されたときに "Address" のイベントを発生させることができます。ただし、これは必要ありません。PropertyChangedPerson

于 2012-08-08T17:43:52.247 に答える
3

すぐに使えるソリューションを見つけることができなかったので、Pieters (および Marks) の提案に基づいてカスタム実装を行いました (ありがとう!)。

クラスを使用すると、深いオブジェクト ツリーの変更が通知されます。これは、INotifyPropertyChangedタイプの実装とINotifyCollectionChanged* コレクションの実装に対して機能します (明らかに、そのために を使用しObservableCollectionています)。

これが非常にクリーンでエレガントなソリューションであることが判明したことを願っていますが、完全にはテストされておらず、機能強化の余地があります。使い方はとても簡単です。ChangeListener静的Createメソッドを使用してインスタンスを作成し、以下を渡すだけINotifyPropertyChangedです。

var listener = ChangeListener.Create(myViewModel);
listener.PropertyChanged += 
    new PropertyChangedEventHandler(listener_PropertyChanged);

これは常にオブジェクトの完全PropertyChangedEventArgsPropertyName「パス」になります。たとえば、Person の「BestFriend」名を変更するPropertyNameと、「BestFriend.Name」にBestFriendなります。 に Children のコレクションがあり、Age を変更すると、値は「BestFriend.Children[].Age」になります。の上。オブジェクトがいつ破棄されるかを忘れないでくださいDispose。その後、(できれば) すべてのイベント リスナーから完全にサブスクライブ解除されます。

.NET (4 でテスト済み) および Silverlight (4 でテスト済み) でコンパイルされます。コードは 3 つのクラスに分かれているため、コードをGist 705450に投稿しました。ここですべてを取得できます: https://gist.github.com/705450 **

*) コードが機能している理由の 1 つは、ObservableCollectionも実装しているためINotifyPropertyChangedです。

**) MIT ライセンスの下でリリースされ、無料で使用できます

于 2016-03-10T11:58:23.377 に答える
0

プロパティ変更通知にスペルミスがあります:

OnPropertyChanged( "SteetOne" );

する必要があります

OnPropertyChanged( "StreetOne" );

于 2012-08-08T20:53:14.173 に答える