Working with progressbar while updating another control.

ddanbe 1 Tallied Votes 3K Views Share

Drawing on a Form does not always happen when you want it. The Refresh() method of a control comes in handy here.
See the following snippet and experiment by commenting out the Refresh.

Start a new WindowsForm project. From the toolbox, add a Label, ProgressBar and Button control on the Form. Set the Visible properties of the Label and ProgressBar to false. Double click the button and fill in the click handler with the code provided. Run the app.

private void button1_Click(object sender, EventArgs e)
        {
            const string message = "Counting the grains of sand on beach ";

            progressBar1.Visible = true;
            progressBar1.Minimum = 1;
            progressBar1.Maximum = 10; // as an example we have 10 long actions to perform
            progressBar1.Step = 1;
            label1.Visible = true;
            
            int i = 1;
            while (i <= 10)
            {
                progressBar1.PerformStep(); // update progress bar
                label1.Text = message + i.ToString(); // set message to display
                label1.Refresh(); // needed or we won't see the label update
                System.Threading.Thread.Sleep(500);  // simulate an action that takes time
                i++;
            }
            label1.Visible = false;
            progressBar1.Visible = false;
            MessageBox.Show("The work is done!", "Notification", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
DanielGreen 6 Light Poster

hi!

i think the better way is, that you use a second thread.
- one for the GUI
- one for the worker

in the worker-thread ( maybe you will use a backgroundwoker ) you will do your time-consuming action and "invoke" the progressbar/label to do your GUI-action.

In your code the line 17. System.Threading.Thread.Sleep(500) will block the whole GUI-thread. So its possible that the refresh in your case will not have the desired effect.

if i have enough time, i will post the code with 2 threads later

hope it helps
Daniel

ddanbe 2,724 Professional Procrastinator Featured Poster

Hi, Daniel
I appreciate your feedback very much!
The whole point of my thread is not so much about handling long tasks. I hoped that was clear by my example string of counting grains of sand:-O (A ridicilous thing to do!)
The point is the fact that I had to use a Refresh to see the text of the label getting updated. The rest of the code is just fill in to make it a little "interesting".
But I'm looking forward to see your snippet, because threading is still a subject I know too little of.

See ya

DanielGreen 6 Light Poster

Hi ddanbe!

i post a very simple example using a backgroundworker. If you need more informations about it, MSDN,codeproject and goolge offers a lot of good tutorials.

In my example i start a backgroundthread, that do "exactly" nothing, but there you can do nearly everything (time-consuming operatings like counting grains of sand), but nothing for GUI.
If you want to refresh/update an GUI-Element like the progressbar or lable, you can use the ReportProgress - function with an value or you use the invoke-function for the control.

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace TestThread
{
	public class ThreadTestForm : Form
	{
		private const int cMaxValue = 100;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.IContainer components = null;

		private System.Windows.Forms.Button btnStart;
		private System.Windows.Forms.Button btnCancel;
		private System.Windows.Forms.ProgressBar progBar;
		private System.Windows.Forms.Label lblState;
		private System.ComponentModel.BackgroundWorker bgWorker;

		private delegate void VoidDelegate(int state);

		public ThreadTestForm()
		{
			InitializeComponent();

			this.PrepareControls4Start();
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing && (components != null))
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}

		#region Windows Form Designer generated code

		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.btnStart = new System.Windows.Forms.Button();
			this.btnCancel = new System.Windows.Forms.Button();
			this.progBar = new System.Windows.Forms.ProgressBar();
			this.lblState = new System.Windows.Forms.Label();
			this.bgWorker = new System.ComponentModel.BackgroundWorker();
			this.SuspendLayout();
			// 
			// btnStart
			// 
			this.btnStart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
			this.btnStart.Location = new System.Drawing.Point(205, 12);
			this.btnStart.Name = "btnStart";
			this.btnStart.Size = new System.Drawing.Size(75, 23);
			this.btnStart.TabIndex = 0;
			this.btnStart.Text = "Start";
			this.btnStart.UseVisualStyleBackColor = true;
			this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
			// 
			// btnCancel
			// 
			this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
			this.btnCancel.Enabled = false;
			this.btnCancel.Location = new System.Drawing.Point(205, 41);
			this.btnCancel.Name = "btnCancel";
			this.btnCancel.Size = new System.Drawing.Size(75, 23);
			this.btnCancel.TabIndex = 1;
			this.btnCancel.Text = "Stop";
			this.btnCancel.UseVisualStyleBackColor = true;
			this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
			// 
			// progressBar1
			// 
			this.progBar.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
						| System.Windows.Forms.AnchorStyles.Left)
						| System.Windows.Forms.AnchorStyles.Right)));
			this.progBar.Location = new System.Drawing.Point(12, 126);
			this.progBar.Name = "progressBar1";
			this.progBar.Size = new System.Drawing.Size(268, 23);
			this.progBar.Step = 1;
			this.progBar.TabIndex = 2;
			// 
			// lblState
			// 
			this.lblState.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
						| System.Windows.Forms.AnchorStyles.Right)));
			this.lblState.Location = new System.Drawing.Point(9, 12);
			this.lblState.Name = "lblState";
			this.lblState.Size = new System.Drawing.Size(187, 23);
			this.lblState.TabIndex = 3;
			this.lblState.Text = "Status:";
			// 
			// bgWorker
			// 
			this.bgWorker.WorkerReportsProgress = true;
			this.bgWorker.WorkerSupportsCancellation = true;
			this.bgWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bgWorker_DoWork);
			this.bgWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bgWorker_RunWorkerCompleted);
			this.bgWorker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bgWorker_ProgressChanged);
			// 
			// Form1
			// 
			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
			this.ClientSize = new System.Drawing.Size(292, 155);
			this.Controls.Add(this.lblState);
			this.Controls.Add(this.progBar);
			this.Controls.Add(this.btnCancel);
			this.Controls.Add(this.btnStart);
			this.Name = "Form1";
			this.Text = "Form1";
			this.ResumeLayout(false);

		}

		#endregion



		private void SetLabelState(int state)
		{
			if (this.lblState.InvokeRequired)
				this.lblState.Invoke(new VoidDelegate(this.SetLabelState), state);
			else
				this.lblState.Text = "State: " + state;
		}

		/// <summary>
		/// time-consuming operations should be done here
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
		{
			BackgroundWorker worker = sender as BackgroundWorker;

			for (int i = 0; i < cMaxValue; i++)
			{
				if (worker.CancellationPending)
				{
					// cancel was initiated -> leave loop
					e.Cancel = true;
					break;
				}

				this.SetLabelState(i);
				worker.ReportProgress(i);
				System.Threading.Thread.Sleep(100);
			}
			e.Result = "READY";
		}

		private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			this.progBar.PerformStep(); 
			// another way to increment the value of the progressbar: this.progressBar1.Value = e.ProgressPercentage;
			// and: this.progressBar1.Increment(1);
		}

		private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			this.PrepareControls4Start();

			if (e.Cancelled)
				this.lblState.Text = "State: ABORT";
			else
				this.lblState.Text = "State: " + e.Result.ToString();
		}

		private void btnStart_Click(object sender, EventArgs e)
		{
			this.btnStart.Enabled = false;
			this.btnCancel.Enabled = true;

			this.bgWorker.RunWorkerAsync();
		}

		private void btnCancel_Click(object sender, EventArgs e)
		{
			this.bgWorker.CancelAsync();
			this.btnStart.Enabled = true;
			this.btnCancel.Enabled = false;
		}

		private void PrepareControls4Start()
		{
			this.progBar.Minimum = 0;
			this.progBar.Maximum = cMaxValue;
			this.progBar.Value = this.progBar.Minimum;

			this.btnStart.Enabled = true;
			this.btnCancel.Enabled = false;
		}
	}
}

