Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

ToolStripProgressBar  -  increment in a thread - keep getting errors!

Posted on 2006-06-19
9
Medium Priority
?
3,691 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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
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 200 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

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

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

Introduction This article series is supposed to shed some light on the use of IDisposable and objects that inherit from it. In essence, a more apt title for this article would be: using (IDisposable) {}. I’m just not sure how many people would ge…
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…
Video by: ITPro.TV
In this episode Don builds upon the troubleshooting techniques by demonstrating how to properly monitor a vSphere deployment to detect problems before they occur. He begins the show using tools found within the vSphere suite as ends the show demonst…
In this video, Percona Solution Engineer Dimitri Vanoverbeke discusses why you want to use at least three nodes in a database cluster. To discuss how Percona Consulting can help with your design and architecture needs for your database and infras…

721 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