Einfaches Invoke in C#

invalidoperationWer kennt sie nicht…. die berühmt-berüchtigte „Ungültiger threadübergreifender Vorgang“ in C#? Sie tritt typischerweise dann auf, wenn man von einem BackgroundWorker oder einem Thread auf Elemente  der Oberfläche zugreifen möchte. Im Netz gibt es hierzu ziemlich viele Anleitungen, beispielsweise die von Microsoft auf https://msdn.microsoft.com/de-de/library/ms171728(v=vs.110).aspx. Diese Lösungen haben eins gemeinsam: Sie sind häufig schwer lesbar und klobig.

private void SetText(string text)
{
	// InvokeRequired required compares the thread ID of the
	// calling thread to the thread ID of the creating thread.
	// If these threads are different, it returns true.
	if (this.textBox1.InvokeRequired)
	{	
		SetTextCallback d = new SetTextCallback(SetText);
		this.Invoke(d, new object[] { text });
	}
	else
	{
		this.textBox1.Text = text;
	}
}

Den Schabernack kann man aber relativ einfach umgehen. Dafür kann man seit .NET 4.0 ein sogenanntes Mixin verwenden, das man irgendwo in seinem Projekt ablegt:

public static class FormInvokeExtension
{
    static public void UIThreadAsync(this Control control, Action code)
    {
        if (control.InvokeRequired)
        {
            control.BeginInvoke(code);
            return;
        }
        code.Invoke();
    }

    static public void UIThreadSync(this Control control, Action code)
    {
        if (control.InvokeRequired)
        {
            control.Invoke(code);
            return;
        }
        code.Invoke();
    }
}

Diese Klasse erweitert SÄMTLICHE Controls um die Funktionen UIThreadAsync und UIThreadSync. Wie der Name schon sagt, wird hiermit entweder synchron oder asynchron ein Codeschnipsel im Context des UI-Thread ausgeführt:

private void SetText(string text)
{
    this.UIThreadAsync(delegate
    {
        this.textBox1.Text = text;
    });
}

Praktischerweise lässt sich das sogar mit LINQ kombinieren:

    ...
    this.UIThreadSync(()=> control3.setText(param1, param2, param3));
    ...