Laden...

Objekt (und alle "Kindobjekte") auf Änderungen überwachen

Erstellt von adm1n vor 9 Jahren Letzter Beitrag vor 9 Jahren 3.950 Views
Thema geschlossen
A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 9 Jahren
Objekt (und alle "Kindobjekte") auf Änderungen überwachen

Hallo Forum,

ich programmiere derzeit eine GUI, die Daten aus einer XML-Datei einliest, in Objekten darstellt und anschließend wieder in eine XML-Datei schreibt.

Wie ihr sicher kennt, zeigt es in der Titelleiste oft ein * an, sobald eine Änderung vorgenommen wurde und ein Speichervorgang erforderlich ist. Diese Funktion will ich implementieren und frage mich daher, wie ich das "Basisobjekt" (inklusive aller Objekte die dann im weiteren erstellt werden) überwachen kann und bei einer Änderung dann die dementsprechende Funktion aufrufe.

Ich habe mir schon INotifyPropertyChanged angeschaut, jedoch findet man diesbezüglich immer nur das Überwachen von einer Variable o.ä., aber nicht von einem Objekt inklusive allen anderen Objekten die daraus resultieren.

Freue mich über eure Hilfe!

Grüße

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

F
10.010 Beiträge seit 2004
vor 9 Jahren

Dann muss das Objekt eben INoty... selber für alle kinder abbonieren und weiterreichen ( ObservableCollection macht das z.b. )

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 9 Jahren

Quasi alle existierenden (Kind-)Klassen das Interface INotifyPropertyChanged zuweisen?
Muss ich dann auch auch bei jeder Klassenvariable eine Funktion aufrufen, sobald diese "gesettet" wird?

Quasi wie hier im Beispiel: INotifyPropertyChanged-Schnittstelle

public class DemoCustomer : INotifyPropertyChanged
    {
        private string customerNameValue = String.Empty;
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        // The constructor is private to enforce the factory pattern.
        private DemoCustomer()
        {
            // ...
        }

        public string CustomerName
        {
            get
            {
                return this.customerNameValue;
            }

            set
            {
                if (value != this.customerNameValue)
                {
                    this.customerNameValue = value;
                    NotifyPropertyChanged();
                }
            }
        }
    }

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

W
955 Beiträge seit 2010
vor 9 Jahren

Naja,

es ist doch nicht schwer. Mach ein abstraktes Basis-Modellobjekt welches INotifyPropertyChanged implementiert und lass Deine Modellobjektedavon erben. Und dann in den Modellobjekt-Properties:



        private String datasourceName;
        public String DatasourceName
        { 
            get { return datasourceName; }
            set { SetProperty(ref datasourceName, value); }
        }  

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 9 Jahren

Ok, damit ich das richtig verstehe:


// Abstrakte Klasse
public abstract class AbstrakteKlasse : INotifyPropertyChanged
 {
     public event PropertyChangedEventHandler PropertyChanged;

     protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String callerMemberName = null)
     {
         if (object.Equals(storage, value)) 
        {
             return false;
         }

         storage = value;
         this.OnPropertyChanged(callerMemberName);
         return true;
     }

     protected void OnPropertyChanged([CallerMemberName] string callerMemberName = null)
     {
         var eventHandler = this.PropertyChanged;
         if (eventHandler != null)
         {
             eventHandler(this, new PropertyChangedEventArgs(callerMemberName));
         }
     }
 }


// Die Haupt- und jede Kindklasse wird dann folgendermaßen aufgebaut
public class Klasse1 : AbstrakteKlasse
{
     private string _Variable;
     public string m_Variable
     {
         get { return _Variable; }
         set { SetProperty(ref _Variable, value); }
     }
}

(siehe Dan Rigby: INotifyPropertyChanged, The .NET 4.5 Way - Revisited)

Wie überwache ich nun den Event in meiner GUI und führe bei einer Änderung dementsprechend eine Funktion aus?

Beim erzeugen jeder Klasse muss ich nun immer eine Funktion übergeben, die die Änderung (PropertyChanged) bearbeitet, oder? Wie mache ich es aber nun, dass ich in meinem (obersten) Hauptobjekt mitbekomme, ob sich in den Unterobjekten etwas geändert hat? Muss das ganze ja irgendwie vom Kind an die Mutter weiterleiten?!


HauptklassenInstanz.PropertyChanged += HandlePropertyChanged;

