Formular soll Wert zurueckgeben
-
ja wie gesagt, bin (noch) nicht so fit...
-
777 schrieb:
Mhh... das wird nicht funktionieren.
Schliesslich kann ich ja von Form2 auch nicht auf globale Werte von Form1 zugreifen...Das müsste aber möglich sein, zumindest wenn du die Variable außerhalb deiner Klasse/Namespace verwaltest und die beiden datein inkludierst.
Auch wenn man globale Variablen wo es nur geht vermeiden sollte.
777 schrieb:
Es muss einen eleganteren Weg geben.
Und so schwer kanns auch nicht sein, da man sowas ja in jedem Programm braucht und sieht...Folgende Möglichkeiten sind mir schon mal über den Weg gelaufen.
Du übergibst deinem zweiten Formular im Konstruktor eine Referenz auf eine Variable von Form1. Bei eins bis einpaar Variablen die übergeben werden ist es noch irgendwie zu verwalten aber wenn es einige (10-...) währen nutzt man Strukturen bzw. Klassen die alle möglichen "Einstellungen" beinhalten. Eine Instanz davon wird in der Form1 (das ist ja auch "nur" eine Klasse" angelegt und beim erzeugen (im Konstruktor) von Form2 übergibst du eine Referenz davon an Form2.
Wenn du jetzt in Form2 der Referenz einen Wert zuweist, ist die natürlich auch in Form1 geändert da es ja der selbe speicherbereich ist auf die gezeigt wird.
Beispiel (ACHTUNG: ungetestet!):
enum Farbe; class Einstellungen; class Form2; //Das ist das Formular 1 class Form1 { public: //Du setzt die Farbe aus den Einstellungen auf Blau, somit hat jetzt m_SchaltflaechenFarbe von der Einstellungsklasse den Wert Blau Form1() { m_Einstellungen.setzeFarbe(Farbe::Blau); } //Beim klicken auf die Schaltfläche "Formular 2 öffnen" erstellst du das Formular 2 mit den Einstellungen in dem Konstruktor als Parameter void clickAufSchaltflaeche() { Form2 ^form = gcnew Form2(m_Einstellungen); form.show(); //wenn du nach jetzt deinem Button sagst das er die Farbe von m_Einstellungen.farbe(); einnehmen soll wird grün sein. } private: Einstellungen m_Einstellungen; }; //Formular 2, bekommt bei der Erstellung im Konstruktor eine Referenz auf die Einstellungen von Formular 1 übergeben. class Form2 { public: Form2(Einstellungen &refAufDieEinstellungen): m_refEinstellungen(refAufDieEinstellungen) { //sonst. Code im Konstruktor } void aendereFarbe() { //hier weißt du der Attribute von den Einstellungen aus Form1 eine neue Farbe zu m_refEinstellungen.setzeFarbe(Farbe::Gruen); } private Einstellungen &m_refEinstellungen; }; //In dieser Klasse kann man beliebig viele Einstellungen verwalten // z. B. noch irgendwelche eingegebene Zahlen etc. class Einstellungen { public: Einstellungen(); void setzeFarbe(const Farbe &neueFarbe) { m_SchaltflaechenFarbe = neueFarbe; } Farbe& farbe() const { return m_SchaltflaechenFarbe; } private: Farbe m_SchaltflaechenFarbe; }; enum Farbe { Blau, Gruen, Gelb };
Ich habe jetzt nicht wirklich Zeit gehabt auf die Syntax besonders das anlegen von Objekten mit ^oder sonst wie zu beachten, ich habe es auch schon seit einiger Zeit nicht mehr mit C++/CLI gemacht. Des wegen bitte ich es zu enschuldigen wenn der Quelltext nicht direkt compilierbar ist.
Ich hoffe einfach das das Prinzip dadurch etwas verständlicher wird.
Gruß
Rudi GPS: Bei Datenbank basierten Projekten, holt auch oft jedes Formular erneut die Einstellungen von der Datenbank und schreib sie beim schließen wieder rein und wenn Form1 (nach schließen von Form2) wieder die Einstellungen holt sind es die aktuellen.
-
Hey Rudi G,
Mir gefaellt dieser Ansatz wirklich gut und als ich deinen Text gelesen habe
ist mir sogar die Idee gekommen gleich eine Referenz des kompletten Formulares an das zweite zu uebergeben.
Allerdings wird das auch wieder nur mit Vorwaetztdeklaration moeglich sein und daher reicht es vermutlich aus einfach eine Struktur mit den Variablen zu erstellen.Ich werde deinen Weg mal testen und eventuell dann auch das Ergebnis noch heir posten.
Bis dann
David
-
Ich persönlich finde es besser, wenn man Klassen nicht so eng aneinander koppelt.
D.h. wenn es ein modaler Dialog ist, dann verpasse der Klasse einfach Getter über die man die Einstellungen abfragen kann, nachdem der User den Dialog geschlossen hat.
Wenn es ein "modeless" Dialog/Fenster ist, dann würde ich dem Ding eher Events verpassen, mittels derer man es irgendwoanders dranknoten kann.
Natürlich lässt sich auch beides kombinieren, also ein Formular welches Events + Getter hat, dann lässt sich dieses sehr flexibel modal oder modeless verwenden.
-
Das mit den Events ist eine Loesungsidee die mir auch schon gekommen ist.
Ich muss an der Stelle aber auch zugeben, dass ich keine Ahnung von den Dingern habe und daher bisher die Finger davon gelassen.
Die Version, die bisher diskutiert wurde funktioniert wunderbar und ich habe hier auch den Quelltext.In diesem Programm wird ein Formular erstellt, welches eine Instanz des zweitesn Formulares erstellt sobald auf einen Button gedrueckt wird.
Das zweite Formular enthaelt einen Konstruktor, der eine Instanz auf das erste Formular entgegen nehmen kann.
Somit uebergibt man den 'This' zeiger an das zweite Formular und kann nun das erste Formular beliebig vom zweiten aus veraendern, da man einen Zeiger auf es zur Verfueung hat.Die Eventidee werde ich auch noch ausprobieren und hier posten, wenn ich es geschafft habe.
Da muss ich mich aber erst noch ein bisschen mit der Materie auseinander setzen.Main.cpp:
// zwei_formulare.cpp: Hauptprojektdatei. #include "stdafx.h" #include "Form1.h" using namespace zwei_formulare; [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; } void Form1::uebergabe(Form1 ^ frm) { For2 ^ fr = gcnew For2(frm); fr->Show(); }
Hier wird von einer Funktion des zweiten Formulares die Hintergrundfarbe
des ersten Formulares auf Rot geandert:
For2.cpp: (Formular Nr. 2)#include "StdAfx.h" #include "For2.h" using namespace zwei_formulare; For2::For2(Form1^ frm) { frm->BackColor = System::Drawing::Color::Red; }
Form1.h
#pragma once #include "For2.h" namespace zwei_formulare { ref class For2; 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 { public: Form1(void) { InitializeComponent(); // //TODO: Konstruktorcode hier hinzufügen. // } void uebergabe(Form1^); 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(133, 215); 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(292, 273); this->Controls->Add(this->button1); this->Name = L"Form1"; this->Text = L"Form1"; this->ResumeLayout(false); } #pragma endregion System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { uebergabe(this); } }; }
For2.h (Header des zweiten Formulares)
#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 zwei_formulare { ref class Form1; /// <summary> /// Zusammenfassung für For2 /// /// 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 For2 : public System::Windows::Forms::Form { public: For2(void) { InitializeComponent(); // //TODO: Konstruktorcode hier hinzufügen. // } For2(Form1^); protected: /// <summary> /// Verwendete Ressourcen bereinigen. /// </summary> ~For2() { 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(90, 160); 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, &For2::button1_Click); // // For2 // this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(292, 273); this->Controls->Add(this->button1); this->Name = L"For2"; this->Text = L"For2"; this->ResumeLayout(false); } #pragma endregion private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { } }; }
Wer weiss: Auch wenn das vielleicht ein noch nicht perfekter Weg ist, so hilft es zumindest ein bisschen zu verstehen, wie das mit den Formularen so funktioniert.
Aber wie schon gesagt: Eventuell folgt noch Eventloesung...
-
hustbaer schrieb:
Ich persönlich finde es besser, wenn man Klassen nicht so eng aneinander koppelt.
Gibt es außer "Geschmacksache" auch andere Gründe (vielleicht besserer Programmierstil, schneller, etc...) warum man bzw. du es nicht so gut findest?
hustbaer schrieb:
D.h. wenn es ein modaler Dialog ist, dann verpasse der Klasse einfach Getter über die man die Einstellungen abfragen kann, nachdem der User den Dialog geschlossen hat.
Wenn es ein "modeless" Dialog/Fenster ist, dann würde ich dem Ding eher Events verpassen, mittels derer man es irgendwoanders dranknoten kann.
Natürlich lässt sich auch beides kombinieren, also ein Formular welches Events + Getter hat, dann lässt sich dieses sehr flexibel modal oder modeless verwenden.
Naja ich persönlich finde es bei sehr vielen Einstellungen sehr mühselig die Klasse dauernd mit getter und setters zu versorgen bzw. verwalten. Ich habe es bisjetzt immer so (Einstellungsklasse) gemacht, da ich die Einstellungen dann immer zentral an einer Stelle habe und nicht suchen muss in welcher Klasse ich welche Getter- und Setters schon habe.
Dieses Zentrale verwaltet hat für mich besondere Vorteile wenn ich z. B. die alle Einstellungen in eine XML schreibe bzw. aus einer XML datei lese etc...
Gruß
Rudi G
-
Rudi G schrieb:
hustbaer schrieb:
Ich persönlich finde es besser, wenn man Klassen nicht so eng aneinander koppelt.
Gibt es außer "Geschmacksache" auch andere Gründe (vielleicht besserer Programmierstil, schneller, etc...) warum man bzw. du es nicht so gut findest?
Klar hat das Vorteile, z.B. bei der Wartbarkeit. Wenn Klassen eng gekoppelt sind, ist es schwierig, eine davon auszutauschen, bzw. evtl. eine alternative Implementierung zu machen. Wenn sie nicht eng gekoppelt sind, geht das viel einfacher.
Allgemein würde ich sagen, wenn man ein Projekt in kleine, voneinander unabhängige Teile auftrennen kann, wird das Ganze übersichtlicher. Natürlich schreibt man in Summe mehr Code, denn irgendwo muss auch der Glue-Code stehen der das ganze wieder zusammenbastelt, aber wie gesagt: ich finde es übersichtlicher und einfacher wartbar.
hustbaer schrieb:
D.h. wenn es ein modaler Dialog ist, dann verpasse der Klasse einfach Getter über die man die Einstellungen abfragen kann, nachdem der User den Dialog geschlossen hat.
Wenn es ein "modeless" Dialog/Fenster ist, dann würde ich dem Ding eher Events verpassen, mittels derer man es irgendwoanders dranknoten kann.
Natürlich lässt sich auch beides kombinieren, also ein Formular welches Events + Getter hat, dann lässt sich dieses sehr flexibel modal oder modeless verwenden.
Naja ich persönlich finde es bei sehr vielen Einstellungen sehr mühselig die Klasse dauernd mit getter und setters zu versorgen bzw. verwalten. Ich habe es bisjetzt immer so (Einstellungsklasse) gemacht, da ich die Einstellungen dann immer zentral an einer Stelle habe und nicht suchen muss in welcher Klasse ich welche Getter- und Setters schon habe.
Dieses Zentrale verwaltet hat für mich besondere Vorteile wenn ich z. B. die alle Einstellungen in eine XML schreibe bzw. aus einer XML datei lese etc...
[/quote]
Ich schlage ja auch nicht vor die Daten in den Forms/Dialogen "aufzuheben", die Instanzen von irgendwelchen Dialogen/Forms sollte es ja auch nur geben solange diese sichtbar sind.
Wo man die Daten (Einstellungen) dann im Endeffekt aufhebt ist wieder eine andere Sache. Dazu gibt's auch mehrere Möglichkeiten - welche Sinn macht muss man anhand des jeweiligen Projekts entscheiden. Bei Programmen die mehrere Benutzer unterstützen (evtl. sogar noch gleichzeitig, also diverse Services/Server) wird so eine Lösung anders aussehen als bei einem kleinen Text-Editor oder sowas.----
BTW: wenn ich das MVP (Model-View-Presenter) Pattern richtig verstanden habe, dann wird das dort auch genau so gemacht: die View (Dialog, Form, ...) ist nicht direkt mit dem Model (Daten) gekoppelt. Die Koppelung übernimmt der Presenter (Glue-Code). Dadurch kann man z.B. die View-Implementierung austauschen (also Daten unterschiedlich darstellen), oder auch die Implementierung des Models (also die Daten z.B. unterschiedlich abspeichern), ohne grössere Änderungen an bestehendem Code machen zu müssen. Man kann sogar zur Laufzeit entscheiden welche konkrete View verwendet werden soll, oder welches konkrete Model.
-
[quote="hustbaer"]Allgemein würde ich sagen, wenn man ein Projekt in kleine, voneinander unabhängige Teile auftrennen kann, wird das Ganze übersichtlicher. Natürlich schreibt man in Summe mehr Code, denn irgendwo muss auch der Glue-Code stehen der das ganze wieder zusammenbastelt, aber wie gesagt: ich finde es übersichtlicher und einfacher wartbar.[/qoute]
Kannst du mir bitte ein konkrettes Beispiel geben, wie man ein Programm in kleine voneinander unabhängige Teile trennen kann?
Gruß
Rudi G
-
soll das jetzt ein scherz sein?
-
hustbaer schrieb:
soll das jetzt ein scherz sein?
Sorry wenn das so aussieht, aber ich meine das ernst.
(oder wolltest du damit sagen das du es auch vorteilhafter findest, das Programm so zu gliedern wie ich es beschrieben habe (Einstellungsklasse etc..))?
Gruß
Rudi G
-
Gewünschte Informationen sollten in einem Einstiegerbuch oder Tutorial erwähnt werden. Dieses Wissen bitte selbst erarbeiten.