8

Presently I'm working with WinForms(in C#) and I have to run the application in the background. For this purpose I'm using asynchronous. When I run the application it's showing an exception like

"Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on."

How can I solve this error?

Rob
  • 45,296
  • 24
  • 122
  • 150
Victor
  • 555
  • 6
  • 15
  • 39
  • possible duplicate of [Help me with that CrossThread?](http://stackoverflow.com/questions/337165/help-me-with-that-crossthread) – jgauffin May 03 '11 at 11:46
  • possible duplicate of [Why am I getting this error:"Cross-thread operation not valid: Control lbFolders accessed from a thread other than the thread it was created on."?](http://stackoverflow.com/questions/244591/why-am-i-getting-this-errorcross-thread-operation-not-valid-control-lbfolders) – Ohad Schneider May 03 '11 at 13:11

8 Answers8

12

When making method calls to a control, if the caller is on a different thread than the one the control was created on, you need to call using Control.Invoke. Here is a code sample:

// you can define a delegate with the signature you want
public delegate void UpdateControlsDelegate();

public void SomeMethod()
{
    //this method is executed by the background worker
    InvokeUpdateControls();
}

public void InvokeUpdateControls()
{
    if (this.InvokeRequired)
    {
        this.Invoke(new UpdateControlsDelegate(UpdateControls));
    }
    else
    {
        UpdateControls();
    }
}

private void UpdateControls()
{
    // update your controls here
}

Hope it helps.

Daniel Peñalba
  • 30,507
  • 32
  • 137
  • 219
  • Hi thank you for giving responce i'm trying what u provide code,actually i call the thred in button_click event..but it allways go to else part ....not enter into this if (this.InvokeRequired) { this.Invoke(new PerformSomeActionDelegate(MyAction)); } – Victor May 03 '11 at 11:47
  • @Victor: That means that Invoke is not required at this point. Make sure that all the code that is executed in the thread performs "safety" (Invoke) calls to update your form's controls. – Daniel Peñalba May 03 '11 at 11:49
  • @Victor: I'm afraid that is not possible – Daniel Peñalba May 03 '11 at 12:03
4

Most often, the best way to do this sort of thing with WinForms is to use BackgroundWorker, which will run your work on a background thread, but provide you with a nice clean way to report status back to the UI.

In a lot of everyday .NET programming, explicitly creating threads or calling .Invoke is a sign that you're not using the framework to its full advantage (of course, there are lots of legitimate reasons to do low-level stuff too, it's just that they're less common that people sometimes realise).

Will Dean
  • 39,055
  • 11
  • 90
  • 118
  • Thank you for giving response i am using this(what u giving link in above) background code in my application.but this is showing error... – Victor May 03 '11 at 11:39
  • 2
    @Victor - you must either use .Invoke (see other people's examples), OR use the BackgroundWorker.ReportProgress method (and the ProgressChanged event which it raises) to access the form. If I was using BackgroundWorker, I would always favour ReportProgress over direct Invoke calls. – Will Dean May 03 '11 at 11:42
2

You need to check if Invoke is required for the control you're trying to update. Something like this:

Action<Control, string> setterCallback = (toSet, text) => toSet.Text = text;

void SetControlText(Control toSet, string text) {
  if (this.InvokeRequired) {
    this.Invoke(setterCallback, toSet, text);
  }
  else {
    setterCallback(toSet, text);
  }
}
AlexCuse
  • 18,008
  • 5
  • 42
  • 51
1

A pattern you might find useful is to do a check at the top of functions that interact with the GUI to see whether you are running on the correct thread or not and have the function invoke itself if required. Like this:

    public delegate void InvocationDelegate();

    public void DoGuiStuff(){
      if (someControl.InvokeRequired){
        someControl.Invoke(InvocationDelegate(DoGuiStuff));
        return;  
      }

      //GUI manipulation here
    }

Using this pattern - if you are on the correct thread when the method is called it doesn't invoke itself, but if you are on a different thread it will invoke itself and then return (so the GUI manipulation logic is only ever called once either way).

Russell Troywest
  • 8,635
  • 3
  • 35
  • 40
1

Updated from Invoke to begin Invoke

// you can define a delegate with the signature you want
public delegate void UpdateControlsDelegate();

public void SomeMethod()
{
    //this method is executed by the background worker
    InvokeUpdateControls();
}

public void InvokeUpdateControls()
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new UpdateControlsDelegate(UpdateControls));
    }
    else
    {
        UpdateControls();
    }
}