private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // Aktion wenn etwas am Objekt der Hauptklasse oder dessen Kindobjekte geändert wurde
        }

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

W
955 Beiträge seit 2010
vor 9 Jahren

Hallo,

  1. Jetzt sendet jeder Knoten PropertyChanged. Wenn Du sie an eine GUI bindest welche NotifyPropertyChanged beherrscht, kann diese die Änderungen anzeigen (beispielsweise ein WPF Treegrid)
  2. Wenn Du diese Events an andere weiterleiten willst (um beispielsweise das Sternchen im Title anzuzeigen) hast Du mehrere Möglichkeiten
    * Der Parentknoten überwacht seine Kinder und schmeißt selber NotifyPropertyChanged wenn eines seiner Kinder ein solches Event generiert => das Ereignis wird zum Root durchgereicht
    * Die Knoten Deines Trees bilden ein Kompositum. Alle dessen Knotentypen implementieren eine Schnittstelle sowas wie HasChanged(). Der veränderte Knoten ruft das an seinen Parent auf => das Ereignis wird zum Root durchgereicht
    Wenn Du das NotifyPropertyChanged Event abonnieren willst nimm lieber PropertyChangedEventManager.AddHandler(object, ViewPropertyChanged, ""); und PropertyChangedEventManager.RemoveHandler(object, ViewPropertyChanged, "");
    Das sind WeakEvents, damit kann der GC nicht mehr benutzte Knoten abräumen auch wenn diese noch Listener besitzen.
49.485 Beiträge seit 2005
vor 9 Jahren

Hallo adm1n,

Muss das ganze ja irgendwie vom Kind an die Mutter weiterleiten?!

müssen musst du nicht. Du kannst auch von außen direkt die Events aller Objekte (Wurzel, Kinder, Kindeskinder ...) abonnieren. Dazu musst man nur einmal (rekursiv) über den Objekt-Baum laufen und von jedem Objekt das Event abonnieren.

Muss das ganze ja irgendwie vom Kind an die Mutter weiterleiten?!

Wie gesagt, müssen musst du nicht, aber wenn du es willst, kannst du es machen. Zum Weiterleiten von Events siehe best practise: Event einer aggregierten Klasse weiterleiten.

herbivore

4.221 Beiträge seit 2005
vor 9 Jahren

Oder Du liest das XML in ein DataSet ein... bindest Die Objekte an die Rows... dann kannst Du mit DataSet.HasChanges jederzeit den Status abfragen... oder dich in die Events des DataSets einklinken

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 9 Jahren

@witte

Okay, vielen Dank! Kann ich mit NotifyPropertyChanged auch überwachen, wenn einer Liste von Objekten ein neues Objekt hinzugefügt wird? Bisher löst PropertyChanged nur aus, wenn ich eine Klassenvariable setzte (da _set { SetProperty(ref Variable, value); }).

Du kannst auch von außen direkt die Events aller Objekte (Wurzel, Kinder, Kindeskinder ...) abonnieren. Dazu musst man nur einmal (rekursiv) über den Objekt-Baum laufen und von jedem Objekt das Event abonnieren.

Okay, das abonnieren mache ich über die Code Zeile aus meinem vorherigen Beispiel, oder?

HauptklassenInstanz.PropertyChanged += HandlePropertyChanged;

Und das eben nicht nur bei der Hauptklasse, sondern in allen Kindklassen.

Oder Du liest das XML in ein DataSet ein... bindest Die Objekte an die Rows... dann kannst Du mit DataSet.HasChanges jederzeit den Status abfragen... oder dich in die Events des DataSets einklinken Hm, ja, auch eine Idee, aber da passen meine Anforderungen nicht ganz. Trotzdem danke.

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

W
955 Beiträge seit 2010
vor 9 Jahren

Kann ich mit NotifyPropertyChanged auch überwachen, wenn einer Liste von Objekten ein neues Objekt hinzugefügt wird? Bisher löst PropertyChanged nur aus, wenn ich eine Klassenvariable setzte Dafür benötigst Du INotifyCollectionChanged. Also die Kindercollections in eine ObservableCollection<T> wrappen.

Gelöschter Account
vor 9 Jahren

