none
Zugriffsprobleme - wo instanziieren damit GUI Elemente Methoden und Felder nutzen können? RRS feed

  • Frage

  • Hallo zusammen,

    seit einigen Monaten arbeite ich mich in C# ein und konnte schon einige Erfolge bei meinen Testprojekten verzeichnen.

    Aber vor einer Herausforderung stehe ich seit Beginn und schnall es einfach nicht.

    KURZ: Ich möchte ein Objekt erstellen (new), dessen Felder (alle public) als Datengrundlage für eine Methode (public) aus dieser Objekt-Klasse (ebenfalls public) dienen.

    Ein Slider soll dabei immer nur eine der vielen Variablen zur Übergabe an die Methode ersetzen und das Berechnungsergebnis auf einem TextBlock ausgeben. Bisher kann ich aber aus einem Slider_ValueChanged Event dieses (vorher woanders) erzeugte Objekt und seine Methoden nicht ansprechen.

    Mir fehlt dieser eine springende Punkt, an welchem Ort muss ich die Instanz erstellen und mit den Benutzerdaten füllen, damit genau dieses Objekt und seine Methode(n) von anderen EventHandlern (hier z.B. Slider) aufgerufen werden kann.

    LANG:

    Ich habe eine Klasse für ein Bausparkonto geschrieben (public) und dieser eine komplexe Berechnungsmethode für die Ermittlung der Verfügbarkeit hinzugefügt.

    public class Bausparkonto
        {
            
            //öff Eigenschaften
            
            public int SaSu { get ; set; }
            public int BSS { get; set; }
            public string Tarif { get; set; }
            public int TB { get; set; }
            public int SB { get; set; }
            public double Saldo { get; set; }
            private double BD;
    
    }

    Hier die erste Zeile der Berechnungsmethode:

    public string berechne_Zuteilung(int Bausparsumme, double Guthaben, string Tarif, int Sparbeitrag, int Tilgungsbeitrag, double Bewertungsdarlehen, int Saldensumme)
    {}

    Die Oberfläche in XAML umfasst verschiedene Eingabefelder um die entsprechenden Variablen an die Methode weiterzugeben.

    Bisher:

    Mit einem Button_Click-Event wird über

    private void Intitialize_Click(object sender, RoutedEventArgs e)
                {
        
                    Bausparkonto BSV = new Bausparkonto();
                    
                    BSV.BSS = Convert.ToInt32(Eingabe_BSS.Text);
                    BSV.SB = Convert.ToInt32(Eingabe_SB.Text);
                    BSV.TB = Convert.ToInt32(Eingabe_TB.Text);
                    BSV.Saldo = Convert.ToDouble(Eingabe_Saldo.Text);
                    BSV.Tarif = Convert.ToString(Auswahl_Tarif.SelectedValue);
                    BSV.SaSu = 0;
                    Ergebnis.Text = BSV.berechne_Zuteilung(BSV.BSS, BSV.Saldo, BSV.Tarif, BSV.SB, BSV.TB, BSV.BSS - BSV.Saldo, BSV.SaSu);
            }

    alles korrekt berechnet und an den TextBlock "Ergebnis" ausgegeben - soweit so gut.

    PROBLEM:

    Ich möchte allerdings, dass der Button wegfällt und über äquivalente Slider, die veränderte Werte zur Berechnung an die Methode schicken und das Ergebnis wieder im TextBlock landet.

    Wenn ich das ganze über das Slider_ValueChanged - Event versuche, ist der Name des Objekts natürlich nicht bekannt, weil es ja woanders erstellt wurde.

    WO ALSO - muss ich das Objekt instanziieren, dass es und seine Methoden auch innerhalb dieser private Ereignishandler wie z.B. Slider_ValueChanged bearbeitet bzw. abgerufen werden kann?

    Ich hoffe ich konnte mein Problem ausreichend beschreiben und heisse jede Hilfe auch mit Links und Dokus willkommen.

    Danke!

    Marco




    • Bearbeitet Marco Sagerer Dienstag, 18. April 2017 10:27 Korrektur Code
    Dienstag, 18. April 2017 10:19

