views:

36

answers:

3

Hi all,

I am designing a form in which I have to increase a Progress bar while an operation is being performed simultainously (In other words, I am showing progress of this operation). This operation takes 50seconds. So I have used a System.Timer Timer to Increase the Progress bar.

There isn't a single thread in my code. When I write Progress_bar.PerformStep() in Timer Event Handler, it gives error as "Cross Thread Operation Not Valid".

[From this error I analyzed that System.Timer must be creating a Thread and Runs the timer in it to peform multiple tasks.]

What should I do to increase my progress bar after every Second?

I tried solution given in this question. It removed the error but Now I can't see the progress bar increasing. Means it Starts.... No Increase for 50 sec and after it 100%.

Code is as follows:

Timer Declaration (It is Global):

public System.Timers.Timer Thetimer = new System.Timers.Timer(1000);

Event Declaration (This is in Constructor to make it...err...Public [May not be a correct word]):

Thetimer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);

Call:

 Thetimer.Start();

 blRetVal = FunctionToBeExecuted(parameter);

 Thetimer.Stop();

Event Handler:

 void _timer_Elapsed(object sender, ElapsedEventArgs e)
        {
           //StatusBar.PerformStep();  -- Tried This. It gives the Error

/* This doesn't give error but Another issue Arises */

            if (InvokeRequired)
            {
                BeginInvoke(new Action(StatusBar.PerformStep));

            }
            else
                StatusBar.PerformStep();

        }

Regards,

Swanand!

P.S. I am using C# and Visual Studio 2008

+3  A: 

It sounds like you're performing your "background" operation on the main thread, which is why your progress bar doesn't update when you invoke it.

Have a look at BackgroundWorker.

Jon B
Yes...That Background task is being perfomed in main thread.... It's not actually a Background task.... It's the language problem... I wanted to say a Task is executing and I want to increase progress bar simultainously! Sorry!
Swanand Purankar
@Swanand - any time you have work that will take any real time (certainly 50sec), you should perform the work in a background thread. `BackgroundWorker` is often times the best way to do this.
Jon B
I have a function which performs this Operation. So according to You, I should move this function in a BackgroundWorker.... Okey! Let me try this. But Any other option which may not involve bigger change!! and Thanks Jon for this solution!
Swanand Purankar
@Swanand - you can also create a new thread and use it to run your existing function. BackgroundWorker is probably going to be less work in the end.
Jon B
That's the thing.... I don't want threads! :(
Swanand Purankar
@Swan - in either case you're going to have a thread. There's no avoiding it. If you perform a long operation in the main thread it will "hang" your application.
Jon B
+1 for being right.
steinar
+2  A: 

OK. Jon B is right. You'll have to have the long running task in a thread, there is no way around that. Simplified, you're doing this:

public partial class Form1 : Form
{
    // ...

    public System.Timers.Timer timer = new System.Timers.Timer(1000); 

    private void Form1_Load(object sender, EventArgs e)
    {
        timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_elapsed);

        timer.Start();

        // Simulates your long running task (FunctionToBeExecuted)
        // NOTE: This freezes the main UI thread for 10 seconds, 
        //       so nothing will be drawn *at all*            
        Thread.Sleep(10000); 

        timer.Stop(); 

    }

    void timer_elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        if (InvokeRequired) 
            this.BeginInvoke(new Action(progressBar1.PerformStep));            
        else
            progressBar1.PerformStep(); 
    }        
}

As you can see in the Load event, you're not only halting the progress bar, you're halting the main UI thread. That's just not acceptable to most users and all good developers should have another option in their toolset.

The only way around this (except running another process) is running the task in a different thread. One of the easiest ways is using a BackgroundWorker, it's really easy. Here are the changes you need:

public partial class Form1 : Form
{            
    // ...

    private void Form1_Load(object sender, EventArgs e)
    {                                              
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        worker.RunWorkerCompleted += 
          new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
        worker.RunWorkerAsync();

    }

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // Your work is completed, not needed but can be handy
        // e.g. to report in some way that the work is done:
        progressBar1.Value = progressBar1.Maximum;
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_elapsed);
        timer.Start();

        // Simulates your long running task (FunctionToBeExecuted)
        // Your main UI thread is free!
        Thread.Sleep(10000);

        timer.Stop();
    }

    // ...
}
steinar
+1 for writing the code :)
Jon B
Not Working! :( No update in Progress Bar and the window hangs for that period. One thing is I can not shift "FunctionToBeExecuted" to a thread as it is a very complex function containing lot of function call. So I put Thread.Sleep(51*1000) [A Added Second as a measure]. Still there is no update in Progress bar. And Window becomes responsive after completion of "FunctionToBeExecuted"
Swanand Purankar
You'll have to move FunctionToBeExecuted to another thread. It doesn't matter if it runs a lot of other stuff. That stuff doesn't have to be in another thread. NOTE: There is absolutely no way around this, trust us.
steinar