1

I have one main windows form and within that form I have custom controls that represents different screens in application. I want to access this control's child controls. There's something I'm not getting here...sometimes I get this error:

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

but sometimes everything works OK. I don't completelly understand why the error...probably something with external device (MEI BillAcceptor) which has an event (inside Form1 class) that does the changes to the control... so let me write a simple code...

//user control
public partial class Screen2 : UserControl
{
    public void changeValue(string txt)
    {
        lblText.Text = txt;
    }
}

and the method changeValue is called from a form1 when particular event is rised...

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        BillAcceptor.SomeBillAcceptorEvent += 
            new SomeBillAcceptorEventHandler(changeText);
    }

    private void changeText(object sender, EventArgs args)
    {
        _screen2.changeValue("some text");
    }
}

So the most annoying thing is that sometimes everything actually works... So my question is "do I have to use Invoke here?" or how do I solve this with less changes to the application...

user568021
  • 1,426
  • 5
  • 28
  • 55
  • Already answered, so I'll just add this comment: "Everything that interacts with the UI must be on the main (UI) thread". Any other (worker) threads must use Method.Invoke before interacting with the UI directly. – Scott Smith Jun 11 '20 at 21:28

4 Answers4

4

In your handler. do something like this.

        if (this.InvokeRequired)
        {
            Invoke(new MethodInvoker(() => 
            {
                _screen2.changeValue("some text");
            }));
        }
        else
        {
            _screen2.changeValue("some text");
        }

I would guess that the event is being raised on a seperate thread other that the main UI thread.

pdiddy
  • 6,217
  • 10
  • 50
  • 111
2

Yes you need to use Invoke if there is a possibility of that method being called from a different thread.

You can check this.InvokeRequired(), if true, then use invoke, if false do a normal call.

Sebastian Piu
  • 7,838
  • 1
  • 32
  • 50
0

This occurs due to thread unsafe call

You should make only thread safe calls in program

Check this link.

sonu thomas
  • 2,161
  • 4
  • 24
  • 38
0

The short answer is yes, you must use Invoke. See this question and its accepted answer if you need details.

The reason the exception is only thrown some of the time, by the way, comes down to timing. You currently have a race condition in which sometimes you get lucky and sometimes you don't.

By the way, here is pretty handy pattern for this sort of thing.

  1. Refactor any code that sets form values into its own private void method(s).
  2. In this new method, call InvokeRequired. If it returns true, call Invoke, passing the current method so as to recurse back into it. If it returns false, go ahead and make the change.
  3. Call this new method from the event handler.

For example:

private void ChangeScreen2() {
    if (this.InvokeRequired) {
        this.Invoke(new MethodInvoker(ChangeScreen2));
    }
    else {
        _screen2.changeValue("some text");
    }
}

private void changeText(object sender, EventArgs args)
{
    ChangeScreen2();
}

The idea being that you sequester all code that modifies the form into these methods that always begin with a check of InvokeRequired and always Invoke themselves if so required. This pattern works with .NET 1.0 onward. For even neater approach, see the accepted answer to this question, which works with .NET 3.0 and later.

Community
  • 1
  • 1
dgvid
  • 26,293
  • 5
  • 40
  • 57