[SOLVED] Progressbar Example Application Not Responding If Moved Or Loses Focus

writhziden

Administrator, .NET/UWP Developer
Staff member
Joined
May 23, 2012
Posts
2,943
Location
Colorado
I wrote a simple program to test out the progress bar functionality. I'm having an issue, though. If I run the .exe (included in my attachment), the application gives the (Not Responding) message and the progress bar disappears. This only happens if I try to click on the window to move it, or if I switch to another Window and it has to redraw.

One method around this is to use the Hide() and Show() commands each time it refreshes, but that causes unwanted side effects like making it difficult to switch between windows or having the progressbar application flash on the screen in a strobe-like fashion. Is there something I am doing wrong?

Included is the release version of the example app and the source code in c++ for further help.

Please let me know if you know what I am doing wrong.


Here is the code being used:

Form1.h
C++:
#pragma once

namespace progressBar {

	using namespace System;
	using namespace System::ComponentModel;
	using namespace System::Collections;
	using namespace System::Windows::Forms;
	using namespace System::Data;
	using namespace System::Drawing;

	/// <summary>
	/// Summary for Form1
	/// </summary>
	public ref class Form1 : public System::Windows::Forms::Form
	{
	public:
		Form1(void)
		{
			InitializeComponent();
			//
			//TODO: Add the constructor code here
			//
		}

	protected:
		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		~Form1()
		{
			if (components)
			{
				delete components;
			}
		}
	public: System::Windows::Forms::ProgressBar^  progressBar1;
	protected: 
	public: System::Windows::Forms::Label^  label1;

	private:
		/// <summary>
		/// Required designer variable.
		/// </summary>
		System::ComponentModel::Container ^components;

#pragma 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>
		void InitializeComponent(void)
		{
			this->progressBar1 = (gcnew System::Windows::Forms::ProgressBar());
			this->label1 = (gcnew System::Windows::Forms::Label());
			this->SuspendLayout();
			// 
			// progressBar1
			// 
			this->progressBar1->Location = System::Drawing::Point(41, 119);
			this->progressBar1->Name = L"progressBar1";
			this->progressBar1->Size = System::Drawing::Size(199, 23);
			this->progressBar1->TabIndex = 0;
			// 
			// label1
			// 
			this->label1->AutoSize = true;
			this->label1->Font = (gcnew System::Drawing::Font(L"Microsoft Sans Serif", 12, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, 
				static_cast<System::Byte>(0)));
			this->label1->ForeColor = System::Drawing::Color::Red;
			this->label1->Location = System::Drawing::Point(64, 91);
			this->label1->Name = L"label1";
			this->label1->Size = System::Drawing::Size(153, 20);
			this->label1->TabIndex = 1;
			this->label1->Text = L"Progress Bar Test";
			// 
			// Form1
			// 
			this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
			this->ClientSize = System::Drawing::Size(284, 262);
			this->Controls->Add(this->label1);
			this->Controls->Add(this->progressBar1);
			this->Name = L"Form1";
			this->Text = L"Form1";
			this->ResumeLayout(false);
			this->PerformLayout();

		}
#pragma endregion
	};
}


progressBar.cpp
C++:
// progressBar.cpp : main project file.

#include "stdafx.h"
#include "Form1.h"
#include <sstream>
#include <string>

using namespace progressBar;
using namespace std;

[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
	// Enabling Windows XP visual effects before any controls are created
	Application::EnableVisualStyles();
	Application::SetCompatibleTextRenderingDefault(false); 

	// Create the main window and run it
	Form1^ myProgress = gcnew Form1();
	myProgress->Show();
	myProgress->progressBar1->Maximum = 10000000;
	for(int i = 0; i < 10000000; i++){
		myProgress->label1->ForeColor = System::Drawing::Color::Black;
		String^ str;
		stringstream sstemp;
		sstemp << "Running "<< i + 1 << " of 10000000";
		string stringstore;
		stringstore = sstemp.str();
		str = gcnew String(stringstore.c_str());
		if(i%10000 == 0){
			myProgress->label1->Text = str;
			myProgress->progressBar1->Value = i;
			myProgress->Refresh();
		}
	}
	return 0;
}