Antworten

  • Hi,

    eventuell so:

    public partial class MainWindow : Window
        {
            Bausparkonto BSV;
    
            public MainWindow()
            {
                InitializeComponent();
    
                BSV = new Bausparkonto();
            }
    
            private void Intitialize_Click(object sender, RoutedEventArgs e)
            {
    
                Bausparkonto BSV = new Bausparkonto();
    
                BSV.BSS = Convert.ToInt32(Eingabe_BSS.Text);
                BSV.SB = Convert.ToInt32(Eingabe_SB.Text);
                BSV.TB = Convert.ToInt32(Eingabe_TB.Text);
                BSV.Saldo = Convert.ToDouble(Eingabe_Saldo.Text);
                BSV.Tarif = Convert.ToString(Auswahl_Tarif.SelectedValue);
                BSV.SaSu = 0;
                Ergebnis.Text = BSV.berechne_Zuteilung(BSV.BSS, BSV.Saldo, BSV.Tarif, BSV.SB, BSV.TB, BSV.BSS - BSV.Saldo, BSV.SaSu);
            }
    
        }
    
        public class Bausparkonto
        {
    
            //öff Eigenschaften
    
            public int SaSu { get; set; }
            public int BSS { get; set; }
            public string Tarif { get; set; }
            public int TB { get; set; }
            public int SB { get; set; }
            public double Saldo { get; set; }
            private double BD;
    
        }

    Gruß

    Stefan


    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP

    • Als Antwort markiert Marco Sagerer Dienstag, 18. April 2017 17:11
    Dienstag, 18. April 2017 11:07

