0

I have two threads that look like this

pbDB_running = true; // start progress bar

Thread connectDb = new Thread(new ThreadStart(ConnectToDb));
Thread runProgress = new Thread(new ThreadStart(RunpbDB));

connectDb.Start();
runProgress.Start();

connectDb.Join();  //wait untill the connection is done

pbDB_running = false; //stop the progress bar

as you probably might have guessed, ConnectToDb is used to make a connection to a database, while runpbDB is making a progress bar run on the interface. The progress bar (pbDB) is a Windows.Forms control created with drag and drop on the design view. The runProgress thread is running RunpbDB() wich looks like this :

private void RunpbDB()
{
    while (pbDB_running)
    {
        if (pbDB.Value == 100) pbDB.Value = 0;
        else pbDB.Value += 1;
    }
    pbDB.Value = 0;
} 

When the two threads start I get the following exception inside RunpbDB() :

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

What can I do to overcome this situation?

peter
  • 143
  • 2
  • 4
  • 10

6 Answers6

2

Have you thought about using a BackgroundWorker? This might make your life a lot easier. You could set two up, one for your database call and the other for your progress bar. Just listen for the background workers ProgressChanged and RunWorkerCompleted events.

More information on MSDN

Kevin Brydon
  • 12,524
  • 8
  • 46
  • 76
  • BackgroundWorker will not work here because peter wants to show the progress on UI thread. – ZafarYousafi Mar 23 '13 at 14:52
  • @ZafarYousafi BackgroundWorker would be fine for what Peter want's to achieve. Yes, in the current form, using the global `pbDB_running` would not work but rework the code to fit the background worker and Peter can achieve what he wants. – Kevin Brydon Mar 23 '13 at 14:57
  • @KevinBrydon Idid'nt think this in that way. +1 from me. – ZafarYousafi Mar 24 '13 at 03:18
1

Use Control.Invoke method to remedy this problem. The whole your solution will became

private void RunpbDB()
{
    while (pbDB_running)
    {
        Invoke((Action)(()=>{
        if (pbDB.Value == 100) pbDB.Value = 0;
        else pbDB.Value += 1;}));
    }
        Invoke((Action)(()=>{pbDB.Value = 0;});
} 
Blablablaster
  • 3,238
  • 3
  • 31
  • 33
  • In this case, runProgress thread is executing only the first Invoke and then executes the rest of the RunpbDB function only after the connectDB finished it's job. – peter Mar 23 '13 at 15:03
1

You can use something along the lines of pbDB.InvokeRequired and if so, call pbDB.Invoke to perform your action back on the UI thread.

You do not need the check if you know it will always be done on a separate thread than the UI thread.

Here is a link to some code on this and other ways to accomplish this.

You could also use a BackgroundWorker

Justin Pihony
  • 66,056
  • 18
  • 147
  • 180
1

This is a security imposed by microsoft for its .NET technology. It basically happens when you access a winforms element from a separate thread, i.e. not in the main thread where the GUI winforms is running. The solution is to create a delegate for your RunpbDB method. See the solution here Best Way to Invoke Any Cross-Threaded Code?. in here too: How to update the GUI from another thread in C#?

Community
  • 1
  • 1
TravellingGeek
  • 1,581
  • 11
  • 16
1

Just make your life easier and use a BackgroundWorker for this if you don't have access to .NET 4.0. If you can use 4.0+, use the TPL. And if you can use 4.5, you can use the new async/await functionality. There are tons of examples here on Stack Overflow. Here is a link from Stephen Cleary comparing them.

Bryan Crosby
  • 6,486
  • 3
  • 36
  • 55
0

Cross thread operation call when UI thread is involved was discouraged by VS team in 2.0(before that it was possible for security reasons. There are two ways to overcome this issue. Easy way is to set the static property

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls

to false which then disables this check globally from your application. But this solution is not advised by any one and not even by me since it again opens the security holes. Another ways is, in the method first check if UI control needs Invoke,If so then use control's invoke method to invoke the current method again and then return. Code can better clear what I want to say so

private void RunpbDB()
    {
        if (pbDB.InvokeRequired)
        {
            pbDB.Invoke(new Action(RunpbDB));
            return;
        }
        while (pbDB_running)
        {
            if (pbDB.Value == 100) pbDB.Value = 0;
            else pbDB.Value += 1;
        }
        pbDB.Value = 0;
    }
ZafarYousafi
  • 8,640
  • 5
  • 33
  • 39