Also found at TSF: http://www.techsupportforum.com/for...-moved-or-loses-focus-669221.html#post3904888
 

Attachments

Last edited:
Just had to create the progress bar form in another thread using namespace System::Threading.

If you need help with this, send me a PM.
 
Why I suggested creating it all dynamically in a new thread is because of this:

C#:
new Thread((ThreadStart)delegate
{
	Button B = new Button() { Size = new Size(50, 20), Location = new Point(0, 0), Name = "TEST" };
	Invoke((MethodInvoker)delegate { this.Controls.Add(B); B.Show(); });
}) { IsBackground = true }.Start();

while (this.Controls.OfType<Button>().FirstOrDefault(b => b.Name == "TEST") == null)
	Application.DoEvents();

this.Controls["TEST"].Text = "Value"; //THIS LINE - Throws no exception, does as it's supposed to do

Anything added to the UI form as a control, dynamically, even from a new thread, will have to be invoked, because we cannot modify anything created on the UI thread, or reference any of it to access properties or anything of that nature, without Invoking from the unknown thread. When we do this, you may as well consider that control created on the UI thread anyways. The only thing that happened in the newly created thread was the instantiation, and that's it.

In the last line i've commented, the reason that WORKS, from the original thread, is because now this control, that we instantiated on the new thread, and invoked to add to the form on the UI thread, is on the UI thread itself. There's no cross-threading boundaries we are overstepping here. Thus no error thrown even when we don't ignore exceptions...

If we can separate this from the UI thread entirely and queue anything you want along with updating that thread's UI rapidly, it doesn't matter with that main UI thread, because it's not going to be blocked or interrupted.

The reason why your app froze when you had clicked on it or anything, is because it couldn't update or redraw as i've mentioned, the rapid execution to update the controls on the UI thread was blocking any further queue of the background messages required to keep the UI functional, and alive.
 
Is there a better, thread-safe way to do this:



C++:
// Form1.h : Form application file
#include <fstream>
#pragma once

namespace progressBar {

	using namespace System;
	using namespace System::ComponentModel;
	using namespace System::Collections;
	using namespace System::Windows::Forms;
	using namespace System::Data;
	using namespace System::Drawing;
	using namespace System::Threading;

	/// <summary>
	/// Summary for Form2
	/// </summary>
	public ref class Form2 : public System::Windows::Forms::Form
	{
	public:
		Form2(void)
		{
			InitializeComponent();
			//
			//TODO: Add the constructor code here
			//
		}

	protected:
		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		~Form2()
		{
			if (components)
			{
				delete components;
			}
		}
	public: System::Windows::Forms::ProgressBar^  progressBar1;
	private: System::ComponentModel::BackgroundWorker^  backgroundWorker1;
	public: 
	protected: 

	protected: 

	private:
		/// <summary>
		/// Required designer variable.
		/// </summary>
		System::ComponentModel::Container ^components;

#pragma 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>
		void InitializeComponent(void)
		{
			this->progressBar1 = (gcnew System::Windows::Forms::ProgressBar());
			this->backgroundWorker1 = (gcnew System::ComponentModel::BackgroundWorker());
			this->SuspendLayout();
			// 
			// progressBar1
			// 
			this->progressBar1->Location = System::Drawing::Point(12, 100);
			this->progressBar1->Name = L"progressBar1";
			this->progressBar1->Size = System::Drawing::Size(260, 23);
			this->progressBar1->TabIndex = 0;
			// 
			// backgroundWorker1
			// 
			this->backgroundWorker1->WorkerReportsProgress = true;
			this->backgroundWorker1->DoWork += gcnew System::ComponentModel::DoWorkEventHandler(this, &Form2::backgroundWorker1_DoWork);
			this->backgroundWorker1->ProgressChanged += gcnew System::ComponentModel::ProgressChangedEventHandler(this, &Form2::backgroundWorker1_ProgressChanged);
			// 
			// Form2
			// 
			this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
			this->ClientSize = System::Drawing::Size(284, 262);
			this->Controls->Add(this->progressBar1);
			this->Name = L"Form2";
			this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
			this->Text = L"Form2";
			this->Load += gcnew System::EventHandler(this, &Form2::Form2_Load);
			this->ResumeLayout(false);

		}
#pragma endregion
	private: System::Void Form2_Load(System::Object^  sender, System::EventArgs^  e) {
				 backgroundWorker1->RunWorkerAsync();
			 }
	private: System::Void backgroundWorker1_DoWork(System::Object^  sender, System::ComponentModel::DoWorkEventArgs^  e) {
				 UpdateZOrder();
				 BringToFront();
				 progressBar1->Maximum = 100;
				 while(progressBar1->Value < progressBar1->Maximum){
					 std::ifstream infile("test.txt");
					 int percentDone = 0;
					 if(infile.good()){
						 infile >> percentDone;
					 }
					 infile.close();
					 progressBar1->Value = percentDone;
					 Refresh();
					 Thread::Sleep(100);
				 }
				 Close();
			 }
	private: System::Void backgroundWorker1_ProgressChanged(System::Object^  sender, System::ComponentModel::ProgressChangedEventArgs^  e) {
				// Change the value of the ProgressBar to the BackgroundWorker progress.
				progressBar1->Value = e->ProgressPercentage;
			 }
	};
}

