Wayneio
Wayneio

Reputation: 3566

Cross thread error, but not using threads

I keep getting

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

on various controls on various forms in a project, and I have googled it and found lot's of responses about how to access stuff from various threads, but as far as I know, i'm not using any other threads in my project, and to change the hundreds of possible places in the code would be unmanageable.

It never used to happen, only since I added various code that seems unrelated. I include a sample of places where I get the errors below, but it has occurred in so many places all over the solution.

keyholderTxt.Text = "Keyholders Currently In:\r\n \r\n Nibley 1: + keyholders";

or this, a better example, as you can see everything that happends from the form loading until the error:

      private void Identification_Load(object sender, System.EventArgs e)
    {
        _Timer.Interval = 1000;
        _Timer.Tick += new EventHandler(_Timer_Tick);
        _Timer.Start();

        txtIdentify.Text = string.Empty;
        rightIndex = null;

        SendMessage(Action.SendMessage, "Place your finger on the reader.");

        if (!_sender.OpenReader())
        {
            this.Close();
        }

        if (!_sender.StartCaptureAsync(this.OnCaptured))
        {
            this.Close();
        }
    }

    void _Timer_Tick(object sender, EventArgs e)
    {
        this.theTime.Text = DateTime.Now.ToString();
    }

    private void OnCaptured(CaptureResult captureResult)
    {
       txtIdentify.Clear();
       //other stuff after the cross thread error
    }

Can things like not closing datareaders cause this kind of error?

I am using a Windows Forms Application.

Upvotes: 4

Views: 1061

Answers (4)

Thomas Levesque
Thomas Levesque

Reputation: 292425

I suspect the culprit is this:

if (!_sender.StartCaptureAsync(this.OnCaptured))

I don't know the API you're using, but based on the name, I think the callback method (OnCaptured) is called on a worker thread, not the UI thread. So you need to use Invoke to perform the action on the UI thread:

private void OnCaptured(CaptureResult captureResult)
{
   if (InvokeRequired)
   {
       Invoke(new System.Action(() => OnCaptured(captureResult)));
       return;
   }

   txtIdentify.Clear();
   // ...
}

Upvotes: 7

Jim Mischel
Jim Mischel

Reputation: 133995

Okay, scratch this. I see you're using System.Windows.Forms.Timer which, as the comment below mentions, already executes on the UI thread. I was thinking you were using System.Timers.Timer.

Wrong answer

The timer callback is executing on a threadpool thread. You can make it execute on the UI thread by setting the SynchronizingObject:

    _Timer.Interval = 1000;
    _Timer.Tick += new EventHandler(_Timer_Tick);
    _Timer.SynchronizingObject = this;
    _Timer.Start();

Upvotes: 3

Gusdor
Gusdor

Reputation: 14334

The callback from _Timer (void _Timer_Tick(object sender, EventArgs e))is occurring on a background thread. Make sure you use a System.Windows.Forms.Timer (assuming you are using windows forms) if you want the callback to be on the UI thread.

As commenters have suggested. Check the thread window in your debugger to check what thread the exception is occurring on.

Alternatively, for windows forms, try this

void _Timer_Tick(object sender, EventArgs e)
{
    this.BeginInvoke(new Action(() => this.theTime.Text = DateTime.Now.ToString()));
}

And for WPF, try this

void _Timer_Tick(object sender, EventArgs e)
{
    this.Dispatcher.BeginInvoke(new Action(() => this.theTime.Text = DateTime.Now.ToString()));
}

And if this is not a control or window and you are in WPF

void _Timer_Tick(object sender, EventArgs e)
{
    System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => this.theTime.Text = DateTime.Now.ToString()));
}

Upvotes: 1

Helic
Helic

Reputation: 949

Have you checked the thread panel in VS?

Upvotes: 1

Related Questions