Stellvertretende Anfrage für einen vollständigen Anfängerguide



  • Hallo Liebe Profis,

    ich gehöre zu dem Haufen Anfänger, die immer wieder auf dasselbe Problem stoßen:

    Variablen bzw. den Status bestimmter Objekte von einem Form in eine andere übernehmen.

    Ich will dazu sagen, ich bin nicht zu faul mir bestimmte Dinge anzulesen und auszuprobieren. Ich besitze auch 2 Bücher zu diesem Thema und habe mich mehrfach mit Themen wie Klassen, Vorwärtsdeklaration etc. beschäftigt. Ich komme nach ewigem Ausprobieren und dem auf unvollständige FAQ Einträge verwiesen werden aber einfach zu einem Punkt, an dem ich nicht mehr weiter weiß.

    Was in den FAQ´s bzw. Büchern gut erklärt ist, sind:

    -Öffnen einer zweiten Form von der ersten
    -Erstellung einer verwalteten Klasse

    Mein Problem ist aber, dass Form2 quasi mein Optionsmenü darstellt in dem man Wissengebiete für ein Quiz für die vers. Spieler per checkbox aktivieren bzw. deaktivieren kann. Bei anderen Leuten ist es evtl. eine Namenseingabe oder eine Zahl, die irgendwo über die gesamte Zeit gespeichert werden soll. Was aber im Prinzip alles auf dasselbe hinausläuft.

    Mein erster Versuch war die Benutzung von globalen "int Variablen" in denen ich z.B. den Status eines Häkchens speichern konnte z.B. player1erkunde=1 als aktiviert. Das funktioniert auch, solange man keine strings benötigt wie z.b. einen Namen der in den Optionen gespeichert werden soll, da strings ja irgendwie nicht global deklariert werden können.

    Da man aber überall lesen kann das globale Variablen ja auch nicht empfehlenswert sind habe ich mir gedacht, arbeite ich objektorientiert, erstelle ich mir eine Klasse Optionen. Gesagt getan, Klasse Optionen mit einer Variablen erstellt:

    public ref class optionen
    {
    public:
    int p1erdkunde;
    optionen(void);
    };
    

    um dann mittels

    private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e)
    {optionen ^optmenu=gcnew optionen;optmenu->p1erdkunde=1;}
    

    eine entsprechende Instanz namens optmenu davon zu erstellen, die für den Spieler 1 festlegt, das Fragen aus dem entsprechenden Fachbereich beim Aufruf von Form2(Optionsmenü) schon aktiviert sind.
    Leider musste ich feststellen, dass die Instanz optmenu anscheinend schon nach ausführen des Ereignisses wieder gelöscht wird. Was mich nach ewigen ausprobieren zu der Frage führte:

    Wie erstelle ich eine permanente Instanz?
    Ist das überhaupt möglich?
    Ist das noch eine Instanz?
    Wo erstelle ich sie?

    Denn auch wenn dann in der Form2 die checkbox aktiviert wird, soll wiederrum optmenu->p1erdkunde=0 gesetzt werden.

    Nachdem was ich als Newb alles gelesen hatte, kann man auf eine Klasse die public ist von überall zugreifen, was ja den Kreis zwischen den beiden Forms schliessen sollte. Da aber auch schon die beiden Forms schon Instanzen sind, hätte ich mir wenn dies funktionieren würde den Umweg über die optionen klasse auch sparen können.

    Ich hoffe ihr könnt mein Dilemma und das wie ich feststellen musste vieler anderer Anfänger verstehn.

    Vielleicht ist jemand so nett einen vollständigen Code für folgendes Beispiel zu posten, aus dem so denke ich jeder die passende Variante für sein Problem ableiten könnte.

    Aus Form1 wird Form2 geöffnet, welches eine Richtextbox enthält in die ein Name eingegeben wird. Dieser Name soll bei jedem erneuten öffnen von Form2 dort wieder erscheinen und von Form1 hat man nach dem Schliessen von Form2 in Form einer string Variablen darauf Zugriff.

    Ich hoffe das ist nicht zuviel verlangt und ich danke euch schonmal für das Lesen meines zu langen Textes. Evtl. könnt ihr meine Denkweise nachvollziehn und mir auf Normal Deutsch erklären warum die in die Tonne gehört 🙂

    Lieben Gruss Markus



  • Hallo,

    Das Problem haben voll viele und ich verstehe nicht wieso, da die Logik hinter der Lösung immer wieder die gleiche ist und die ist trivial.

    Als erstes muss man sicht bewusst werden was man programmiert. C++/CLI mit dem .Net Framework also benutzt man auch .Net Funktionalität. Sprich, keine globalen Variablen, dafür sind aber Properties nen gutes Stichwort.

    Dann zur Form die geöffnet werden soll. Die implementiert einfach ein öffentliches Property und die Einstellungen die der User macht werden dort gespeichert. Das ist bei Settings sinnvollerweise natürlich ne eigene Klasse, die die einzelnen Einstellungen kapselt. Mehr muss die gar nicht machen.

    Zur anderen Form die die Einstellungsform öffnen soll. Da wird einfach als Klassenvariable so nen Settingsobjekt angelegt. Dann hat man irgendwo nen Eventhandler der z.B. von nem Button Klick ausgelöst wird, und wo dann die Settingsform geöffnet wird. Dann macht man einfach sowas (Achtung bissle Pseudocode):

    Settingsform_Öffnen_Eventhandler {
      Settingsform f = new Settingsform();
      f.Settings = m_settings // Settings ist das öffentliche Property der Settingsform, m_settings die Klassenvariable für die Settings
      f.Show();
    }
    

    Und das wars. Alle Änderungen die man in der Settingsform an den Settings vornimmt, werden automatisch an die Klassenvariable durchgereicht und man hat damit die aktuellen Einstellungen von der Form2 auf die Form1 bekommen. Das ist natürlich der einfachste Fall wo sofort alles übernommen wird, wenn man sowas wie übernehmen/abbrechen möchte, muss man ein zwei Zeilen mehr schreiben. Prinzip ist aber genau das gleiche.

    Diese Erklärung ist übrigens nicht anders als die anderen zig hundert Erklärungen die man zu dem Thema findet... Weiß nicht was man hätte anders erklären sollen.



  • Vielen Dank für deine Antwort, den Teil habe ich so schon verwendet um den Settingsstatus aus globalen Variablen zu übernehmen. Mein Problem ist es aber, eine Klasse zu erzeugen, die permanent erhalten bleibt um anstelle der globalen Variablen ihren Wert an den Settingsstatus zu übergeben.
    Wie schon beschrieben bleiben meine Klasseninstanzen irgendwie auf der Strecke.

    Ist wahrscheinlich wirklich total easy aber anscheinend die klassische Brett vorm Kopf Stelle über die man nacher lachen kann 😃

    Gruss Markus



  • Wie gesagt, vergiss globale Variablen und verwendet .Net Datentypen, sonst kriegst du vorne und hinten Probleme zwischen managed und unmanaged Code.

    Wenn du ein Objekt einer Klasse (die natürlich ein Referenztyp sein muss) als Instanzvariable anlegst (sorry das ist mein Fehler, meine natürlich auch im vorherigen Beitrag Instanzvariablen, keine Klassenvariablen), dann lebt die Instanz solang wie das Objekt lebt wo sie erstellt wurde, sprich, in deinem Fall solange wie auch die Hauptform geöffnet ist. Die verschwindet net, dann läuft irgendwas falsch. Kannst ja mal bissle Code posten wie dus bisher machst.



  • Also bisher mache ich es so wie oben beschrieben. Beim laden von meiner Hauptform(Form1) erstelle ich von der Klasse Optionen eine Instanz.

    Klasse Optionen mit Hinzufügen erstellt:

    public ref class optionen 
    { 
    public: 
    int p1erdkunde; 
    optionen(void); 
    };
    

    Instanzerstellung in Form1:

    private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) 
    {optionen ^optmenu=gcnew optionen;optmenu->p1erdkunde=1;}
    

    Das Problem: Nur innerhalb dieser geschweiften Klammern funktioniert die Zuweisung "optmenu->p1erdkunde=1;" Wenn ich noch einen Button hinzufüge:

    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e)
    {menu->p1status=3;}
    

    wird die fehlermeldung menu: nicht deklarierter Bezeichner ausgegeben, was für mich heisst die Instanz menu gibt es schon nichtmehr.



  • unten natürlich auch optmenu statt nur menu

    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) 
    {optmenu->p1status=3;}
    


  • Args ist schon spät p1status unten ist natürlich p1erdkunde......



  • Du erstellst die Instanz nicht als Instanzvariable der Klasse, sondern einfach nur im Eventhandler, weil erst dort erstellst du die Referenz optmenu. Das du im Form_Load erst mit gcnew die Instanz erstellst ist okay, aber die Referenz dafür musst du natürlich bei allen anderen Instanzvariablen ablegen, da sie sonst nur im Sichtbarkeitsbereich des Eventhandler gültig ist.

    Auch wenn ich blöderweise vielleicht zur Verwirrung beigetragen hab durch den ersten Post, aber ist dir der Unterschied klar zwischen Instanz- und Klassenvariablen? Und auch was nen Sichtbarkeitsbereich ist?

    p1erdkunde und was es sonst noch in deiner Settingsklasse gibt an öffentlichen Variablen, sollten besser Properties sein.



  • Danke für deine Mühe ich glaub langsam dämmert mir mein Fehler ^^ ich hab mir nochmal intensivst das Kapitel mit den Klassen bzw. Instanzvariablen durchgelesen und hab da wohl einiges verwechselt. Das ist das Problem dieser relativ trockenen Literatur, man liest es glaub es verstanden zu haben, aber in der Praxis sieht es dann trotz den gegebenen Beispielen häufig noch etwas anders aus 😛

    Gruss Markus



  • Ich habe jetzt die FAQ, diverse Bücher und Internetquellen durchgearbeitet, ewig ausprobiert aber ich bekomme es nicht hin.

    Hab mich vor allem am Thread Variabeln übertragen (Zugriff von Form1 auf Form2)? aus den FAQs langgehangelt, aber egal was ich im Konstruktor von Form1 auch für Propertys erstelle, es funktioniert nicht. In Form2 kennt er Form1 aus irgendwelchen Gründen nicht, obwohl Form2 ja von Form1 aus aufgerufen wird.

    Hab jetzt mal in der einfachsten Variante einfach den String Meintext in den automatisch erstellen Form1 Code geschrieben, ähnlich wie in den FAQs. In Form1 lässt sich dieser auch Prima mittels

    Form1::MeinText="irgendwas";
    

    verändern. Aber aus Form2 heraus kennt er nicht mal Form1 geschweige denn Form1::MeinText. Meine Frage ist jetzt, wie mache ich Form1 in Form2 bekannt? Scheint ja irgendwas mit Vorwärtsdeklaration zu tun haben, aber was das wird mir einfach nicht klar.

    Bitte gebt mir ein Beispiel wo in Form1 ich den String deklarieren muss und mit welchem Befehl in Form2 ich dann darauf zugreifen kann.

    Das ist die einfachste Variante, die natürlich absoluter Mist zu sein scheint.

    [cli]public ref class Form1 : public System::Windows::Forms::Form
    {
    public: String ^MeinText;
    Form1(void)
    {
    InitializeComponent();
    //
    //TODO: Konstruktorcode hier hinzufügen.
    //
    }[/cli]



  • Lege bitte mal ein Projekt mit dem Namen "TstBsp" an...
    Füge einen Button auf Form1 hinzu, durch einen Doppelklick auf den Button legst Du das Click_Event an.
    Dieses Click_Event verlagerst Du in die TstBsp.cpp Datei.
    Dann füge eine Form2 hinzu. Das gleich mit dem Button wie in Form1, nur das Click_Event verlagerst Du in die Form2.cpp Datei.
    Der Code ist unten aufgelistet.
    Ich habe den ganzen Code eingefügt, so kannst Du das besser nachvollziehen.

    Form1.h

    #pragma once
    
    #include "Form2.h"
    
    namespace TstBsp {
    
    	using namespace System;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    
    	/// <summary>
    	/// Zusammenfassung für Form1
    	///
    	/// Warnung: Wenn Sie den Namen dieser Klasse ändern, müssen Sie auch
    	///          die Ressourcendateiname-Eigenschaft für das Tool zur Kompilierung verwalteter Ressourcen ändern,
    	///          das allen RESX-Dateien zugewiesen ist, von denen diese Klasse abhängt.
    	///          Anderenfalls können die Designer nicht korrekt mit den lokalisierten Ressourcen
    	///          arbeiten, die diesem Formular zugewiesen sind.
    	/// </summary>
    	public ref class Form1 : public System::Windows::Forms::Form
    	{
    	private:
    		String^ _bspText;//Variable vereinbaren:
    
    	public:
    		Form1(void)
    		{
    			InitializeComponent();
    			//
    			//TODO: Konstruktorcode hier hinzufügen.
    			//
    			_bspText = String::Empty;//Varaible initialisieren:
    		}
    
    	protected:
    		/// <summary>
    		/// Verwendete Ressourcen bereinigen.
    		/// </summary>
    		~Form1()
    		{
    			if (components)
    			{
    				delete components;
    			}
    		}
    	private: System::Windows::Forms::Button^  button1;
    	protected: 
    
    	private:
    		/// <summary>
    		/// Erforderliche Designervariable.
    		/// </summary>
    		System::ComponentModel::Container ^components;
    
    #pragma region Windows Form Designer generated code
    		/// <summary>
    		/// Erforderliche Methode für die Designerunterstützung.
    		/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
    		/// </summary>
    		void InitializeComponent(void)
    		{
    			this->button1 = (gcnew System::Windows::Forms::Button());
    			this->SuspendLayout();
    			// 
    			// button1
    			// 
    			this->button1->Location = System::Drawing::Point(264, 111);
    			this->button1->Name = L"button1";
    			this->button1->Size = System::Drawing::Size(75, 23);
    			this->button1->TabIndex = 0;
    			this->button1->Text = L"button1";
    			this->button1->UseVisualStyleBackColor = true;
    			this->button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click);
    			// 
    			// Form1
    			// 
    			this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
    			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
    			this->ClientSize = System::Drawing::Size(443, 353);
    			this->Controls->Add(this->button1);
    			this->Name = L"Form1";
    			this->Text = L"Form1";
    			this->ResumeLayout(false);
    
    		}
    #pragma endregion
    //
    //Button1 Click in Form1.h
    //
    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e);
    
    //
    //Property:
    //
    public: property String^ _propBspText
        {
            String^ get()
            {
                return _bspText;
            }
    
            void set(String^ _wert)
            {
                _bspText = _wert;
            }
    	}	
    };
    }
    

    TstBsp.cpp

    // TstBsp.cpp: Hauptprojektdatei.
    
    #include "stdafx.h"
    #include "Form1.h"
    
    using namespace TstBsp;
    
    [STAThreadAttribute]
    int main(array<System::String ^> ^args)
    {
    	// Aktivieren visueller Effekte von Windows XP, bevor Steuerelemente erstellt werden
    	Application::EnableVisualStyles();
    	Application::SetCompatibleTextRenderingDefault(false); 
    
    	// Hauptfenster erstellen und ausführen
    	Application::Run(gcnew Form1());
    	return 0;
    }
    
    //
    //Button1 Click in TstBsp.cpp
    //
    System::Void Form1::button1_Click(System::Object^  sender, System::EventArgs^  e) 
    			 {
    				 _propBspText::set("Hallo");
    				 MessageBox::Show(_propBspText::get());
    
    				 Form2^ _form2 = gcnew Form2();
    				 _form2->ShowDialog();
    			 }
    

    Form2.h

    #pragma once
    #include "Form1.h"
    
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    
    namespace TstBsp {
    
    	/// <summary>
    	/// Zusammenfassung für Form2
    	///
    	/// Warnung: Wenn Sie den Namen dieser Klasse ändern, müssen Sie auch
    	///          die Ressourcendateiname-Eigenschaft für das Tool zur Kompilierung verwalteter Ressourcen ändern,
    	///          das allen RESX-Dateien zugewiesen ist, von denen diese Klasse abhängt.
    	///          Anderenfalls können die Designer nicht korrekt mit den lokalisierten Ressourcen
    	///          arbeiten, die diesem Formular zugewiesen sind.
    	/// </summary>
    	public ref class Form2 : public System::Windows::Forms::Form
    	{
    	public:
    		Form2(void)
    		{
    			InitializeComponent();
    			//
    			//TODO: Konstruktorcode hier hinzufügen.
    			//
    		}
    
    	protected:
    		/// <summary>
    		/// Verwendete Ressourcen bereinigen.
    		/// </summary>
    		~Form2()
    		{
    			if (components)
    			{
    				delete components;
    			}
    		}
    	private: System::Windows::Forms::Button^  button1;
    	protected: 
    
    	private:
    		/// <summary>
    		/// Erforderliche Designervariable.
    		/// </summary>
    		System::ComponentModel::Container ^components;
    
    #pragma region Windows Form Designer generated code
    		/// <summary>
    		/// Erforderliche Methode für die Designerunterstützung.
    		/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
    		/// </summary>
    		void InitializeComponent(void)
    		{
    			this->button1 = (gcnew System::Windows::Forms::Button());
    			this->SuspendLayout();
    			// 
    			// button1
    			// 
    			this->button1->Location = System::Drawing::Point(126, 85);
    			this->button1->Name = L"button1";
    			this->button1->Size = System::Drawing::Size(75, 23);
    			this->button1->TabIndex = 0;
    			this->button1->Text = L"button1";
    			this->button1->UseVisualStyleBackColor = true;
    			this->button1->Click += gcnew System::EventHandler(this, &Form2::button1_Click);
    			// 
    			// Form2
    			// 
    			this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
    			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
    			this->ClientSize = System::Drawing::Size(284, 264);
    			this->Controls->Add(this->button1);
    			this->Name = L"Form2";
    			this->Text = L"Form2";
    			this->ResumeLayout(false);
    
    		}
    #pragma endregion
    //
    //Button1 Click in Form2.h
    //
    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e);
    
    	};
    }
    

    Form2.cpp

    #include "StdAfx.h"
    #include "Form2.h"
    
    using namespace TstBsp;
    
    //
    //Button1 Click in Form2.cpp
    //
    System::Void Form2::button1_Click(System::Object^  sender, System::EventArgs^  e) 
    			 {
    				 Form1^ _form1 = gcnew Form1();
    				 _form1->_propBspText::set("Komme aus Form2");
    				 MessageBox::Show(_form1->_propBspText::get());
    			 }
    


  • Vielen vielen Dank für deine Mühe, das sieht doch sehr vollständig aus. Muss es jetzt noch nacharbeiten aber endlich sieht man mal, wo die ganzen Codezeilen genau hingehören. Als Anfänger ist man sonst mit den kurzen Codeabschnitten ohne Zusammenhang oft überfordert.

    Gruss Markus



  • So also ich habe Programm wie besprochen erstellt und es wird fehlerfrei kompiliert und funktioniert auch. Ich kann das was passiert an sich auch gut nachvollziehn.
    Ich habe aber immer noch das Problem, dass ich die auf "Komme aus Form2" gesetzte property nicht in Form1 z.B. in einem label anzeigen lassen kann.

    Ich habe zum testen Form1 ein label und einen zweiten Button hinzugefügt.
    Durch klicken dieses Buttons soll dann der Text der Property (also "Komme aus Form2") im label angezeigt werden. Weder mittels get-Methode noch irgendwie anders lässt sich diese geänderte property anzeigen.

    So wie ich den Code als Unwissender verstehe erstellst du ja in der Form2.cpp durch das klick Event eine neue Instanz von Form1 mit dem Text in der Property. Allerdings kann die alte Instanz von Form1 nicht darauf zugreifen, oder doch?

    Was muss ich im ursprünglichen Form1 im Clickevent schreiben um im Label den Text der Property anzeigen zu lassen?

    Vielen Dank schonmal im Voraus!



  • Ok, jetzt habe ich Dich verstanden...
    Lege ein Label in Form1 an und ändere den folgenden Code...

    Form1.h Änderung in Zeile 8

    // 
    //Property: 
    // 
    public: property String^ _propBspText 
        { 
            String^ get() 
            { 
    			label1->Text = _bspText;
                return _bspText; 
            } 
    
            void set(String^ _wert) 
            { 
                _bspText = _wert; 
            } 
        }
    

    TstBsp.cpp Änderung in Zeile 10

    // 
    //Button1 Click in TstBsp.cpp 
    // 
    System::Void Form1::button1_Click(System::Object^  sender, System::EventArgs^  e) 
                 { 
                     _propBspText::set("Hallo"); 
                     MessageBox::Show(_propBspText::get()); 
    
                     Form2^ _form2 = gcnew Form2(); 
                     _form2->ShowDialog(this); 
                 }
    

    Form2.cpp Änderung in Zeile 6-8

    // 
    //Button1 Click in Form2.cpp 
    // 
    System::Void Form2::button1_Click(System::Object^  sender, System::EventArgs^  e) 
                 {
    				 Form1^ _mainForm = (Form1^)this->Owner;
    				 _mainForm->_propBspText::set("Komme aus Form2"); 
    				 MessageBox::Show(_mainForm->_propBspText::get()); 
                 }
    


  • So ein finales Danke :))))))

    Dank dir kann ich nun endlich String Elemente von einem Form ins andere übertragen. Danke auch nochmal an alle anderen die mir auf dem Weg geholfen haben.

    Hab deinen Code jetzt für meine Wünsche noch verändert und bin vollkommen zufrieden.

    Gruss Markus



  • Für die, die es interessiert hab ich das ganze jetzt in meinen Augen nochmal deutlich vereinfacht:

    Mit dem Klassenmanager verwaltete Klasse anlegen (in meinem Fall heißt sie Optionen)

    und in der Optionen.h einfach entsprechende Variablen hinzufügen:

    #pragma once

    public ref class Optionen
    {
    public: static System::String ^spieler1name="Spieler1";
    	Optionen(void);
    };
    

    nur in Form 2 oben:

    #include "Optionen.h"
    

    Jetzt lässt sich aus Form1 oder Form2 mittels Optionen::spieler1name="dein Text"; einfach der String ändern. Auf Methoden zu verzichten ist zwar nicht schön aber für meinen Fall die einfachste Methode.

    Gruss Markus


Anmelden zum Antworten