Laden...

Auf Übergeordnete Eigenschaften zugreifen

Erstellt von fritziiiii vor 6 Jahren Letzter Beitrag vor 6 Jahren 3.203 Views
F
fritziiiii Themenstarter:in
7 Beiträge seit 2014
vor 6 Jahren
Auf Übergeordnete Eigenschaften zugreifen

Hi,
bitte verzeiht meinen blöden Betreff, aber ich weiß nicht wie ich mein Problem besser bezeichnen könnte:

Ausgangslage:

  • ich habe eine Klasse "EinzelnesElement"

  • ich habe eine Klasse "Basisklasse"
    In dieser Basisklasse erzeuge ich eine ObservableCollection<EinzelnesElemente> ListeElemente
    In der Basisklasse ist zudem die Eigenschaft "Status" deklariert.

Nun zum Problem:
Ich würde in der Klasse "EinzelnesElement" zu gern den Wert der Eigenschaft "Status" abfragen!
Wie krieg ich das hin?


namespace WpfApp1
{
    public class EinzelnesElemente : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null) PropertyChanged(this, e);
        }

        public string Name { get; set; }

        public void StatusAbfragen()
        {
            // hier würd ich gern den Status der Basisklasse abfragen
        }
    }


    public class Basisklasse : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null) PropertyChanged(this, e);
        }

        public ObservableCollection<EinzelnesElemente> ListeElemente = new ObservableCollection<EinzelnesElemente>();

        public int Status = 5;

    }

        /// <summary>
        /// Interaktionslogik für MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
    {
        public MainWindow()
        {

            Basisklasse basis = new Basisklasse();
            basis.ListeElemente.Add(new EinzelnesElemente());
            InitializeComponent();
        }
    }
}


D
985 Beiträge seit 2014
vor 6 Jahren

Da wirst du irgendwie an EinzelnesElemente eine Referenz zu der Basisklasse übergeben müssen, sonst geht das nicht.

Wenn du nicht weißt in welchem Haus du wohnst, wie sollst du wissen wie es aussieht?

O
79 Beiträge seit 2011
vor 6 Jahren

Oder indem man schlicht und ergreifend

EinzelnesElement

von

BasisKlasse

ableitet...

709 Beiträge seit 2008
vor 6 Jahren

Sir Rufos Antwort erscheint mir schlüssiger.

Schau dir mal folgenden Fall an:
Es gibt ein Basisklassenobjekt und 5 EinzelnesElemente-Objekte, die sich in ListeElemente der Basisklasse befinden.
Man will jetzt auf den Status zugreifen.

Bei einer Ableitung hat jedes Objekt seinen eigenen Status und weiß immer noch nichts über den Status des Objekts, in dessen ListeElemente man sich befindet.

F
fritziiiii Themenstarter:in
7 Beiträge seit 2014
vor 6 Jahren

Schonmal danke für eure Antworten.
Habe nun meinen Code angepaßt, aber der Groschen ist noch nicht ganz gefallen

die Basisklasse habe ich um die Methode "ElementHinzufuegen ergänzt. In dieser Methode gebe ich eine Referenz auf die Variable "Status"

public void ElementHinzufuegen(ref int status)
        {
            ListeElemente.Add(new EinzelnesElemente(ref status));
        }

In der Klasse "EinzelnesElement" habe ich einen Konstruktor eingefügt, der die Referenz entgegennimmt

 public EinzelnesElemente(ref int status)
        {
            Status = status; 
        }

Aber jetzt komm ich nicht weiter. Was muß ich machen, damit ich in der Klasse mit der Referenz weiterarbeiten kann und NICHT als neue Variable ablege?

namespace WpfApp1
{
    public class EinzelnesElemente : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null) PropertyChanged(this, e);
        }

        int Status;

        // Konstruktor
        public EinzelnesElemente(ref int status)
        {
            Status = status;  //Baustelle
        }
        

        public string Name { get; set; }

        public string StatusAbfragen()
        {
            return Convert.ToString(Status);
        }



    }


    public class Basisklasse : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null) PropertyChanged(this, e);
        }

        public ObservableCollection<EinzelnesElemente> ListeElemente = new ObservableCollection<EinzelnesElemente>();

        public int Status = 5;
        

        public void ElementHinzufuegen(ref int status)
        {
            ListeElemente.Add(new EinzelnesElemente(ref status));
        }

    }

    public partial class MainWindow : Window
    {
        Basisklasse basis = new Basisklasse();
        public MainWindow()
        {
            basis.ElementHinzufuegen(ref basis.Status);

            InitializeComponent();
           
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            basis.Status = 7;
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
          
            Label_Ausgabe.Content = basis.ListeElemente[0].StatusAbfragen();
        }
    }
}
2.298 Beiträge seit 2010
vor 6 Jahren

