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.aspx

    Simon



  • danke! Klappt Super 😃

    LG Solick


Anmelden zum Antworten