What I want is when some method is doing some task UI keeps itself active and I want to show the progress of the work in a progress-bar.
I have a method, a BackGroundWorker and a Progressbar. I want to call the method when BackGroundWorker starts running and show the progress. The method contains a loop. So, it can report the progress.
So, what can be done?
private void Form1_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'dataSet1.TBLMARKET' table. You can move, or remove it, as needed.
myBGWorker.WorkerReportsProgress = true;
}
private void myBGWorker_DoWork(object sender, DoWorkEventArgs e)
{
parseFiles();
}
private void myBGWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
}
private void myBGWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done");
}
private void parseButton_Click(object sender, EventArgs e)
{
myBGWorker.RunWorkerAsync();
}
public void parseFiles()
{
for()
{
//parsing
myBGWorker.ReportProgress(...);
}
}
But it's not working. The Progressbar is not updating. Only a small progress is showing after the MessageBox "Done".
Instead of using one ParseFiles method (which should depend on myBGWorker) use loop and method which parse one file. Report progress percentage in that loop:
private void parseButton_Click(object sender, EventArgs e)
{
parseButton.Enabled = false;
myBGWorker.RunWorkerAsync();
}
private void myBGWorker_DoWork(object sender, DoWorkEventArgs e)
{
for(int i = 0; i < filesCount; i++)
{
ParseSingleFile(); // pass filename here
int percentage = (i + 1) * 100 / filesCount;
myBGWorker.ReportProgress(percentage);
}
}
void myBGWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
}
void myBGWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
parseButton.Enabled = true;
MessageBox.Show("Done");
}
To. soham.m17
using with sender argument
private void myBGWorker_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
for(int i = 0; i < filesCount; i++)
{
ParseSingleFile(); // pass filename here
int percentage = (i + 1) * 100 / filesCount;
worker.ReportProgress(percentage); // use not myBGWorker but worker from sender
}
}
I am sorry about the question. Actually the code works fine. It was not showing the Progressbar as the argument in myBGWorker.ReportProgress() was fraction and not percentage. So, it was not showing it. Sorry for the inconvenience.
Moderator may delete this thread. Otherwise it can be a tutorial for others.
Related
hi guys i tried to copy some files with this Code everything is good and the app will copy files but in copy progress i cant move my app or do anything
i tried to use thread but its not works i also use backgroundWorker but still nothing the only control that doesnt get stuck is progressBar its works fine here is my code :
public Form1()
{
InitializeComponent();
backgroundWorker1.Dispose();
backgroundWorker1.DoWork += BackgroundWorker_DoWork;
backgroundWorker1.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
backgroundWorker1.ProgressChanged += BackgroundWorker_ProgressChanged;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker2.DoWork += BackgroundWorker2_DoWork;
backgroundWorker2.WorkerReportsProgress = true;
}
private void BackgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
File.Copy(sourcePath, targetPath);
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < fileSize; i++)
{
int p = (i + 1) * 100 / Convert.ToInt32(fileSize);
backgroundWorker1.ReportProgress(p);
}
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lbProgress.Text = e.ProgressPercentage.ToString();
progressBar1.Value = e.ProgressPercentage;
}
private void btnTarget_Click(object sender, EventArgs e)
{
folderBrowser = new FolderBrowserDialog();
if (folderBrowser.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
targetPath += folderBrowser.SelectedPath + #"\" + fileName;
lbTarget.Text = targetPath;
}
}
private void btnSource_Click(object sender, EventArgs e)
{
op = new OpenFileDialog();
if (op.ShowDialog() == DialogResult.OK)
{
sourcePath += op.FileName;
lbSource.Text = sourcePath;
fileInfo = new FileInfo(sourcePath);
fileSize = fileInfo.Length / 1024;
fileName = fileInfo.Name;
MessageBox.Show(string.Format("File size is: {0} KB", fileSize));
}
}
private void btnCopy_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
backgroundWorker2.RunWorkerAsync();
}
You're updating the progress bar faster than the UI can update, for every single byte of the file being copied in a tight loop. You're flooding the UI thread with pointless work.
Remove backgroundWorker1, it's not doing anything useful anyway. If you don't have a way to track the progress (which you don't with File.Copy), just use a progress bar without progress (set Style to Marquee).
For testing I created a simple winform application with a button, a label and a background worker and added the following corresponding events:
private void OnBackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
var worker = (BackgroundWorker)sender;
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
worker.ReportProgress(i * 10);
}
}
private void OnBackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
labelProgress.Text = e.ProgressPercentage.ToString();
}
private void OnBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
labelProgress.Text = "Done";
}
private void OnButtonProgressClick(object sender, System.EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
Works as expected.
Could You try to update Your DoWork to this:
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int mainProgress = 0;
for (int i = 0; i < fileSize; i++)
{
int calculatedProgress = (i + 1) * 100 / Convert.ToInt32(fileSize);
if(calculatedProgress > mainProgress)
{
mainProgress = calculatedProgress;
backgroundWorker1.ReportProgress(mainProgress);
}
}
}
maybe You are doing so many updates that simply Window Thread is all the time updating only progress and don't have a time to make anything else?
My code that works:
private void textBox1_TextChanged(object sender, EventArgs e)
{
progressBar1.Visible = true;
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
progressBar1.Visible = false;
}
If I add something for the computer to do, as seen in the following code example, the computer does not show the progress bar until it's done doing the computation. What I want it to do is show the progress bar first, then do the computation, then on some other event I want to hide the progress bar. Why can't I do it this way?
private void textBox1_TextChanged(object sender, EventArgs e)
{
progressBar1.Visible = true;
FindPrimeNumber(50000);
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
progressBar1.Visible = false;
}
The requested FindPrimeNumber code:
public int FindPrimeNumber(int n)
{
int count = 0;
int a = 2;
while (count < n)
{
int b = 2;
int prime = 1;// to check if found a prime
while (b * b <= a)
{
if (a % b == 0)
{
prime = 0;
break;
}
b++;
}
if (prime > 0)
count++;
a++;
}
return (--a);
}
the FindPrimeNumber code is just something to make the computer do a task for a while, so I can test to see if my progress bar is going to show.
I figured it out. In this example, a user enters a 5 digit number in a text box, then a progress bar shows up on the form as it is processing the number in a math function, then the result is put into a second text box and the progress bar goes away.
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (textBox1.TextLength == 5)
{
progressBar1.Visible = true;
int textFromTextBox1 = Int32.Parse(textBox1.Text);
backgroundWorker1.RunWorkerAsync(textFromTextBox1);
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = FindPrimeNumber((int)e.Argument);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
textBox2.Text = e.Result.ToString();
backgroundWorker1.CancelAsync();
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
progressBar1.Visible = false;
}
Note: This example does not handle all exceptions, but it works great. As you can probably see in the code I am passing a value into the BackgroundWorker which gets passed into the FindPrimeNumber method, then I am retrieving the result out of the BackgroundWorker.
More notes for newbies:
I have the BackgroundWorker property WorkerSupportsCancellation set to True.
In WinForms, after dropping the BackgroundWorker onto the form, when you double click it, it will generate the DoWorkEventHandler for you, then in the Solution Explorer go to the Events and double click on RunWorkerCompleted so it can generated that for you as well. Otherwise, you will have to do a lot of manual code entry.
" int ans = 2;
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i <21; i++)
{
ans = 2;
label1.Text += i.ToString();
while (true)
{
if (ans == 1)
{
break;
}
}
}
}
private void button1_Click(object sender, EventArgs e)
{
ans = 1;
} "
this is a simple app
I want to print a number & then wait to the button to be clicked to break the while loop
but when I run the application , the form doesn't show .
"T think that the problem is the while (true)".
what to do?
Use a timer. Start the timer when the form loads. Each time it ticks, increment the number and display it. On button click, you just need to stop the timer.
private Timer _myTimer;
private int number = 0;
private void Form1_Load(object sender, EventArgs e)
{
_myTimer = new Timer();
_myTimer.Interval = 1; // 1 millisecond
_myTimer.Tick += new EventHandler(MyTimer_Tick);
_myTimer.Start();
}
// increments the number at timer tick
private void MyTimer_Tick(object sender, EventArgs e)
{
number ++;
// TODO: update UI here
}
// Stops the timer
private void button1_Click(object sender, EventArgs e)
{
_myTimer.Stop();
}
It's best to not use a loop here. Since this loop won't end you won't ever leave Form_Load and it won't display the form. If you were trying to do some task when the user clicks a button, why not move that logic to button1_Click?
The correct way to implement such a task as you describe would be as such:
private EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.AutoReset);
private void Form1_Load(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 26; i++)
{
ewh.WaitOne();
Action updateLable = () => label1.Text = "" + i;
label1.BeginInvoke(updateLable);
}
});
}
private void button1_Click(object sender, EventArgs e)
{
ewh.Set();
}
As you can see I've replaced your busy wait (while(true)) with a .Net wait handle.
One of the answers describes a timer that acts every millisecond - that is a busy wait of sorts.
This is what async/await is for. Mark your Load() event with "async", then "await" a Task that continues when a ManualResetEvent is triggered in the Button click handler:
private System.Threading.ManualResetEvent mre = new System.Threading.ManualResetEvent(false);
private async void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < 21; i++)
{
label1.Text = i.ToString();
mre.Reset();
await Task.Factory.StartNew(() => { mre.WaitOne(); });
}
button1.Enabled = false;
label1.Text = "Done!";
}
private void button1_Click(object sender, EventArgs e)
{
mre.Set();
}
This is the code that I am trying to execute, but stepping through my code I never see any progress indicated or updated on my windows form showing progressbar1. This is my 1st attempt in getting a background worker to function properly, and all I have is a windows form with one button on it and this is all of the code involved in the project.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private int i = 0;
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = false;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
ReadySteadyGo();
worker.ReportProgress((i * 10));
FinalizeAndFinish();
worker.ReportProgress((i * 10));
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Text = "Done!";
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Text = (e.ProgressPercentage.ToString() + "%");
}
private void ReadySteadyGo()
{
Thread.Sleep(100000);
}
private void FinalizeAndFinish()
{
Thread.Sleep(1000);
}
}
It appears that you are using Thread.Sleep() to simulate a long-running operation. There are a few things you should consider based on your code example:
When the backgroundWorker1.RunWorkerAsync(); is executed, it starts working on another thread. Thus, if you are debugging interactively and you have not set a breakpoint in the backgroundWorker1_DoWork method, you are not likely to see this code execute.
When the Thread.Sleep(100000) executes, it essentially means that the background worker will pause for 100 seconds - so you need to make sure you are waiting at least that long to see the UI updated.
Also, as per Hans Passant's comment, consider the following:
Nor can you see it doing anything, there's no point to assigning the
ProgressBar.Text property since it doesn't display text. Set Value
instead.
I recreated your example in Visual Studio and am hitting a breakpoint in backgroundWorker1_DoWork so the multi-threading is working properly, you just need to do proper processing?
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
When the following code executes, the progress bar doesn't go upto the end and when its 90% complete, the message box is shown. I find the problem with the for loop but don't know what the real problem inside is. Please help. I searched the same kind of questions here in stackoverflow too. But didn't find any solution.
Here is my code:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
break;
}
backgroundWorker1.ReportProgress(i);
Thread.Sleep(100);
}
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Cancelled");
progressBar1.Value = 0;
}
else
{
progressBar1.Value = 0;
MessageBox.Show("Done");
}
}
I have reproduced your problem. The problem lies in the DoWork method that is too fast and the progress bar repaint of the bar cannot keep up. If you try to change that Thread.Sleep to a bigger interval (I have tried 300ms) you could be see the full painting.
However, you could help the progress bar in its repainting if you move the MessageBox.Show("Done") before the setting to zero of the progressbar.Value
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Cancelled");
progressBar1.Value = 0;
}
else
{
MessageBox.Show("Done");
progressBar1.Value = 0;
}
}
Change backgroundWorker1.ReportProgress(i);
To:
backgroundWorker1.ReportProgress(i+1);
Or change the loop to:
for (int i = 1; i <= 100; i++)