views:

5250

answers:

4

I have a problem like this :

I have a Form named MainForm. I have a long operation to be taken place on this form.

While this long operation is going on, I need to show another from named ProgressForm on top of the MainForm.

ProgressForm contains a progress bar. Which needs to be updated while the long operation is taking place.

After the Long operation is completed, the ProgressForm should be closed automatically.

I have written some code like the following:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace ClassLibrary
{
    public class MyClass
    {
        public static string LongOperation()
        {
            Thread.Sleep(new TimeSpan(0,0,30));

            return "HelloWorld";
        }
    }
}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace BackgroungWorker__HelloWorld
{
    public partial class ProgressForm : Form
    {
        public ProgressForm()
        {
            InitializeComponent();
        }

        public ProgressBar ProgressBar
        {
            get { return this.progressBar1; }
            set { this.progressBar1 = value; }
        }
    }
}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using ClassLibrary;

namespace BackgroungWorker__HelloWorld
{
    public partial class MainForm : Form
    {
        ProgressForm f = new ProgressForm();

        public MainForm()
        {
            InitializeComponent();  
        }

        int count = 0;
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (f != null)
            {
                f.ProgressBar.Value = e.ProgressPercentage;
            }

            ++count;
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled) 
            {  
                MessageBox.Show("The task has been cancelled");  
            }  
            else if (e.Error != null)  
            {                  
                MessageBox.Show("Error. Details: " + (e.Error as Exception).ToString());  
            }  
            else 
            {  
                MessageBox.Show("The task has been completed. Results: " + e.Result.ToString());  
            }
        }


        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            if (f == null)
            {
                f = new ProgressForm();
            }

            f.ShowDialog();

            //backgroundWorker1.ReportProgress(100);

            MyClass.LongOperation();

            f.Close();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();

            this.Close();
        }
    }
}

I am not finding the way to update the progressBar.

Where should I place backgroundWorker1.ReportProgress() and how should I call this?

I must not make any change in MyClass. Coz, I don't know what would happen or how long would it take to complete the operation in this layer of my application.

Can anyone help me?

+5  A: 

One problem is that you're sleeping for 30 seconds. Normally you'd call ReportProgress at various points within your long-running task. So to demonstrate this, you might want to change your code to sleep for 1 second, but 30 times - calling ReportProgress each time it finishes a sleep.

Another problem is that you're showing your ProgressForm from the background thread. You should start it in the UI thread, but hook the background worker's ProgressChanged event to it. Then when the background worker reports progress, the progress form will be updated.

Jon Skeet
I must not make any change in MyClass. Coz, I don't know what would happen in this layer of my app.
JMSA
Then you can't accurately report progress. I mean you can add a timer into the `ProgressForm` to just increment the progress bar every second, but that will only provide an illusion. If you've got no way of finding out *actual* progress, then how useful is a progress bar?
Jon Skeet
+1  A: 

ReportProgress is the method which you'll have to call in your 'working' method. This method will raise the 'ProgressChanged' event.

In your form, you can attach an eventhandler to the ProgressChanged event. Inside that eventhandler, you can change the position of the progressbar. You can also attach an eventhandler to the RunWorkerCompleted event, and inside that eventhandler, you can close the form which contains the progressbar.

Frederik Gheysels
This is a general solution I guess. Plz read the Problem again.
JMSA
+2  A: 

It should be noted you need to set

backgroundWorker1.WorkerReportsProgress = true;

if you want the background worker to raise the ProgressChanged event and

backgroundWorker1.WorkerSupportsCancellation = true;

if you wish to be able to cancel the worker thread.

As the others have said, run the f.ShowDialog() in your UI thread and use ProgressChanged to update the ProgressWindow.

To get the ProgressForm to self close, in backgroundWorker1_RunWorkerCompleted, call f.Close(); This means the long operation has completed and we can close the window.

Alastair Pitts
What is the benefit of calling ProgressWindow from the UI thread?
JMSA
A: 

I'm facing the same kind of problem (long running task on form loading in an MDI application). That's why I'm interested in this solution. Has anyone build a trully functional sample ? Because, when I try to close the form in backgroundWorker1_RunWorkerCompleted, I've got the following error occurs : "Opération inter-threads non valide : le contrôle 'ProgressForm' a fait l'objet d'un accès à partir d'un thread autre que celui sur lequel il a été créé."

Thanks for your responses.

WF
seperate question required, dont post a question as an answer.
Anonymous Type