Archiv des Tags ‘Delegate mit Parameter’

Richtiges Invoke von UI Controls

Samstag, den 19. Januar 2008

Zu diesem Thema gibt es meiner Meinung nach im Netz nicht viel schlaue Links und Hinweise, da wollte ich mal ein bisschen aufräumen. Aber zuerst einmal um was es da genau geht.

Wenn man mit dem Visual Studio ein GUI erstellt und auf diesem GUI zum Beispiel eine Textbox hinzufügt , diese GUI dann kompiliert und ausführt, dann laufen alle GUI Operationen im sogenannten Main- oder auch UI-Thread. Dieser Thread ist zuständig für das richtige Updaten, Zeichen etc. der Benutzerelemente.

Programmiert man eine Funktion die viel Rechenzeit braucht und führt diese Funktionen in einem neuen Thread aus, so muss man die Crossthreadingcalls beachten, wenn man aus dem neu erstellten Thread auf die Benutzerelemente des Mainthreads zugreifen will (um zum Beispiel in der Textbox einen Text hinzuzufügen). Denn greift man ohne zusätzliche Sicherheit einfach aus dem zweiten Thread auf das Text Property der Textbox zu, so kann aus unter Umständen dazu führen, dass zwei Thread s zur gleichen Zeit auf die Textbox zugreifen und es kommt zu unvorhersagbaren Verhalten auf dem GUI. Doch wie kann man dies umgehen?

Die Lösung heisst Delegates und Control.Invoke().

Nehmen wir an wir haben eine Methode MyMethod du jedesmal wenn Sie von einem anderen Thread aufgerufen wird auf dem GUI den Text eines Labels myControl auf “Vom Thread aufgeweckt!” setzt. Damit die Methode MyMethod threadsafe aufgerufen werden kann, muss folgendes Codesegment implementiert werden:

Methode ohne Parameter und zugehöriges Delegate

Private delegate void MyMethodDelegate();
Private void MyMethod() {
	if(myControl.InvokeRequired) {
		myControl.Invoke(new MyMethodDelegate(MyMethod));
		return;
	}
	// Add rest of the code here
        myControl.Text = "Vom Thread aufgeweckt!";
}

Der Ablauf des obigen Codesegments ist folgendermassen. Man definiert ein Delegate (quasi typensicherer Methodenpointer), das später auf die Methode MyMethod “zeigt”. Danach prüft man in der eigentlichen Methode über myControl.InvokeRequired ob das Control aus einem anderen Thread angesprochen wurde. Ist dies der Fall, so führt man ein Invoke auf myControl über myControl.Invoke mit dem Delegate MyMethodDelegate durch und übergibt dem Delegate die aufgerufene Methode (hier MyMethod) als Parameter. Nach dem Invoke verlässt man die Methode direkt wieder über die Anweisung return;. Der eigentliche Zugriff auf das Control myControl erfolgt dann im Codeblock // Add rest of the code here.

Die Logik dahinter ist, dass durch den Aufruf von Inkvoke auf das Control, wird über das Delegate die Methode nach einmal aufgerufen und der zugreifende Thread meldet sich quasi beim UI-Thread an und wartet, bis dieser den Zugriff auf das Control frei gibt. Sobald dies der Fall ist, kann der Thread in die Methode und dieses Mal ist das Flag InvokeRequired auf false gesetzt und es wird nur der eigentliche Codeblock bei // Add rest of the code here ausgeführt.

Doch wie funktioniert das Ganze, wenn die Methode MyMethod ein oder mehrere Parameter entgegen nimmt? Hier ist der Codeauszug:

Methode mit Parameter und zugehöriges Delegate

Private delegate void MyMethodWithParameterDelegate(string message);
Private void MyMethodWithParameter(string myParameter) {
	if(myControl.InvokeRequired) {
		myControl.Invoke(new
MyMethodWithParameterDelegate(MyMethodWithParameter), new object[]{ myParameter });
		return;
	}
	// Add rest of the code here
        myControl.Text = myParameter;
}

Der Ablauf ist wieder genau gleich, einfach wird in diesem Fall ein Delegate mit Parameter spezifiziert und beim Invoke wieder ein Delegate erstellt und die Parameter für die Methode als Objektarray übergeben. That’s it!

Viel Spass beim Ausprobieren…