C++:
// progressBar.cpp : main project file.

#include "stdafx.h"
#include "Form1.h"
#include <sstream>
#include <string>
#include <fstream>

using namespace progressBar;
using namespace std;

public ref class Work
{
public:
	Work(){};
   static void DoWork(){	   
		ofstream outExitFile("tmp\\outExitFile.txt");
		outExitFile << 0;
		outExitFile.close();
		Application::Run(gcnew Form2());
   }
};

[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
	Work^ myWork = gcnew Work();
	ThreadStart^ threadDelegate = gcnew ThreadStart( &Work::DoWork );
	Thread^ newThread = gcnew Thread( threadDelegate );
	newThread->Priority = ThreadPriority::Highest;
	newThread->Start();
	
	// Create the main window and run it
	
	int maxValue = 1000000000;
	for(int i = 0; i < maxValue; i++){
		if((i+1)%(maxValue/100) == 0){
			double percentDone = 100.0 * double(i+1)/double(maxValue);
			ofstream outfile("test.txt");
			outfile << int(percentDone);
			outfile.close();
			System::Threading::Thread::Sleep(10);
		}
	}
	
	return 0;
}
 
Hmm, what's the Thread.Sleep() for in your test? And i'm also just curious about that file you're writing to for the percent change?

Is your goal to check progress against all scanned dumps at once? Or display something like a label which says "1 of # dumps scanned..." where # increases, and as that current dump finishes the progress goes from 100% to 0% each iteration through scanning each dump? This would be a bit of a bugger to do it that way over scanning multiple dumps with multiple threads. For something easier, if you had 3 dumps all at the same time, you may want to ease the calculations by changing the max to (100 * {# of threads}), 300(%) total in the case of 3 threads.

You're using a background worker though, which some people prefer. I prefer other manual methods, but to each his own. You've got a lot going on at once though. And Thread.Sleep() in loops and for prolonged periods of time is considered bad. Some people like to use Thread.Sleep(0) for a few reasons, but it doesn't sleep the Thread and serves a different unique purpose.

You should read here: http://msmvps.com/blogs/peterritchi...p-is-a-sign-of-a-poorly-designed-program.aspx

:thumbsup2:
 
I got the information about Sleep from other forums where users were trying to do similar things. There were mixed versions of Sleep, some with 0 in them, some with 100 (which I found to really slow down the apps too much, so I settled on 10). Can you explain why it may be bad to have sleep for prolonged periods of time...

EDIT: Reading the page you linked... May have more questions afterward.​

My understanding from the limited and unexplained threads I've read is that the Sleep allows one thread to pause while the other does work. Not sure if this would help or not. I was trying to find something to allow John's system to handle the apps with the threading, but I did not really understand the PMs you sent him. Threading is very new to me, and I'm a bit confused due to the lack of information on forums. I also find MSDN impossible to learn anything from since their examples are either too basic or too complex with no middle ground...

