[Webinar] Streamline your web hosting managementRegister Today

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 3743
  • Last Modified:

ToolStripProgressBar - increment in a thread - keep getting errors!


"{"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
bswiftly
Asked:
bswiftly
  • 4
  • 3
1 Solution
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
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
 
bswiftlyAuthor Commented:
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
 
bswiftlyAuthor Commented:
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
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
bswiftlyAuthor Commented:
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
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
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
 
bswiftlyAuthor Commented:
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
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
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
 
LopsyCommented:
try this
 this.BeginInvoke(new MethodInvoker(delegate()
   3.
           {
   4.
                 this.progressMainBar.Increment(1);
   5.
           }
   6.
      ));
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now