Was Sir Rufo meinte war eher der Ansatz, dass deine Elemente eine Referenz auf den Parent haben.

In deinem Fall:


public class EinzelnesElement
{
      private BasisKlasse _parent;
    
      public EinzelnesElement(Basisklasse parent)
      {
          this._parent = parent;
      }

      public void StatusAbfragen()
      {
           // wie auch immer deine Statusabfrage aussieht
           this._parent.Status ??? 
      }
}

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

F
fritziiiii Themenstarter:in
7 Beiträge seit 2014
vor 6 Jahren

Danke!

Das war die Lösung!

T
461 Beiträge seit 2013
vor 6 Jahren

Also ich hätte in der Klasse EinzelnesElemente ein Event eingebaut, welches den aktuellen Status besorgt.

Somit müßte die Basisklasse das Event eines jeden Eintrags registrieren.
Dadurch fällt die Abhängigkeit des Klassenverweises von Basisklasse aus EinzelnesElemente weg.

Ist dann die andere Lösung, im Gegensatz zu @Sir Rufo's Vorschlag.

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

2.298 Beiträge seit 2010
vor 6 Jahren

Hallo ThomasE,

deine Lösung ist natürlich die sauberere. Für den aktuellen Anwendungsfall, nehme ich aber an dass diese EinzelElemente nur Bestandteil von "BasisKlasse" sind und damit eine feste Kopplung möglich ist.

Spätestens wenn fritziiiii die Elemente noch an anderer Stelle initialisiert die nichts mit "BasisKlasse" zu tun hat sollte er natürlich die saubere Umsetzung in Betracht ziehen und über Events den Status anfragen.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

1.040 Beiträge seit 2007
vor 6 Jahren

Wie genau soll die (sauberere) Lösung mit dem Event im EinzelnesElement aussehen? 🤔

F
10.010 Beiträge seit 2004
vor 6 Jahren

Das Event kommt an die klasse die das Ereignis auslöst.

Aber Ich denke eher das fritziiiii nicht mal verstanden hat was INotifyPropertyChanged bedeutet bzw. wie man es tatsächlich anwendet, denn einfach nur das Interface grob implementieren bringt überhaupt nix, wenn man keine Properties implementiert .

Ansonsten müsste man ja nur INPC von der Basisklasse abonieren und entsprechend reagieren.

5.657 Beiträge seit 2006
vor 6 Jahren

Wie man die INotifyPropertyChanged-Schnittstelle richtig implementiert und verwendet, ist in [Artikel] MVVM und DataBinding beschrieben. Der Rest sind dann eher Grundlagen der Objektorientierten Programmierung.

Weeks of programming can save you hours of planning

F
fritziiiii Themenstarter:in
7 Beiträge seit 2014
vor 6 Jahren

Hallo FZelle,

keine Frage, ich bin bestimmt nicht die hellste Kerze auf der Torte, was Programmierung angeht.
Zu meiner Verteidigung muß ich jedoch anfügen, daß ich aus meinem bestehenden Projekt Code-Bruckstücke herausgenommen habe, und daraus ein Grundgerüst für meine Fragestellung hier aufgebaut habe. Das INotifyPropertyChanged hab ich einfach nur vergessen herauszunehmen. Mein Hauptaugenmerk war darauf gerichtet, einen kurzen, auf das Kernproblem zielenden Code zu liefern.

Da du dich ja scheinbar mit Events auskennst, würde es mich freuen, wenn du mein Code-Beispiel so abänderst, daß es mit Events funktioniert. P!lle scheint das ja durchaus auch zu interessieren.

Gruß
Fritziiiii

5.657 Beiträge seit 2006
vor 6 Jahren

Hi fritziiiii,

wir helfen ja gerne weiter, aber das Forum ist nicht dazu da, dir deinen Code zu schreiben. Davon würdest du ja auch nichts lernen.

Wenn du dich nicht mit Events auskennst, dann kannst du dir die Beispiele in der Doku anschauen, oder die entsprechenden Kapitel im OpenBook vom Rheinwerk-Verlag: 🛈

Niemand wird mit Programmierkenntnissen geboren, aber wenn du dir die Basics nicht anliest, dann wirst du es auch nicht lernen.

Hier gibt es auch noch ein paar interessante Links für den Einstieg: [FAQ] Wie finde ich den Einstieg in C#?

Bitte beachte auch [Hinweis] Wie poste ich richtig?, besonders Punkt 1.1 und 4.

Weeks of programming can save you hours of planning

F
10.010 Beiträge seit 2004
vor 6 Jahren

@fritziiiii:
Deine Herangehensweise ist vollkommen falsch.

Du nimmst irgendwelche brocken und versuchst die zusammenzustockeln ohne zu verstehen was die machen.

