How can I update my user interface from a thread that did not create it?

How can I update my user interface from a thread that did not create it?

Rate This
  • Comments 34

When performing any action on a control which requires the updating of a user interface element (e.g. setting the Text property on almost any class derived from Control, updating the data source behind a DataGrid), these operations MUST take place on the thread that created the UI element.

In order to do this, the Control class provides the Invoke method, which will take a delegate and execute it on the thread that the UI element was created on. In order to use this, one must declare a function that performs the UI operation. For example, say a form has a TextBox on it named m_TextBox. To update the text from another thread, create a method that will update the Text property on the TextBox:

// The declaration of the textbox.
private TextBox m_TextBox;

// Updates the textbox text.
private void UpdateText(string text)
{
  // Set the textbox text.
  m_TextBox.Text = text;
}

Now, create a delegate that has the same signature as the method that was previously defined:

public delegate void UpdateTextCallback(string text);

In your thread, you can call the Invoke method on m_TextBox, passing the delegate to call, as well as the parameters.

m_TextBox.Invoke(new UpdateTextCallback(this.UpdateText), 
new object[]{”Text generated on non-UI thread.”});

Note: Do not create a method that matches the EventHandler delegate signature and pass that. The implementation of Invoke on the Control class will not take into account the parameters passed to Invoke if the type of the delegate is EventHandler. It will pass the control that Invoke was called on for the sender parameter as well as the value returned by EventArgs.Empty for the e parameter.

[Author: Nicholas Paldino]

Leave a Comment
  • Please add 1 and 3 and type the answer here:
  • Post
  • Hi Nicholas,
    Yes, I've done this before, and it's a pain in the rear. I come from C++, and love C# since (among other things) it saves me time updating headers. However, 3 things that I hate about C# are the typing associted with updating of controls in threads that you outline, the typing to have "generic" get/set calls for a class variable (why not an attribute?), and the lack of default params in function calls (come on, even VB has this). Please, guys, you built a beautiful language - now help me cut down on all the typing that I have to do.

    Ed Eichman
    Cambrils, Spain
  • I really can't say how many days i spent researching the threading topic, as i'm new to it. I actually never did the control updating without Invoke, but it seemed there was problems anyway, so question did i do 'right' when i chose to do this:

    worker class:
    some method calls: Update("blabla");

    public void Update(string str)
    {
    UpdateControl uc= new UpdateControl(controlclass.Update);
    uc.DynamicInvoke(new object[]{ (stopwatch.Peek() / (float)10).ToString() + str });
    }

    delegate void UpdateControl(string str);



    control class:
    public void Update(string s)
    {
    AppendTextDelegate atd = new AppendTextDelegate(MyAppendText);
    textBox1.BeginInvoke(atd,new object[]{s.Trim() + Environment.NewLine});
    }


    public void MyAppendText(string str)
    {
    lock(textBox1)
    {
    textBox1.AppendText(str);
    }
    }

    --
    Situation is i have the Update method of the worker class called from various places in that class, updating the stopwatch time elapsed (QueryPerformanceCounter) into the controls texbox. The test cases i encountered some problems initially was when i did this in worker class some 200 times
    socket.BeginConnect(ipep,new AsyncCallback(this.OnRemoteConnected),this);

    OnRemoteConnected calls the Update. There was some incorrectly updated line in the textbox, so i ended putting up all this lock etc crap you see pasted here. I doubt this should be really necessary, whats your take on this?

  • Actually, i pre-empted you would ask how i connect 200+ IP's inside same second. Actually i didn't. I tried this against lan ip's and when the OnRemoteConnected tries BeginReceive and fails, it calls Update("connection failed"); .. obviously!
  • Here is some boiler plate that I use a lot. The benefit is that the delegate stays with the target function and their is only one function for both the external caller and the internal implementation.

    public delegate string function(object[] params);

    public string foo(object[] parameters)
    {
    if(InvokeRequired)
    {
    Debug.WriteLine("not in the right thread");
    return (string)Invoke(new function(foo),parameters);
    }
    else
    {
    return "cool, huh?";
    }
    }
  • Ed, it's not C#'s problem, not even .NET's, that you cannot access controls from another thread than that which created the control. You have to do it as well when programming with C++ against the Win32 API.
    Secondly, what should be attributed for getters/setters? The class variable? That would negate the advantage of them.
  • The problem that I'm having is that the call to ListView.Invoke() itself deadlocks. I have no idea why this would be happening. Here is what I do:

    1) Create a ListView control
    2) Call an Asynchronous Delegate from the UI thread
    3) Call ListView.Invoke from the Asynchronous Delegate to call another delegate

    The result is that ListView.Invoke deadlocks, with the top of the call stack for that thread showing "System.Threading.WaitHandle.WaitOne()" In fact, calling ListView.Refresh() or just about anything else will cause a deadlock as well. As far as I can tell, no other thread is using the ListView. What could be causing this?
  • how also we call a function from a user interface when the function is writen in c++ and the GUI in c#.
  • I am trying to do this from another class altogether, not just another thread of the same class. How would I do this? Do I still have to pass the reference of the form to the other class inorder for it to be able to update my form when the right event occurs?

    Thanks
    Abdullah
  • Please, clearly explain to me about the way to create the user-interface thread(step-by-step).

    Thanks
  • Please, clearly explain to me about the way to create the user-interface thread(step-by-step).

    Thanks
  • Hi, I have tried to use the above method but got a run time error : arguementexception when it tries to invoke the datagrid..any idea what happened?

    have a delegate method(UpdateTextCallback) to update the
    datagrid(updateDs2)

    updateDs2 (DataSet ds)
    {
    datagrid2.datasource = ds.tables[0].defaultview;
    }

    private void viewAttendanceNotes_dsStatus(DataSet myDs_viewAttNotes)
    {
    this.viewNoteProgressBar.Value=100;
    this.viewNoteProgressBar.Hide();
    Cursor.Current = Cursors.Default;
    //new code - not so sure about the syntax. Somehow i need to pass the
    dataset into the updateDs2 para
    this.dataGrid2.Invoke(new UpdateTextCallback(this.updateDs2));
    }

  • How can I update my user interface from another thread? How can I update my user interface from another thread? When performing any action on a control which requires the updating of a user interface element (e.g. setting the Text property on almost any class derived from Control, updating the data source behind a DataGrid), these operations MUST take place on the thread that created the UI element. In order to do this, the Control class provides the Invoke method, which will take a delegate and execute it on the thread that the UI element was created on. A must read for those who have free-threaded code that wants to side-effect the UI. This is an often misunderstood aspect of Windows programming and is long-standing since the advent of Win16. Way back then ... you typically only had one process and one thread. But with today's programs that are performing background tasks (like pulling RSS feeds), checking email, performing asynchronous web services calls, etc. you can easily find yourself running on a thread that doesn't have UI affinity (i.e. you're not running on the thread that the UI objects were created on). Be sure to take the time to...
  • PingBack from http://mikesdump.wordpress.com/2006/01/15/net-compact-framework-updating-the-user-interface-from-a-worker-thread/

  • PingBack from http://www.innobead.com/davidko/?p=55

Page 1 of 3 (34 items) 123