In some of the forums I read through, I did see that Sleep(0) is a method to allow the other thread to have access, but I did not understand it, and the forum helper did not explain it to the person they were helping. What's the unique purpose?


I'm running the .dmps sequentially. I've determined a rough guess of how long each .dmp might take and calculated the percentage using statistics I gathered from a number of .dmp runs on my system. I've hard coded this calculation into the apps. It's not perfect, but it's generally pretty close to accurate for the percentage completed for the number of .dmps run. I do not run .dmps in parallel, and I do not plan to do so since it would probably make the ordering scheme more complicated than I want to deal with for the output of the processing apps.

The progress bar output in the apps is very similar to what I did in that sample program, but the apps have a flashier version.
 
Last edited:
Alright, I read through the link. Apparently Sleep is not what I want to use for this. What I was trying to accomplish was:

  1. ]The main thread pauses for a very short time to allow the progress bar to update.
  2. The progress bar should update pretty quickly since it does rather simple I/O taks with files to determine how many times the main thread has gone through the loop (hypothetically speaking)
  3. The thread then resumes and the progress bar is not updated while the main thread does its work if there are no threads available for it to run on.

Not sure if I explained that adequately. I basically want to take into account that some people may not have the hardware resources to run both the progress bar and the .dmp analysis simultaneously. It works fine on my system without the Sleep statements, but obviously not on John's system, for instance. Is there anything I can do on my end to make the threading work on John's end? Perhaps the Yield() function?
 
