Solved

ToolStripProgressBar  -  increment in a thread - keep getting errors!

Posted on 2006-06-19
9
3,616 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
  • 4
  • 3
9 Comments
 
LVL 85

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
Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
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 85

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 85

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

Networking for the Cloud Era

Join Microsoft and Riverbed for a discussion and demonstration of enhancements to SteelConnect:
-One-click orchestration and cloud connectivity in Azure environments
-Tight integration of SD-WAN and WAN optimization capabilities
-Scalability and resiliency equal to a data center

Question has a verified solution.

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

This article introduced a TextBox that supports transparent background.   Introduction TextBox is the most widely used control component in GUI design. Most GUI controls do not support transparent background and more or less do not have the…
We all know that functional code is the leg that any good program stands on when it comes right down to it, however, if your program lacks a good user interface your product may not have the appeal needed to keep your customers happy. This issue can…
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…
This video shows how to use Hyena, from SystemTools Software, to bulk import 100 user accounts from an external text file. View in 1080p for best video quality.

831 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