private void UpdateControls()
{
    // update your controls here
}
Pankaj
  • 9,749
  • 32
  • 139
  • 283
  • Hi Pankaj allready i have try this code but the invokerequired allways false when it is true... i call the invokerequired in my button click event..... – Victor May 03 '11 at 13:00
  • replace this with your buttonid and check again. – Pankaj May 03 '11 at 13:07
  • i wrote code like this private void btnsiteranks_Click_1(object sender, EventArgs e) { InvokeUpdateControls(); } public void InvokeUpdateControls() { if (this.InvokeRequired) { this.BeginInvoke(new PerformSomeActionDelegate(Geturls)); } else { Geturls(); } } – Victor May 03 '11 at 13:11
  • ok now it is working fine just i change code like if(!this.InvokeRequired)... but i want display the lable text when data processing how....please help me – Victor May 03 '11 at 13:53
  • @Pankaj your `InvokeUpdateControls` is slightly dangerous. If Invoke is required, then you are asynchronously executing the delegate, i.e. your scheduling it for a later time, but if you are on the same thread (or window handle is not created yet), you are executing it right there itself. This may or may not be desired, but one should have the difference in mind. – nawfal Oct 12 '21 at 07:42
0

The UI changes can be done with Control.Invoke() methods, this cross thread exception can be solved using below code snippet.

void UpdateWorker()
{
   //Here ddUser is the user control
   //Action to be performed should be called within { } as like below code
   if (this.ddUser.InvokeRequired)
      ddUser.Invoke(new MethodInvoker(() => { ddUser.Size = new Size(100, 100); }));
}
Amal
  • 576
  • 1
  • 6
  • 26
0

I knew the topic is 10 years old, but I would like to improve the solution for generic through lambda selector instead of defining of each type of setter

    private void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value)
    {
        if (this.InvokeRequired)
            this.Invoke(MyUtils.GetSetter(selector), control, value);
        else
            DataCrawlerUtils.GetSetter(selector)(control, value);
    }  

Or static

    public static void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value) where C : Control
    {
        if (control.InvokeRequired)
            control.Invoke(DataCrawlerUtils.GetSetter(selector), control, value);
        else
            DataCrawlerUtils.GetSetter(selector)(control, value);
    }

GetSetter method from here to assign value to a property has been selected through lambda

    public static Action<T, TProperty> GetSetter<T, TProperty>(
       Expression<Func<T, TProperty>> pExpression
    )
    {
        var parameter1 = Expression.Parameter(typeof(T));
        var parameter2 = Expression.Parameter(typeof(TProperty));

        // turning an expression body into a PropertyInfo is common enough
        // that it's a good idea to extract this to a reusable method
        var member = (MemberExpression)pExpression.Body;
        var propertyInfo = (PropertyInfo)member.Member;

        // use the PropertyInfo to make a property expression
        // for the first parameter (the object)
        var property = Expression.Property(parameter1, propertyInfo);

        // assignment expression that assigns the second parameter (value) to the property
        var assignment = Expression.Assign(property, parameter2);

        // then just build the lambda, which takes 2 parameters, and has the assignment
        // expression for its body
        var setter = Expression.Lambda<Action<T, TProperty>>(
           assignment,
           parameter1,
           parameter2
        );

        return setter.Compile();
    }

Then the using is pretty simple

        SetControlSafety(txtStatus, x => x.Text, "Loading resources...");
Tấn Nguyên
  • 1,607
  • 4
  • 15
  • 25
0

BeginInvoke

It is a good way to prevent a cross-thread exception. I read it in a book "The C# Programmer’s Study Guide (MCSD"

You can use BeginInvoke

BeginInvoke method is used to change values of UI control from other threads. It does it in a thread-safe way. It requires a delegate; it tells which UI control needs to change its value.

  private async void button1_Click(object sender, EventArgs e)
   {
     Task task = Task.Run(() =>
        {
           this.BeginInvoke(new Action(() =>
               {
                  label1.Text = "Hello";
                  }));
         });
      await task;
  }

The value of label1.Text shall be changed to “Hello” and no exception will arise because it’s a threadsafe operation.