Passiert mir immer mal wieder. Ich versuche die Inhalte eines Controls aus einem anderen Thread heraus zu verändern als aus dem, der das Control urpsrünglich mal erzeugte. Was dabei heraus kommt ist klar: The calling thread cannot access this object because it is owned by another thread.
Ich muss also folglich meinen Code im Kontext des Threads ausführen, der das Control erstellte. Bis dato nutzte ich immer folgendes Snippet:
this.Dispatcher.Invoke( new Action( delegate() { // Do your code here MessageBox.Show("Invoke succeeded"); } ) );
Wann immer dieser Codeblock erreicht wird, werden die folgenden Anweisungen im Kontext des UI-Threads ausgeführt. Das klappte bisher immer ganz gut und ich nutzte es z.B. immer dann wenn ich Debug-Meldungen zu meinem Debug-Listener-Dialog sandte. Leider scheint diese Methode ziemlich „teuer“ zu sein. Als ich heute einmal an einem langsameren Rechner arbeitete und mehrere Debugmeldungen pro Sekunde absetzte, ging die Performance des Programms doch stark in die Knie. Ein kurzes Profiling sprach Bände, Invoke
ist schweinelahm, besonders wenn es, wie in diesem Fall gar nicht notwendig gewesen wäre. Zum Glück gibt es für WPF die Möglichkeit für jedes Control das Feld InvokeRequired
abzufragen welches angibt, ob sich die Programmausführung gerade in dem Thread befindet, der das Control erstellt hat. Somit könnte ich also folgenden Code ausführen:
if (this.Dispatcher.CheckAccess()) { // Your Invoked Code } else { // Your code without invoking }
Das funktioniert zwar, aber ich müsste ja meinen Code zweimal tippen und pflegen…unschön. Zur Lösung des Problemchens entschied ich mich für Lambda-Ausdrücke, denn so konnte ich meinen Code in einer Variablen zwischenspeichern.
// Create a function with a string input and a bool return value Func<string,bool> handleEvent = (string pMessage) => { MessageBox.Show(pMessage); return true; }; // Are we running on the correct UI Thread if (this.Dispatcher.CheckAccess()) { //Directly run the method handleEvent(pMessage); } else { // Invoke the method from the UI Thread dispatcher this.Dispatcher.Invoke(handleEvent,pMessage); }
Jetzt läuft es so wie ich es mir vorstelle. Verschönern, Verkürzen und Modularisieren kann man natürlich immernoch.
Done;