2009-08-26 5 views
16

diese Frage wird meinen Mangel an Verständnis für das erwartete Verhalten zeigen, wenn die Umsetzung/mit INotifyPropertyChanged:Beim Verschachteln von Eigenschaften, die INotifyPropertyChanged implementieren, muss sich das übergeordnete Objekt anpassen.

Die Frage ist - für die Bindung wie erwartet funktionieren, wenn Sie eine Klasse, die sich INotifyPropertyChanged implementiert, die verschachtelt hat Von Eigenschaften des Typs INotifyPropertyChanged wird erwartet, dass Sie die Änderungsbenachrichtigung für diese Eigenschaften intern abonnieren und dann die Benachrichtigungen weitergeben? Oder erwartet man von der verbindlichen Infrastruktur, dass die Smart-Software dies überflüssig macht?

Zum Beispiel (Code beachten Sie ist nicht vollständig - nur die Frage zu illustrieren gemeint):

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")); 
      } 
     } 
    } 

Also, in diesem Beispiel hat wir ein verschachteltes Adressobjekt in einem Person-Objekt haben. Beide implementieren INotifyPropertyChanged, so dass die Änderung ihrer Eigenschaften zur Übertragung von Benachrichtigungen über die Änderung von Eigenschaften an Abonnenten führt.

Aber sagen wir mal mit Bindung jemand abonniert Änderungsbenachrichtigung für ein Person-Objekt und "lauscht" nach Änderungen an der Address-Eigenschaft. Sie erhalten Benachrichtigungen, wenn sich die Address-Eigenschaft selbst ändert (ein anderes Address-Objekt wird zugewiesen), erhalten jedoch KEINE Benachrichtigungen, wenn die vom verschachtelten Adressobjekt (Stadt oder Straße) enthaltenen Daten geändert werden.

Dies führt zu der Frage - ist die bindende Infrastruktur erwartet, damit umzugehen, oder sollte ich innerhalb meiner Implementierung von Person abonnieren Benachrichtigungen über das Adressobjekt und dann propagieren sie als Änderungen an "Adresse" abonnieren?

Wenn Sie an diesen Punkt kommen, danke, dass Sie sich die Zeit genommen haben, diese langatmige Frage zu lesen?

Kommentar sehr geschätzt!

Phil

+0

Ich habe diese Frage nach dem googlen gefunden. Für mich sieht es so aus, als müssten Sie das PropertyChanged-Ereignis von Children manuell abonnieren und es in WPF-Bindungen arbeiten lassen. – loraderon

+1

loraderon, ich bin mir ziemlich sicher, dass das nicht der Fall ist - zumindest in meinen Tests hat sich gezeigt, dass dies der Fall ist. Und es gibt keine Information (die ich gefunden habe), um etwas anderes zu sagen. Haben Sie irgendwelche Links zu Informationen, die Sie zu diesem Thema bereitstellen können? Vielen Dank. Phil – Phil

+0

Ich habe auch keine Links. In meinem aktuellen Projekt musste ich das PropertyChanged Event blasen, damit es funktioniert. Ich bin ein Neuling bei WPF und MVVM, also könnte es bei meinem Projekt etwas Besonderes sein. – loraderon

Antwort

1

Sie beantwortet diese Frage, wenn Sie

sagte

... sagen mit Bindung jemand Anmeldung Benachrichtigung auf ein Person-Objekt zu ändern,

, dass jemand Abonnieren von Person und hat keine Möglichkeit zu wissen, ob Adresse sich geändert hat. Sie müssen also selbständig mit dieser Situation umgehen (was sehr einfach zu implementieren ist).

+1

Ist das wirklich der Fall? Zum Beispiel in WPF, könnte ich tun, um diese Hier (Wenn ich richtig liege!), würde die verbindliche Infrastruktur sicherstellen, dass die Textfelder Stadt und Straße aktualisiert werden, wenn sich entweder die Eigenschaft Adresse oder die Straße/Stadt ändert Änderungen. – Phil

+0

