Solved

ToolStripProgressBar  -  increment in a thread - keep getting errors!

Posted on 2006-06-19
9
3,623 Views
Last Modified: 2012-05-05

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


I can't seem to increment anything or access any form elements because of the error above....

How can I make it threadsafe to access my progress bar?

I have my main form, and a seperate class entirely that just manages a busy state

using (StatusBusy busy = new StatusBusy())
{

//do code here so when this code block exits the status busy section exits.


}


Inside my StatusBusy class it is currently just modifying a label on the form, but I want to have the progress bar increment...  

how can I do this in a background thread, without having the above error thrown ?   A simple new thread creation, or even putting it on the threadpool won't work...

0
Comment
Question by:bswiftly
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
9 Comments
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 16939778
Here is the basic idea...Form has a Button (button1) and a ProgressBar (progressBar1):

    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }

        private MyClass mc = null;
       
        private void button1_Click_1(object sender, EventArgs e)
        {
            if (mc == null)
            {
                button1.Enabled = false;
                mc = new MyClass();
                mc.ProgressUpdate += new MyClass.ProgressUpdateDelegate(mc_ProgressUpdate);
                mc.ProcessComplete += new MyClass.ProcessCompleteDelegate(mc_ProcessComplete);
                mc.Start();
            }
        }
     
        void mc_ProgressUpdate(int curValue, int maxValue)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MyClass.ProgressUpdateDelegate(mc_ProgressUpdate), new object[] {curValue, maxValue });
            }
            else
            {
                this.progressBar1.Value = (int)((double)curValue / (double)maxValue * (double)this.progressBar1.Maximum);
            }
        }

        void mc_ProcessComplete()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MyClass.ProcessCompleteDelegate(mc_ProcessComplete));
            }
            else
            {
                mc = null;
                button1.Enabled = true;          
            }
        }

        private class MyClass
        {

            public delegate void ProgressUpdateDelegate(int curValue, int maxValue);
            public event ProgressUpdateDelegate ProgressUpdate;

            public delegate void ProcessCompleteDelegate();
            public event ProcessCompleteDelegate ProcessComplete;
           
            private System.Threading.Thread thread = null;

            public void Start()
            {
                if (thread == null)
                {
                    thread = new System.Threading.Thread(new System.Threading.ThreadStart(Worker));
                    thread.Start();
                }
            }

            private void Worker()
            {
                for (int i = 1; i <= 5; i++)
                {
                    ProgressUpdate(i, 5);
                    System.Threading.Thread.Sleep(1000);
                }
                ProcessComplete();
            }

        }

    }
0
 
LVL 1

Author Comment

by:bswiftly
ID: 16939800
I guess that might work the way I was used to having it work, where MyClass could  call the ProcessComplete(); method on dispose, and I could use the code block as normal.

I will let you know if this works.. sure is a lot of work for a little progress bar!

0
 
LVL 1

Author Comment

by:bswiftly
ID: 16939824
Oh wait, MyClass is an inner class, hmm..  I think I'll need to make a few modifications to make this code really easy to work, because my setup is a little more modular than the code you gave me.  

1) I don't want to have to wire the event handlers everytime I use the code block, so I'll want the event handlers inside MyClass.
2) MyClass can't be an inner class of Form1.
3) having this loop for 5 seconds and end is arbitrary, if this takes 10 seconds the status bar is stationary for the last 5 seconds, this is why I want a continuous loop, and will try to get the processComplete() method to be called on the dispose of MyClass, so when the code block finishes it will stop it.

goal: as I stated in the question:

using(MyClass mc = new MyClass())
{
//progress bar is changing while code in this block executes.
}

//progress bar stops when the above block is finished.
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 1

Author Comment

by:bswiftly
ID: 16939895
seems like I can't wire up those events from the MyClass constructor instead.  

This method is a little too clunky, because I want a VERY simple interface for GUI developers to use, without them having to remember to wire up the events everytime.

its essential that I use the previous method of the code block as I mentioned above.
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 16940296
1) I don't want to have to wire the event handlers everytime I use the code block, so I'll want the event handlers inside MyClass.

    How else do you expect your class to communicate back to the form then?...

2) MyClass can't be an inner class of Form1.

    It doesn't need to be.  The class can be anywhere...

3) having this loop for 5 seconds and end is arbitrary, if this takes 10 seconds the status bar is stationary for the last 5 seconds, this is why I want a continuous loop, and will try to get the processComplete() method to be called on the dispose of MyClass, so when the code block finishes it will stop it.

    You can only continously update the progressbar if your class is actually raising continous events.  Otherwise you might as well just put a regular old timer on your form and increment the progressbar every XXX seconds.  If you reach the max then reset it back to zero and repeat.  When you receive the "ProcessComplete" event then turn off your timer.   This approach would almost completely decouple the "progress" from the class though...


Give us a bigger picture of what you are doing and we may be able to suggest a better alternative approach...
0
 
LVL 1

Author Comment

by:bswiftly
ID: 16943036
well as per #1 - its just that I'm using this in enough places that the code would look kind of ugly  - especially because its all component based and depending where I was at I'd have to get a reference to the main form.. not all that hard, but still..what I have right now displays a label custom to the code block.  ie:

using(StatusBusy busy = new StatusBusy("Logging in . . . ")
{
}

sets the status bar to logging in, when the log in is done, the dispose method resets the status bar.  

My goal is to make it as easy as that, and hopefully not have to re-write any of those code blocks..  I use it frequently enough that with the code wiring up events 20 places in each form its just not slick enough (for me anyways - but your solution was helpful).

I just think there must be an easier way to do this...
0
 
LVL 86

Accepted Solution

by:
Mike Tomlinson earned 50 total points
ID: 16946060
The only way I see this working is for you to pass in references to the controls you want to update:

    using(StatusBusy busy = new StatusBusy(progressBar1, someOtherControls, passedInHere, "Logging in . . . ")
    {
    }


Something like that.  Then you follow the same pattern as I showed above.  From within your class you can use a Delegate and the Invoke() method to update the controls from you seperate thread.

Instead of:

    this.Invoke(...)

You would use the control itself:

    progressBar1.Invoke(...)
0
 

Expert Comment

by:Lopsy
ID: 21878599
try this
 this.BeginInvoke(new MethodInvoker(delegate()
   3.
           {
   4.
                 this.progressMainBar.Increment(1);
   5.
           }
   6.
      ));
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Extention Methods in C# 3.0 by Ivo Stoykov C# 3.0 offers extension methods. They allow extending existing classes without changing the class's source code or relying on inheritance. These are static methods invoked as instance method. This…
Entity Framework is a powerful tool to help you interact with the DataBase but still doesn't help much when we have a Stored Procedure that returns more than one resultset. The solution takes some of out-of-the-box thinking; read on!
Although Jacob Bernoulli (1654-1705) has been credited as the creator of "Binomial Distribution Table", Gottfried Leibniz (1646-1716) did his dissertation on the subject in 1666; Leibniz you may recall is the co-inventor of "Calculus" and beat Isaac…

733 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question