as i mentioned above - it is only a very simple example for the backgroundworker (starting and aborting an operation, if operation is too long), i think you will see the possibilities ;-)

Daniel

ddanbe 2,724 Professional Procrastinator Featured Poster

Thanks Daniël! I will surely have a look in what you sent.:)

DanielGreen 6 Light Poster

Hi!
There is also another possibility you can try with our peace of code (but it is also not the best solution).
Use instead of System.Threading.Thread.Sleep(500) the function Application.DoEvents() or you can combine both:

int i = 1;
while (i <= 10)
{
  progressBar1.PerformStep(); // update progress bar
  label1.Text = message + i.ToString(); // set message to display
  label1.Refresh(); // needed or we won't see the label update
  Application.DoEvents(); // will take also some milliseconds
  System.Threading.Thread.Sleep(250); // simulate an action that takes time
  i++;
}

The function Application.DoEvents() allows each thread todo a little bit of work (-> for more information take a look in MSDN), so in your case the refresh will be done before the Sleep-Statement blocks the Main-Thread (GUI).

Daniel

sknake 1,622 Senior Poster Featured Poster

using Application.DoEvents(); is NOT a good idea. Use another thread or force screen updating (like the original code snippet) but don't process messages with DoEvents() in the middle of a long-running code block.

g2gayan -4 Junior Poster in Training

Hey,, Nice Work,,,
can i set some forms to get loaded from this progressbar ?
any suggestions ?

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.