Stell dir vor du baust ein Fahrrad, dazu nimmst du 2 Räder, einen rahmen, Lenker usw.
Dann schaust du wie die irgendwie zusammengehören und machst dann den lenker an die Pedale, die Räder an den Sattel und fragst dich warum das nicht fährt.

Genauso ist das hier auch.
Du musst dir erst die Grundlagen aneignen, danach kannst du erst mit dem Probieren anfangen.

Schon die Aussage das INPC hier versehentlich noch dran ist, und dann auch noch falsch implementiert, aber in Wirklichkeit die Lösung zu deinem Problem wäre, zeigt das du wegen fehlender Grundlagen den Wald vor lauter Bäumen nicht siehst.

Erstmal eine echte Basisklasse die mal INPC "richtiger" implementiert


public class BaseViewModel : INotifyPropertyChanged, IDisposable
{
	public event PropertyChangedEventHandler PropertyChanged;
	protected virtual void OnPropertyChanged(string propertyName)
	{
		var handler = PropertyChanged;
		if (handler != null) 
			handler(this, new PropertyChangedEventArgs(propertyName));
	}
	protected bool SetProperty<T>(ref T oldValue, T newValue, [CallerMemberName]string propertyName = null)
	{
		if (oldValue == null && newValue == null)
		{
			return false;
		}

		if ((oldValue == null && newValue != null) || !oldValue.Equals(newValue))
		{
			oldValue = newValue;
			OnPropertyChanged(propertyName);
			return true;
		}

		return false;
	}
	protected virtual void Dispose(bool disposing)
	{
	}
	public void Dispose()
	{
		Dispose( true );
	}
	
}

Und nun die beiden klassen


public class EinzelnesElemente : BaseViewModel
{
	private int m_BasisStatus;
	// Konstruktor
	public EinzelnesElemente(Basisklasse basisKlasse)
	{
		basisKlasse?.PropertyChanged += BasisPropertyChanged;
	}
	protected override void Dispose(bool disposing)
	{
		basisKlasse?.PropertyChanged -= BasisPropertyChanged;
	}

	protected void BasisPropertyChanged(object sender, PropertyChangedEventArgs e)
	{
		if( e.PropertyName == "Status")
		{
			m_BasisStatus = (sender as Basisklasse).Status;
		}
	}

	public string Name 
	{ 
		get { return m_Name;}
		set { SetProperty<string>(m_Name,value); }
	} string m_Name;

	public string StatusAbfragen()
	{
		return Convert.ToString(m_BasisStatus);
	}
}
public class Basisklasse : BaseViewModel
{
	public ObservableCollection<EinzelnesElemente> ListeElemente = new ObservableCollection<EinzelnesElemente>();

	public int Status
	{
		get { return m_Status;}
		set { SetProperty<int>(ref m_Status, value);}
	} int m_Status;

	public void ElementHinzufuegen(ref int status)
	{
		ListeElemente.Add(new EinzelnesElemente(this));
	}
}

F
fritziiiii Themenstarter:in
7 Beiträge seit 2014
vor 6 Jahren

Hi FZelle,

danke für deine Antwort. Werde vermutlich jetzt erstmal eine gewisse Zeit brauchen, um das Aufzuarbeiten, was du mir präsentierst.

Aber zwei Sachen würd ich noch gerne loswerden:
Thema INotifyPropertyChanged: Die verwendete Version hab ich aus meinen C#-Buch (= Identisch mit dem Rheinwerk Openbook Kapitel 21.2, einfache Datenbindung) Das kann doch nicht sooo falsch sein, oder?

Thema 2:
Bitte ettwas Geduld mit den "Amateuren". Ich bin durchaus bemühlt, mein Wissen in C# zu vertiefen, glaube auch, mit meinem Projekt durchaus einiges gelernt zu haben, aber man stößt als Laie eben immer wieder an Probleme wo man selbst nicht weiterkommt und man Hilfe braucht, weil man gar nicht weis in welche Richtung man suchen muß

T
461 Beiträge seit 2013
vor 6 Jahren