Last edited:
Okay, you're running the dumps sequentially. But depending on how many commands you're pipelining through, would also change the estimated time, so that's where i'd find calculating the total time for completion to be fairly tough. As for Thread.Yield (which is what I assume you mean since there's other variations of the yield keyword as well,) is available for .NET 4.0 or higher though. So if that's appropriate, then you could use it... Windows 7 comes with .NET 3.5 by default though, so that's generally what I try to target. If we use 4.0, perhaps you'll have to get the user to install 4.0 before he/she can continue, but you'll have to mention this in a public place so that users know why their app isn't working if they only have 3.5 or lower.

That progressbar though... Should not be a significant determining factor for powerful computer vs. weak computer. You could update it less frequently though, and instead of jumping at every little percent change, jump at maybe every 5% change?

Look into the Threading.Timer as well, ThreadPools, and the generic Queue class. These could be interesting for you, depending on how you'd like to do this. You just want to display a progressbar though and keep dmp file scanning sequential? I thought you wanted to scan multiple dmp files at once, so that changes things :)

I have a few ideas in mind as well for a more accurate percentage reading. But i'd write it in C# probably, just to give you the idea. You may be able to get a handle on the process's open console window, or since you're running a console application, you may as well just read the out stream for that console window itself.. Determine where you are with the commands you're sending in as args %1, %2, etc... separated by the ';' I believe. And jump the progressbar value from there.
 
Last edited:
Here's an example I came up with for downloading data from links to the driver table, based on driver names:

xqBYP.gif


Multi-threaded, which is why you can move it around and it doesn't affect the UI at all for the main form.

I used Regex to parse:
C#:
string driver = Regex.Match(html, @"\b((\w|\d)+\.sys)\b", RegexOptions.IgnoreCase).Groups[1].Value;
string desc = Regex.Match(html, @"Driver Description:\s+</strong>.*?\b(.*?)\b</p>", RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups[1].Value;
string updateSite = Regex.Match(html, @"<strong>Driver Update Site:\s+</strong>((.*?<a href="".*?"">(?<update>.*?)</a>)|(<span class=""red"">(?<update>.*?)</span>))",
	RegexOptions.IgnoreCase | RegexOptions.Singleline).Groups["update"].Value;
 
Last edited:
If you wanted to take a look, not sure how or what you're doing with the DRT to get/retrieve data, here's my DriverDisplay() class (unoptimized):
C#:
#region Driver Data Display
public class DriverDisplay
{
	private WebClient _wc = new WebClient();

	ProgressBar _Progress;
	Label _Label;
	TextBox _tBox;
	Form _Frm;

	public DriverDisplay()
	{
		_wc.DownloadProgressChanged += _wc_DownloadProgressChanged;
		_wc.DownloadStringCompleted += _wc_DownloadStringCompleted;

		Form frm = new Form();
		frm.Size = new Size(500, 375);
		frm.FormBorderStyle = FormBorderStyle.SizableToolWindow;
		frm.Text = "Downloading Data...";

		ProgressBar p = new ProgressBar();
		p.Size = new Size(450, 20);
		p.Location = new Point(18, 15);
		p.Maximum = 100;
		p.Value = 0;
		p.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
		_Progress = p;

		Label lbl = new Label();
		lbl.Text = "Progress: " + p.Value.ToString();
		lbl.Width = frm.Width - 12;
		lbl.TextAlign = ContentAlignment.MiddleCenter;
		lbl.Location = new Point(0, 35);
		lbl.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
		_Label = lbl;

		TextBox T = new TextBox();
		T.Multiline = true;
		T.ScrollBars = ScrollBars.Both;
		T.WordWrap = false;
		T.Size = new Size(450, 240);
		T.Location = new Point(18, 75);
		T.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom;
		_tBox = T;

		frm.Controls.Add(p);
		frm.Controls.Add(lbl);
		frm.Controls.Add(T);

		frm.FormClosing += _Frm_FormClosing;
		_Frm = frm;
	}

	private void UpdateValues(int progress)
	{
		try
		{
			_Frm.Invoke((MethodInvoker)delegate
			{
				int i = progress + (100 * (totalLinks - downloadQueue.Count));
				_Progress.Value = i;
				_Progress.Update();

				string s = ((double)i / _Progress.Maximum * 100).ToString();
				_Label.Text = "Progress: " + s.Substring(0, s.IndexOf('.') > -1 ? s.IndexOf('.') + 3 : s.Length) + "%";
				_Label.Update();
			});
		}
		catch (ObjectDisposedException) { }
	}

	int num;
	int totalBytes;
	int totalLinks;

	Queue<MethodInvoker> downloadQueue;
	public void GetDriverData(string[] drivers)
	{
		_Frm.Show();
		num = 0;
		totalBytes = 0;
		totalLinks = drivers.Length;

		downloadQueue = new Queue<MethodInvoker>(drivers.Length);
		_Progress.Maximum = drivers.Length * 100;

		foreach (string d in drivers)
			downloadQueue.Enqueue((MethodInvoker)delegate { _wc.DownloadStringAsync(new Uri("https://www.sysnative.com/drivers/driver.php?id=" + d), d); });

		downloadQueue.ElementAt(0).Invoke();
	}

	private void _wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
	{
		UpdateValues(e.ProgressPercentage);
	}

	private void _wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
	{
		if (!e.Cancelled || e.Error != null)
		{
			string html = e.Result;
			string driver = e.UserState.ToString();

			RegexOptions RO = RegexOptions.IgnoreCase | RegexOptions.Singleline;
			string desc = Regex.Match(html, @"Driver Description:\s+</strong>.*?\b(.*?)\b</p>", RO).Groups[1].Value;
			string updateSite = Regex.Match(html, @"<strong>Driver Update Site:\s+</strong>((.*?<a href="".*?"">(?<update>.*?)</a>)|(<span class=""red"">(?<update>.*?)</span>))", RO).Groups["update"].Value;

			_tBox.AppendText(string.Format("Driver: {0}\r\nDescription: {1}\r\nUpdate: {2}\r\n\r\n", driver, desc, updateSite));
		}
		else
		{
			MessageBox.Show(e.Error.Message);
		}

		num++;
		totalBytes += e.Result.Length;
		downloadQueue.Dequeue();

		if (downloadQueue.Count > 0)
			downloadQueue.ElementAt(0).Invoke();
		else
			_Frm.Text = string.Format("[{0} Drivers] Async Downloaded: {1} bytes", num, totalBytes);
	}

	private void _Frm_FormClosing(object sender, FormClosingEventArgs e)
	{
		if (downloadQueue.Count > 0)
		{
			e.Cancel = true;
			MessageBox.Show("Please wait until the driver data has been downloaded from the table...", "Download in progress", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
		}
	}
}
#endregion
 

Has Sysnative Forums helped you? Please consider donating to help us support the site!

Back
Top