12

Hi I see following code:

void UpdateMessage (string message)
{
    Action action = () => txtMessage.Text = message;
    this.Invoke (action);
}

Why using Action and then invoke action here? Why not just using txtMessage.Text = message to replace the code in function body?


Update

A fuller version of the code, presented in a comment, reproduced below with syntax highlighting, indentation etc.

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
        InitializeComponent(); 
        new Thread(Work).Start(); 
    } 

    void Work() 
    { 
        Thread.Sleep(5000); 
        UpdateMessage("My Garden"); 
    } 

    void UpdateMessage(string message) { 
        Action action = () => textBox1.Text = message; 
        this.Invoke(action); 
    } 
} 
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
spspli
  • 3,128
  • 11
  • 48
  • 75
  • 1
    I think what you've stumbled onto there is what we in the business call "bad code". It might have been copied from someplace in the system that actually needed indirection. But you are 100% right. This does not. – Axeman May 10 '11 at 13:19
  • @Axeman How can you tell from the code supplied that UpdateMessage always runs in the GUI thread? – David Heffernan May 10 '11 at 13:22
  • 3
    @Axeman this is not a "bad code", this is UI thread invocation. – Viacheslav Smityukh May 10 '11 at 14:34
  • 1
    Not to be confused with a action.Invoke() call – Viacheslav Smityukh May 10 '11 at 14:40
  • @David Heffernan, well that's the one thing I contemplated. But there was nothing there specifically that says that this is GUI code--and provided it is, and provided that updating the `Text` field is that laborious or dangerous as a background thread, why the hell hasn't the component chosen a safe/right way *itself*? It's still dumb code--whether it happens to be endemic or not. – Axeman May 10 '11 at 15:01
  • @Axeman Those are just the rules of Windows. What alternative do you propose? – David Heffernan May 10 '11 at 15:21
  • @David Heffernan, something that hides that kind complexity would be preferable. My first thought was that it was like a `DoEvents` in VB or a `window.setTimeout( 0 )` in JavaScript (which just queues the handler) – Axeman May 10 '11 at 16:26
  • @Axeman it's pretty simple once you are familiar with the pattern – David Heffernan May 10 '11 at 16:41

3 Answers3

20

Because this code runs on a different thread from the UI and must be marshalled across to the UI thread with Invoke.

The documentation for Control.Invoke() states:

Executes the specified delegate on the thread that owns the control's underlying window handle.

This is all necessary because the underlying Windows framework requires that operations on a window handle are performed by the thread that owns the window handle.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
12

If UpdateMessage is called from another thread you need to Invoke into main thread in order to interact with GUI elements

If you use just txtMessage.Text = message you will get CrossThreadOperationException

Stecya
  • 22,896
  • 10
  • 72
  • 102
  • Yes, this UpdateMessage is invoked in another thread other than main UI thread. But why I use txtMessage.Text = message and didn't see the exception which is supposed to be there? Thanks! – spspli May 10 '11 at 15:07
4

You should work with a control's properties in the UI thread, otherwise you will receive exception.

Control.Invoke() will execute your delegate by sending windows message to the window message loop.

But you can optimize code to prevent unnecessary thread syncronization when it don't required:

void UpdateMessage (string message)
{
    if(InvokeRequired)
    {
        Invoke((Action)()=>UpdateMessage(message));
        return;
    }

    txtMessage.Text = message;
}
Viacheslav Smityukh
  • 5,652
  • 4
  • 24
  • 42
  • using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; namespace TestAction { public partial class Form1 : Form { public Form1() { InitializeComponent(); new Thread(Work).Start(); } void Work() { Thread.Sleep(5000); UpdateMessage("My Garden"); } void UpdateMessage(string message) { Action action = () => textBox1.Text = message; this.Invoke(action); } } } – spspli May 10 '11 at 14:24
  • 1
    In this code, you call UpdateMessage method from the thread that was started in from constructor. The Control.Invoke() call is nessesary in the current scenario. – Viacheslav Smityukh May 10 '11 at 14:33
  • Thanks! I know this is necessary now. But I just wonder why if I don't use it, I didn't see any exception. – spspli May 10 '11 at 15:09
  • Try start it under debugger, you should see that exception. – Viacheslav Smityukh May 10 '11 at 15:12