FormClosing anderer Thread als Form?
-
Hallo,
ich habe zum üben einen kleinen Texteditor geschrieben.
Über die Beenden-Funktion im Menü wird die Funktion Beenden() aufgerufen. Hier wird geprüft, ob Der Inhalt der TextBox gespeichert ist, wenn nicht, wird nachgefragt und bei bei bejahen gespeichert. das Programm läuft fehlerfrei durch.
Nun wollte ich abfangen, wenn man zum Beenden das Fenster schließt, also das Rote X anklickt. Dazu habe ich FormClosing aufgerufen und dort nur die Funktion Beenden() aufgerufen.
Hier bekomme ich jedoch eine Fehlermeldung, dass der Zugriff auf TextBox von einem anderen thread erfolgt als dem Erzeuger Thread.
Nun meine Frage: Erzeugt FormClosing einen neuen Thread? Und wenn ja, hat jemand eine Idee, wie ich das Problem lösen kann?
Danke und Grüße
Solick
-
Und wenn ja, hat jemand eine Idee, wie ich das Problem lösen kann?
Benutze Control.Invoke dafür:
http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx
-
Hallo theta,
danke für den Hinweis. Wenn ich es richtig verstanden habe, geht es bei Invoke darum, zwischen zwei Klassen zu handeln. Bei mir spielt sich aber alles in einer Klasse ab (public ref class Form1). Dass müsste doch eigentlich ein Thread sein oder nicht?
-
nein, klasse und thread haben keinen zusammenhang.
simon
-
Sorry, ich meinte zwischen zwei Instanzen...
ist es nicht so, dass jede Instanz einer Klasse ein neuer Thread ist?
-
nein.
-
hmm ich hab mir die Anleitung durchgelesen und verstehe es leider nicht wirklich. Dort wird die Form-Klasse vererbt an eine neue Klasse myformcontrol.
Dann wird dort eine zweite Klasse definiert, und dort eine Funktion mittels invoke aus der ersten Klasse aufgerufen.
Mein Problem besteht aber darin, dass die Methode teil der Klasse Form ist. Wie setzt man das dann da um?
-
solick schrieb:
hmm ich hab mir die Anleitung durchgelesen und verstehe es leider nicht wirklich. Dort wird die Form-Klasse vererbt an eine neue Klasse myformcontrol.
Dann wird dort eine zweite Klasse definiert, und dort eine Funktion mittels invoke aus der ersten Klasse aufgerufen.
Mein Problem besteht aber darin, dass die Methode teil der Klasse Form ist. Wie setzt man das dann da um?
Es geht auf genau dieselbe Art, wie wenn es Methoden von zwei verschiedenen Objekten sind.
Zum andern muss ich sagen, dass der FormClosing Event auf demselben Thread ausgeführt wird, auf dem auch die ganze Form ausgeführt wird.
Da muss noch ein anderes Problem sein.
Zeige doch mal die relevanten Code Teile. Ev. ein Mini Bsp. wo das Verhalten nachvollzogen werden kann.Simon
-
Wie geschrieben, es ist eine Programmierübung.
Anbei der gekürzte Code:
#pragma once #include "Warnung1.h" #include "UeberFenster.h" namespace texteditor { using namespace System; using namespace System::IO; 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 { String ^dateiname; bool savestat; bool beenden; private: System::Windows::Forms::ToolStripMenuItem^ speichernUnterToolStripMenuItem; String ^ex; public: Form1(void) { InitializeComponent(); // //TODO: Konstruktorcode hier hinzufügen. // dateiname = "unbekannt.txt"; this->Text = "L-Edit - " + dateiname; savestat = true; ex = nullptr; beenden = false; } protected: /// <summary> /// Verwendete Ressourcen bereinigen. /// </summary> ~Form1() { if (components) { delete components; } } //Hier wurde Code entfernt //Öffnet die Datei und liest ihren Inhalt ein void Open(String ^dateiname) { try { StreamReader ^sr = gcnew StreamReader(dateiname); textBox1->Text = sr->ReadToEnd(); this->Text ="L-Edit - " + dateiname; sr->Close(); changeSaveStat(true); } catch(IOException^) { MessageBox::Show("Datei konnte nicht geöffnet werden.!","Fehler"); } } //Speichern unter-Dialog void SaveAs() { SaveFileDialog ^saveFileDialog1 = gcnew SaveFileDialog(); //Startverzeichnis vorgeben, danach immer das zuletzt //angesteuerte Verzeichnis verwenden (Nicht die Vorgabe wiederherstellen saveFileDialog1->InitialDirectory = "."; saveFileDialog1->RestoreDirectory = false; //Filer für Dateien mit den Extension saveFileDialog1->Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"; //Ersten Filter als Vorgabe wählen saveFileDialog1->FilterIndex = 2; if (saveFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK) { SaveFile(saveFileDialog1->FileName); dateiname = saveFileDialog1->FileName; changeSaveStat(true); } } //Speichern void SaveFile(String ^datnam) { //Speichert die Datei try { StreamWriter ^sw = gcnew StreamWriter(datnam); sw->Write(textBox1->Text); sw->Close(); } catch (IOException ^ex) { MessageBox::Show("Datei konnte nicht gespeichert werden.","Fehler"); } finally { //MessageBox::Show(ex->ToString(),"Inhalt von ex"); if (ex == nullptr) { changeSaveStat(true); } } } //Speicher-Anzeige ändern void changeSaveStat(bool stat) { if (stat == false) { this->Text="L-Edit - " + dateiname + " (*)"; savestat = false; } if (stat == true) { this->Text="L-Edit - " + dateiname; savestat = true; } } //Beenden und vorher prüfen ob aktuelle Änderungen gespeichert sind void Beenden() { if (savestat==false && beenden == false) { // Warnfenster1 initialisieren Warnung1 ^warnfenster2 = gcnew Warnung1(); // Displays the MessageBox. if ( warnfenster2->ShowDialog() == System::Windows::Forms::DialogResult::Yes ) { if (dateiname != "unbekannt.txt") SaveFile(dateiname); else SaveAs(); } beenden = true; } Close(); } #pragma region Windows Form Designer generated code // hier wurde Code entfernt private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) { } private: System::Void neuToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { if (textBox1->Text != "") { // Warnfenster1 initialisieren Warnung1 ^warnfenster1 = gcnew Warnung1(); // Displays the MessageBox. if ( warnfenster1->ShowDialog() == System::Windows::Forms::DialogResult::Yes ) { textBox1->Text = ""; dateiname = "unbekannt.txt"; changeSaveStat(true); } } } private: System::Void beendenToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { Beenden(); } private: System::Void ausschneidenToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { textBox1->Cut(); } private: System::Void kopierenToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { textBox1->Copy(); } private: System::Void einfügenToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { textBox1->Paste(); } private: System::Void öffnenToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { OpenFileDialog ^openFileDialog1 = gcnew OpenFileDialog(); //Startverzeichnis vorgeben, danach immer das zuletzt //angesteuerte Verzeichnis verwenden (Nicht die Vorgabe wiederherstellen openFileDialog1->InitialDirectory = "."; openFileDialog1->RestoreDirectory = false; //Filer für Dateien mit den Extension .h und .cpp openFileDialog1->Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"; //Ersten Filter als Vorgabe wählen openFileDialog1->FilterIndex = 2; // Es kann nur eine Datei ausgewählt werden openFileDialog1->Multiselect = false; if (openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK) { Open(openFileDialog1->FileName); dateiname = openFileDialog1->FileName; changeSaveStat(true); } } private: System::Void speichernToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { if (dateiname != "unbekannt.txt") SaveFile(dateiname); else SaveAs(); } private: System::Void überToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { UeberFenster ^ueber1 = gcnew UeberFenster(); ueber1->ShowDialog(); } private: System::Void textBox1_TextChanged(System::Object^ sender, System::EventArgs^ e) { changeSaveStat(false); } private: System::Void speichernUnterToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { SaveAs(); } private: System::Void Form1_FormClosing(System::Object^ sender, System::Windows::Forms::FormClosingEventArgs^ e) { /*Beenden();*/ } }; }
-
Ist nicht viel eher das Close() in der Beenden() Methode zuviel?
Bei Close() wird nämlich wieder ein FormClosing Event ausgelöst, was wiederum Close() aufruft, was wiederum...StackOverflowException
Simon
-
naja aber die Funktion Beenden() kann ja von zwei Stellen her aufgerufen werden, einmal Übers Menü->Beenden und einmal über abfangen des Haupt-Fenster-Schließens.
ich könnte allerdings den inhalt von Beenden ins FormClosing Event reinschreiben, dann müsste ich nur das vollenden des Closing ggf. abfangen können. Wäre das sinnvoller?
-
ja, das denke ich.
Simon
-
ok dann noch eine letzte Frage: Wie breche ich im FormClosing Event das Schließen ab?
-
solick schrieb:
ok dann noch eine letzte Frage: Wie breche ich im FormClosing Event das Schließen ab?
Indem Du das FormClosingEventArgs Objekt benutzt (Property: Cancel):
http://msdn.microsoft.com/de-de/library/system.windows.forms.formclosingeventargs.aspxSimon
-
danke! Klappt Super
LG Solick