Zur Ergänzung hier nun mein Lösungsvorschlag: (Angelehnt an @FZelle's BaseViewModel )


public class EinzelnesElemente : BaseViewModel
{
    // Konstruktor
    public EinzelnesElemente()
    {
    }

    // Gebundene Eigenschaft(en)
    public string Name
    {
        get { return m_Name;}
        set { SetProperty<string>(m_Name,value); }
    } string m_Name;

    
    public string StatusAbfragen()
    {
        if(this.HoleAktuellenStatusEvent == null) return null;

        HoleAktuellenStatusEventArgs args = new HoleAktuellenStatusEventArgs();
        this.HoleAktuellenStatusEvent(this, args);  

        return args.AktuellerStatus.ToString();
    }

    public event EventHandler<HoleAktuellenStatusEventArgs> HoleAktuellenStatusEvent;
}

public class Basisklasse : BaseViewModel
{
    public ObservableCollection<EinzelnesElemente> ListeElemente = new ObservableCollection<EinzelnesElemente>();

    public int Status
    {
        get { return m_Status;}
        set { SetProperty<int>(ref m_Status, value);}
    } int m_Status;

    public void ElementHinzufuegen(ref int status)
    {
        EinzelnesElemente einElement = new EinzelnesElemente();
        einElement.HoleAktuellenStatusEvent += this.EinzelnesElemente_HoleAktuellenStatusEvent;
        this.ListeElemente.Add(einElement);
    }

    // Event(s)
    private void EinzelnesElemente_HoleAktuellenStatusEvent(object sender, HoleAktuellenStatusEventArgs args)
    {
        args.AktuellerStatus = this.Status;
    }
}

public class HoleAktuellenStatusEventArgs : EventArgs
{
    public HoleAktuellenStatusEvent()
    {
        this.AktuellerStatus = -1;
    }

    public int AktuellerStatus {get; set;}
}

Der Sinn dahinter ist der, daß die Klasse EinzelnesElemente unabhängig von Basisklasse ist und wieder woanders verwendet werden könnte.

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

1.040 Beiträge seit 2007
vor 6 Jahren

Also ich habe in meiner 12-jährigen Entwicklerkarriere noch nie ein Event gesehen, dass etwas abfragt und dessen EventArgs nach dem Auslösen des Events geändert werden.
Davon ab, jeder, der die Instanz des EinzelnesElemente kennt, kann sich auf das Event hängen und dann den Status ändern.

Sowas setzt doch hoffentlich niemand um?! 8o

Wir kennen zwar nicht den genauen Anwendungsfall von fritziiiii, in den meisten Fällen ist jedoch eine Abhängigkeit des Child vom Parent nicht weiter tragisch.

709 Beiträge seit 2008
vor 6 Jahren

EventArgs, die geändert werden, hast du bestimmt schon bei RoutedEvents oder dem Closing-Ereignis eines Fensters gesehen.

1.040 Beiträge seit 2007
vor 6 Jahren

Stimmt.

Sauber ist das Ganze mMn trotzdem nicht. 👅

1.029 Beiträge seit 2010
vor 6 Jahren

Hi,

naja - passt wirklich nicht so toll auf den Anwendungsfall - aber gerade in Bezug auf EventArgs kenne ich einige, die z.B. so funktioneren, dass man EventArgs.Handled = true; setzt, damit sonst keiner mehr was macht. So unüblich ist das nicht - wenn ich mich recht entsinne war das z.B. bei System.Windows.Forms irgendwo so...

LG

T
461 Beiträge seit 2013
vor 6 Jahren

Sauber ist das Ganze mMn trotzdem nicht. 👅

Finde ich in Ordnung.

Nur wie sonst schafft man eine saubere Trennung?

Davon ab, jeder, der die Instanz des EinzelnesElemente kennt, kann sich auf das Event hängen und dann den Status ändern.

Das ist ja auch Sinn und Zweck des Ganzen, hatte ich ja extra beschrieben.

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

3.170 Beiträge seit 2006
vor 6 Jahren

Hallo,

Nur wie sonst schafft man eine saubere Trennung?

Könnte man z.B. auch über ein Interface lösen:

interface IStatusHolder
{
    int Status { get; }
}

class Basisklasse: IStatusHolder
{
...
}

class EinzelnesElemente
{
    private IStatusHolder StatusHolder { get; set; }
    EinzelnesElemente(IStatusholder statusHolder)
    {
        StatusHolder = statusHolder;
    }

    public string StatusAbfragen()
    {
        return Convert.ToString(StatusHolder.Status);
    }
}

Dann ist es vollkommen egal, ob der Status von Basisklasse osder sonst irgendwoher kommt. Eine feste Kopplung gibt es nur noch zum Interface.
Und dann kann man sich überlegen, ob IStatusHolder bereits auch ein INotifyPropertyChanged ist, um sich - wie von FZelle vorgeschlagen - über Änderungen benachrichtigen zu lassen.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

T
461 Beiträge seit 2013
vor 6 Jahren

Klar, Interface, hätt ich gleich selbst drauf kommen können. Danke!

Und dann kann man sich überlegen, ob IStatusHolder bereits auch ein INotifyPropertyChanged ist, um sich - wie von FZelle vorgeschlagen - über Änderungen benachrichtigen zu lassen.

Das mit dem INotifyPropertyChanged wäre mir wieder zu spezifisch, da ist das mit einem eigenen Interface meiner Meinung nach günstiger... Außer das eigene Interface implemetiert INotifyPropertyChanged aber das würde zu weit führen, ohne einem Konzept wäre das nur ein durcheinander...

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