Hey everyone,

I'm trying to do something that I have never done, I have an external class that does a lot of calculations (usually takes about 10 minutes to finish).

The iterations usually run from 0 to 100 000, so I would like to have a progress bar that iterates between those values on my main windows form. Also a new thread needs to be created since my windows form freezes until the calculation is finished.

If the calculations was done within my windows form code it would have been easy since I could have used a normal progress bar and the backgroundworker class for an extra thread. But I have an external class that I call, so I'm not sure how to pass data to my main windows form while the external class executes the code.

Please let me know if I'm not clear enough on what I want to do.

Any help would be appreciated.

Thanks!

Recommended Answers

All 10 Replies

Check out the BackgroundWorker class. Basically you put all your execution handling into its DoWork event handler, then call RunWorkerAsync on the instance to set it to run. You can use the ProgressChanged event handler to report the progress back to your GUI thread, and the RunWorkerComplete event handler is fired when the worker finishes its work. There are heaps of online examples of this process.

http://msdn.microsoft.com/en-us/library/ms228963.aspx
or maybe http://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents.aspx


I'm not sure how it fits into your design but couldn't you "Raise" an event every time the progress bar needs updated? Do you know what a delegate is? You may also be able to call an update function directly as opposed to raising an event.

Alternatively maybe this would be fine:

while(something)
{
Application.DoEvents();
}

http://msdn.microsoft.com/en-us/library/ms228963.aspx
or maybe http://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents.aspx


I'm not sure how it fits into your design but couldn't you "Raise" an event every time the progress bar needs updated? Do you know what a delegate is? You may also be able to call an update function directly as opposed to raising an event.

Alternatively maybe this would be fine:

while(something)
{
Application.DoEvents();
}

Hey,

Thank you very much for your reply! No I don't know what a delegate is... My knowledge is limited.

I will go read on events of that I also don't know anything.

Basically my design looks like this:

Windows form that calls a static function in an external class
The function in the external class does the calculations which can take many minutes to complete. After completion the calculated arrays are returned to the windows form procedure.

So basically my windows form freezes for that period, and I cannot update any progress bar since the class is external.

But I found a good event and delegate tutorial think that might be the key as you proposed

Take a look at this simple example, which shows how to use a new thread (thread is busy with some loop), and shows how to use delegates to update progressBar:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
 
namespace WFInvoke
{
    public partial class Form1 : Form
    {     
        public Form1()
        {
            InitializeComponent();
        }
 
        [STAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }
 
        private  void IncrementMe()
        {
            for (int i = 0; i < 100; i++)
            {
                UpdateProgress();
                Thread.Sleep(100);
            }
            if(InvokeRequired)
            {
                Invoke(new MethodInvoker(Close));
            }
            else
            {
                Close();
            }
        }
 
        private  void UpdateProgress()
        {
            if (this.progressBar1.InvokeRequired)
            {
                MethodInvoker updateProgress = UpdateProgress;
                progressBar1.Invoke(updateProgress);
            }
            else
            {
                progressBar1.Increment(1);
            }
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            ThreadStart threadStart = IncrementMe;
            threadStart.BeginInvoke(null, null);
        }       
    }
}

Mitja thanks for the code... I cannot see how that code will enable me to update a progressbar from one class to another class. On the same class yes but not from different classes.

My knowledge about C# is not much so please excuse my incompetence.

YOu wanna show a progressBar on another Form?
Simply create the method to update progressBar (like is mine "UpdateProgress" method) on another form, mark it as Public, so you can access to it from the main form.
Thats all.

Ok, drop a BackgroundWorker component onto your form, rename it bgWorker, and use this code as an example:

private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // do some long-winded process here
            // this is executed in a separate thread
            int maxOps = 1000000;
            for (int i = 0; i < maxOps; i++)
            {
                rtbText.AppendText(i.ToString() + "\r\n");
                // report progress as a percentage complete
                bgWorker.ReportProgress(100 * i / maxOps);
            }
        }

        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // update the progress bar
            pbProgress.Value = e.ProgressPercentage;
        }

        private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // return to "normal" mode of operation
            this.Cursor = Cursors.Default;
            btnGo.Enabled = true;
        }

        private void btnGo_Click(object sender, EventArgs e)
        {
            // give the appearance of something happening
            this.Cursor = Cursors.WaitCursor;
            btnGo.Enabled = false;
            // call RunWorkerAsync to start the background thread
            bgWorker.RunWorkerAsync();
        }

I think this solution is much cleaner, and all of the thread handling is taken care of for you.

EDIT: In my example, btnGo is a button, pbProgress is the progress bar, rtbText is a rich text box, and bgWorker is the background worker.

YOu wanna show a progressBar on another Form?
Simply create the method to update progressBar (like is mine "UpdateProgress" method) on another form, mark it as Public, so you can access to it from the main form.
Thats all.

Thanks I will try although I'm still a bit confused

Ok, drop a BackgroundWorker component onto your form, rename it bgWorker, and use this code as an example:

private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // do some long-winded process here
            // this is executed in a separate thread
            int maxOps = 1000000;
            for (int i = 0; i < maxOps; i++)
            {
                rtbText.AppendText(i.ToString() + "\r\n");
                // report progress as a percentage complete
                bgWorker.ReportProgress(100 * i / maxOps);
            }
        }

        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // update the progress bar
            pbProgress.Value = e.ProgressPercentage;
        }

        private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // return to "normal" mode of operation
            this.Cursor = Cursors.Default;
            btnGo.Enabled = true;
        }

        private void btnGo_Click(object sender, EventArgs e)
        {
            // give the appearance of something happening
            this.Cursor = Cursors.WaitCursor;
            btnGo.Enabled = false;
            // call RunWorkerAsync to start the background thread
            bgWorker.RunWorkerAsync();
        }

I think this solution is much cleaner, and all of the thread handling is taken care of for you.

EDIT: In my example, btnGo is a button, pbProgress is the progress bar, rtbText is a rich text box, and bgWorker is the background worker.

Hey thank you very much!

This worked to some extent but I still have two problems:

I cannot incrementally update the progress bar since the method that is invoked exists in an external class. I will have to use what is called delegates although I'm still confused how to use them exactly but I will figure it out eventually :)

The second problem is VS throws a serious error: "You cannot update a control that was created on another thread". What I do is I update a table created in the normal thread with new data generated in the backgroundworker thread, and VS says it is impossible. See below for exact error text. How can I bypass this problem?

Cross-thread operation not valid: Control 'dataGV' accessed from a thread other than the thread it was created on.

Thanks!

I did an example code how to use Threading and progressBar on two forms.
You can download a project here.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.