Alle Antworten

  • Hi,

    eventuell so:

    public partial class MainWindow : Window
        {
            Bausparkonto BSV;
    
            public MainWindow()
            {
                InitializeComponent();
    
                BSV = new Bausparkonto();
            }
    
            private void Intitialize_Click(object sender, RoutedEventArgs e)
            {
    
                Bausparkonto BSV = new Bausparkonto();
    
                BSV.BSS = Convert.ToInt32(Eingabe_BSS.Text);
                BSV.SB = Convert.ToInt32(Eingabe_SB.Text);
                BSV.TB = Convert.ToInt32(Eingabe_TB.Text);
                BSV.Saldo = Convert.ToDouble(Eingabe_Saldo.Text);
                BSV.Tarif = Convert.ToString(Auswahl_Tarif.SelectedValue);
                BSV.SaSu = 0;
                Ergebnis.Text = BSV.berechne_Zuteilung(BSV.BSS, BSV.Saldo, BSV.Tarif, BSV.SB, BSV.TB, BSV.BSS - BSV.Saldo, BSV.SaSu);
            }
    
        }
    
        public class Bausparkonto
        {
    
            //öff Eigenschaften
    
            public int SaSu { get; set; }
            public int BSS { get; set; }
            public string Tarif { get; set; }
            public int TB { get; set; }
            public int SB { get; set; }
            public double Saldo { get; set; }
            private double BD;
    
        }

    Gruß

    Stefan


    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP

    • Als Antwort markiert Marco Sagerer Dienstag, 18. April 2017 17:11
    Dienstag, 18. April 2017 11:07
  • Hallo Marco Sagerer,

    das Problem lässt sich lösen indem Du dir Gedanken über eine Architektur für deine Anwendung machst, dort scheint bisher nichts vorhanden zu sein. Zugriffsprobleme liegen oft an schlechter oder gar nicht vorhandener Architektur.

    Bei dem was Du in deinem Quelltextbeispiel zeigst erschließt sich mir nicht, wozu Du überhaupt eine Klasse hast nach der Du ein Objekt anlegst, deren Variablen (bzw. hier Felder) du befüllst um anschließend diese Variablen nicht als solche zu nutzen, sondern diese in eine Methode übergibst, die nur eine Berechnung durchführt ohne auch nur im geringsten etwas am instanzierten Objekt zu ändern.

    Du könntest deine Berechnung auch einfach in der Klasse zu deinem Fenster unterbringen (die Felder sind eh Überflüssig), damit wären deine Zugriffsprobleme schnell gelöst.


    - Gruß Florian

    Dienstag, 18. April 2017 12:33
  • Hi Florian,

    vielen Dank für deine Hinweise und Vorschläge.

    Meinst du mit "Architektur" das Thema 3-Schichten-Modell oder grundsätzlich die Verteilung von Aufgaben/Methoden/Variablen im Code? Ich bin um jede Aufklärung, Links oder spannende Tutorials sehr dankbar. :)

    Wenn du auch einen kleinen Satz zu möglichen "Veränderungen" eines Objektes möchtest, sehr gern.

    Ich hatte mich zuvor mit dem Thema an ein paar Konsolen Anwendungen versucht und scheiterte daran, dass irgendwann so viele Methoden unabhängig voneinander angesprochen werden mussten und der Verlauf so linear sein musste. Zudem hab ich mich dann in das Thema eingelesen und festgestellt, dass in der OOP im Prinzip alles über Klassen und Objekte funktionieren sollte.

    Da ich im späteren Verlauf noch andere "Tarife" und Eigenschaften meines Rechners benötige, welche unterschiedlich zu handhaben sind, müsste ich dann die Methoden einzeln anpassen oder neu schreiben. Die Wiederverwendbarkeit und Erweiterbarkeit einer entsprechenden Klasse mit öffentlichen und privaten Methoden, fand ich hier sehr reizvoll.

    Gibt es denn noch eine andere oder bessere Art und Weise außerhalb einer Klasse, die meine Bedürfnisse für die weitere Entwicklung erfüllen und vielleicht sogar einfacher sind? (siehe vorhergehender Absatz)

    Vielen Dank für deine Hilfe und Grüße

    Marco

    Dienstag, 18. April 2017 17:51
  • Hallo Stefan,

    danke für deine Hilfe, damit komme ich endlich ein gutes Stück weiter.

    Ich verstehe gerne die Thematik dahinter. Kannst du mir sagen, wie du auf diese Vorgehensweise gekommen bist und mit welchem Thema ich mich da genau beschäftigen sollte um besser zu werden? Irgendwie fand das in all meinen Recherchen bisher keine Anwendung. Ich hatte vieles probiert, z.B. in oder vor der Main-Methode:

    Bausparkonto BSV = new Bausparkonto();
    Aber die Aufteilung wie du sie jetzt gebracht hast, kann ich noch nicht nachvollziehen.

    Vielen lieben Dank für deine schnelle Hilfe.

    Grüße

    Marco

    Dienstag, 18. April 2017 17:54
  • Hallo Marco,

    ich meine grundlegende Prinzipien der Objektorientierten Softwareentwicklung.
    Einige Skripte dazu findest Du hier: http://www.pisoftware.de/kostenlose-downloads.aspx
    In der gut sortieren Bibliothek findet sich zum Thema zum Beispiel:
    Object-Oriented Design (Booch)
    Analyse und Design  mit UML 2 (Oestereich)

    Die Trennung von Geschäftslogik und Benutzeroberfläche (oder auch das 3-Schichten-Modell) ist insbesondere in Hinsicht auf spätere Erweiterung sehr hilfreich.
    Um auf deinen Code zurück zu kommen: So gehört in deinem Beispiel die Position der Slider zu den Eigenschaften deines Fensters, der Wert den diese wiederspiegeln ist aber eine Eigenschaft deines Bausparkonto Objektes, über das Hinaus haben beide zunächst nichts miteinander zu tun. Es ist also in der benötigten Architektur nicht nötig das dein Fenster ein Bausparkonto kennt, noch ist es nötig das dein Bausparkonto ein Fenster kennt. Diese Entkopplung kannst Du über zum Beispiel über eine Schnittstelle erreichen, die beiden Klassen bekannt ist - so kann dein Fenster später auch für andere Konten genutzt werden, welche die gleiche Schnittstelle bedienen, aber eine andere Geschäftslogik beinhalten. Beispiele findest Du in den vorgenannten Quellen.

    Zu den Veränderungen des Objektes. Du nutzt in deinem Code Felder, die befüllt aber nicht genutzt werden - konkret: Die Übergabe der dem Objekt bereits bekannten Felder an deine Methode als Parameter ist völlig unnötig oder aber deine Felder sind völlig unnötig - das ist eine Designentscheidung ob auf die Felder oder die Parameter verzichtet werden sollte (ob das eine oder andere Besser ist, kann ich aufgrund der mir bekannten Informationen nicht sagen). Falls die Felder über die Nutzung in den Parametern keinen weiteren nutzen haben, tippe ich drauf, dass eine statische Klasse ohne Möglichkeit eigener Instanzierung sinnvoller ist.

    Als Beispiele:
    Die Berechnung könnte dann so aussehen:

    private void neueBerechnungswerte( )	// Aufruf durch Eventhandler (Slider geändert oder Andere die Werte ändern könnten?)
    {
     Ergebnis.Text = konto.berechne_Zuteilung(BSS, Saldo, Tarif, SB, TB, BSS - Saldo, SaSu);
    }

    In den dazugehörigen Eventhandler, zum Beispiel beim Slider für BSS Änderung

     BSS = Convert.ToInt32(Eingabe_BSS.Text);
    Falls es doch ein instanziertes Objekt sein soll
    private void neueBerechnungswerte( )
    {
     // Die Felder sind dem Objekt bekannt -> müssen nicht übergeben werden
     Ergebnis.Text = konto.berechne_Zuteilung();
    }
    Und hier in dem dazugehörigen Eventhandlern (ebenfalls Änderung für BSS)

     konto.BSS = Convert.ToInt32(Eingabe_BSS.Text);

    Das nur zur Orientierung, im Detail wirst Du sicher zu etwas (vielleicht ähnlichem) anderem kommen.


    - Gruß Florian

    Mittwoch, 19. April 2017 07:32
  • Hallo Florian,

    wow, vielen Dank, dass du dir soviel Zeit nimmst und deine Überlegungen und Erfahrungen ausführst. Ich glaube ich verstehe auch langsam was du meinst. Ich werde mir deine Lektüre-Empfehlung zu Herzen und das Neudesign des Codes in Angriff nehmen.

    Nochmals: vielen Dank! und alles Gute!

    Marco

    Mittwoch, 19. April 2017 08:26