Was ich soweit verstanden habe ist das du eine Liste auf Veränderung überwachen willst also dann bitte zeig auch die Liste in deinem Code und nicht das Item. Das einzelne <T> nützt uns hier nix. Schlimmer noch hab ich den Eindruck das du 1:N Relationen hast also das deine Items weitere ListProperties haben. In dem Fall musst du dich entscheiden ob du alles über den System.ComponentModel namespace lernen willst oder ob du dich eher für WPF entscheidest. So oder so: wenn du die Hürde nehmen willst musst du dir Zeit einfach nehmen, das Problem dahinter ist zu multidimensional und einfache Antwort wirds nicht geben.

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 9 Jahren

@Sebastian.Lange
Danke für die Antwort. Ich versuche das ganze mal expliziter zu erklären:

Es ist so, dass ich eine XML-Datei komplett in Objekten und Variablen abbilde. Jedes XML-Element ist eine Klasse und jedes Attribut ist eine Klassenvariable. Das XML-Element A gibt es nur ein einziges mal, alle anderen XML-Elemente können mehrmals vorkommen, unterscheiden sich aber immer nur in den Attributen. Vom XML-Element AA gibt es im Beispiel unten also 2 Objekte, von XML-Element A nur 1 Objekt.

XML-Datei


<A>
   <AA Attribut1="Wert1"> 
      <AAA></AAA>
      <AAB>
         <AABA></AABA>
      </AAB>
      <AAC></AAC>
   </AA>
   <AA Attribut1="Wert2">
      <AAA Attribut1="Wert1" Attribut2="Wert2"></AAA>
      <AAA Attribut1="Wert2" Attribut2="Wert2"></AAA>
   </AA>
   <AB> 
      <ABA Attribute1="Wert1"> </ABA>
      <ABA Attribute1="Wert2"> </ABA>
   </AB>
   
</A>

Klassenstruktur


Class A
{
   Liste von Objekten AA
   Liste von Objekten AB
}

Class AA
{
   Attribut1

   Liste von Objekten AAA
   Liste von Objekten AAB
   Liste von Objekten AAC
}

Class AAB
{
   Liste von Objekten AABA
}

Class AAA
{
   Attribut1
   Attribut2
}
usw.

Nun will ich wissen, ob sich am Objekt A oder irgend einem Attribut oder Liste von anderen Objekten (die sich "darunter" befinden) etwas geändert hat.

Hoffe ich habe es nun verständlicher beschrieben, wenn nicht, bitte kurze Rückmeldung 😃

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

16.842 Beiträge seit 2008
vor 9 Jahren

witte hat es doch schon erklärt: sobald sich irgendein Child ändert, dann meldet dieses das an das Parent. Und das Parent meldet das wiederum an sein Parent - bis zum Root hoch.
Realisierung über INotifyPropertyChanged um die Änderung einer Eigenschaft zu realisieren und ObservableCollection für die Liste der Childs.

A
adm1n Themenstarter:in
51 Beiträge seit 2014
vor 9 Jahren

Ja, ich weiß. Die Erklärung im letzten Post war für Sebastian.Lange gedacht, da (wohl) das Ganze von mir undeutlich präsentiert wurde 😃 Danke.

Die Grenzen meiner Sprache bedeuten die Grenzen meiner Welt - Tractatus 5.6, Ludwig Wittgenstein

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo adm1n,

Okay, das abonnieren mache ich über die Code Zeile aus meinem vorherigen Beispiel, oder?

ja, (allerdings setzen wir die Grundlagen voraus. Also bitte keine (unnötigen) Nachfragen dazu. Siehe auch [Hinweis] Wie poste ich richtig? Punkt 1.1.1.

Und das eben nicht nur bei der Hauptklasse, sondern in allen Kindklassen.

In erster Linie geht es mal darum, das Event alle Kindobjekte zu abonnieren, egal von welcher Klasse die sind. Wenn das Event in der Oberklasse definiert ist, steht es ja automatisch in allen Unterklassen zur Verfügung. Klar müssen die Unterklassen auch noch dafür sorgen, dass das Event auch bei allen Änderungen ausgelöst wird, aber das steht auf einem anderen Blatt. Vorhanden ist das Event alleine durch die Deklaration in der Oberklasse und die normale Vererbung. Und damit kann es einheitlich für alle (Kind-)Objekt abonniert werden.

Ansonsten haben wir schon alle nötige erklärt. Du musst es jetzt nur noch umsetzen. Die Umsetzung ist deine Aufgabe. Dafür hast du jetzt alle Informationen, die du brauchst.

herbivore

Thema geschlossen