Solved

ToolStripProgressBar  -  increment in a thread - keep getting errors!

Posted on 2006-06-19
9
3,581 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
 
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
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
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

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

This article describes a simple method to resize a control at runtime.  It includes ready-to-use source code and a complete sample demonstration application.  We'll also talk about C# Extension Methods. Introduction In one of my applications…
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!
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

747 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now