Sorry, dass Xaml nicht so gut in den Kommentar kam. Wie auch immer, was ich versuche zu sagen ist, dass es die Anforderung des Aufrufers (der Entität, die das Objekt Person verwendet) sein kann, sich für Änderungsbenachrichtigungen sowohl beim Personenobjekt als auch bei verschachtelten Eigenschaftsobjekten, die der Aufrufer verwendet, zu registrieren *. Ich sage nicht, dass dies der Fall ist! ... Deshalb stelle ich die ursprüngliche Frage, weil ich glaube, dass es für zwei Designs möglich ist (entweder Verantwortung auf den Benutzer oder auf den Implementierer schieben). Ich habe versucht, MS Dokumentation zu suchen, aber habe nichts Definitives gefunden. Prost! – Phil

+0

Es tut mir leid, angenommen, dass Sie Winfomrs verwenden. Ich habe nicht viel Wissen über WPF, aber ich vermute, dass es auch in WPF genauso funktionieren wird. WPF hat ein Konzept von Ereignissen, die aufblühen und Sie werden diese Tatsache wahrscheinlich nutzen müssen. Sehen Sie sich diesen Artikel an, er sollte Ihnen erklären, wie Sie benutzerdefinierte Routingereignisse erstellen können. http://msdn.microsoft.com/en-us/library/ms742806.aspx –

2

Eine der einfachsten Möglichkeiten, es zu tun ist, um einen Ereignishandler zu Person hinzufügen, welche Benachrichtigungsereignisse aus m_address Objekt behandelt:

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, ich weiß, wie die Benachrichtigung über die Änderung von Eigenschaften für Kinder implementiert wird. Die Frage ist, ob dies erforderlich ist, damit die Datenbindung korrekt funktioniert. Von dem, was ich gesehen habe, scheint die Antwort Nein zu sein - Sie müssen keine Änderungsbenachrichtigung durchführen, wenn sich ein Kindobjekt ändert: Es wird bereits von der verbindlichen Infrastruktur verarbeitet (zumindest für WPF) – Phil

+10

Sie können den Newsletter abbestellen Das PropertyChanged-Ereignis von Old m_address, bevor m_address auf einen neuen Wert in Address set gesetzt wurde. –

+0

Was passiert, wenn Adresse eine DependencyProperty ist? – tofutim

0

Wenn Sie untergeordnete Objekte sehen wollen, als ob sie ein Teil sind Ein Elternteil direkt, Sie müssen selbst sprudeln.

Für Ihr Beispiel wären Sie in Ihrer Ansicht an "Address.Street" gebunden, daher müssen Sie einen Notifypropertychanged bubble, der diese Zeichenfolge enthält.

Ich schrieb einen einfachen Helfer, dies zu tun. Sie rufen einfach BubblePropertyChanged (x => x.BestFriend) in Ihrem übergeordneten Ansichtsmodellkonstruktor auf. n.b. Es gibt eine Annahme, dass Sie eine Methode namens NotifyPropertyChanged in Ihrem Parent haben, aber Sie können das anpassen, um zu passen.

 /// <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

Eine alte Frage, doch ...

war meine ursprüngliche Ansatz Kind-Eigenschaft auf heften sich an die Eltern verändert. Dies hat einen Vorteil, das Essen des Elternteils ist einfach. Ich muss nur den Eltern abonnieren.

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); 
     } 
    } 
} 

Die Verwendung ist einfach:

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(); 
     } 
    } 
} 

Doch dieser Ansatz nicht sehr flexibel ist und es gibt keine Kontrolle über sie, zumindest ohne zusätzliche komplexe Technik. Wenn das subskribierende System die Flexibilität hat, verschachtelte Datenstrukturen zu durchlaufen, ist seine Anwendbarkeit auf Kinder der ersten Ebene beschränkt.

Während die Einschränkungen akzeptabel sind, je nach Verwendung, habe ich seitdem von diesem Ansatz entfernt, wie es nie sicher ist, wie die Datenstruktur schließlich verwendet wird. Derzeit bevorzugen Lösungen wie diese:

https://github.com/buunguyen/notify

Auf diese Weise auch komplexe Datenstrukturen sind einfach und vorhersehbar, es ist teilnehmergesteuert, wie abonnieren und wie sie reagieren, spielt sie auch mit Fähigkeiten von Bindungsmotoren.

Verwandte Themen