Hi I am trying to show a progress bar while in background the code is looping till it finds a file with specific name.
I have written following code for that but the progress bar values doesn't change.
What should I change in the code below?
public partial class Form1 : Form
{
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
public Form1()
{
InitializeComponent();
progressBar1.Visible = false;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged +=
new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(backgroundWorker1_WorkDone);
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
progressBar1.Visible = true;
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Your background task goes here
for (int i = 0; i <= 100; i++)
{
// Report progress to 'UI' thread
backgroundWorker1.ReportProgress(i);
// Simulate long task
while (!File.Exists(#"C:\Users\Test.txt"))
{
continue;
}
}
}
void backgroundWorker1_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
void backgroundWorker1_WorkDone(object sender,
RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
}
}
The continue in your code will simply continue to the next iteration of the while(true) loop. It won't go back up to the for loop as you expect - it just keeps looping in there over and over again. If it wasn't a background worker, it'd hang your entire program. Since it is, it just hangs that thread. I expect that one CPU core stays at 100% while this is running.
That being said, while the goal here is admirable, there's no good way to accomplish it. Even if you fix the infinite loop, your progress bar will either "finish" at a low percentage (1%, 2%, 3%, done) or go up to 100, then stop updating, but without the file yet existing.
Related
I have the main thread which is wizard in WPF.
after user finished set the properties of the wizard, it processing data.
It takes a few seconds and I would like to raise a progress bar which report on the progress.
Hence, I set always on the main thread variable call currentStep.
I have totally thresholdStep steps which equals to 12.
So I want that the progress bar will work as a thread but it will also will be connected to the main thread by using currentStep variable.
So, I used by background worker like this:
public partial class MessageWithProgressBar : Window
{
private BackgroundWorker backgroundWorker = new BackgroundWorker();
public MessageWithProgressBar()
{
InitializeComponent();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.ProgressChanged += ProgressChanged;
backgroundWorker.DoWork += DoWork;
backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
}
private void DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(100);
int i = (int)e.Argument;
backgroundWorker.ReportProgress((int)Math.Floor((decimal)(8*i)));
if (i > GeneralProperties.General.thresholdStep)
backgroundWorker.ReportProgress(100);
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progress.Value = e.ProgressPercentage;
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
WindowMsg msg = new WindowMsg();
msg.Show();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (backgroundWorker.IsBusy == false)
backgroundWorker.RunWorkerAsync(GeneralProperties.General.currentStep);
}
}
In addition, I called the background worker from the main thread as below:
MessageWithProgressBar progress = new MessageWithProgressBar();
progress.Show();
What acutally happens is that DoWork called only once with currentStep = 1 and it don't updates in relation to the main thread which also updated currentStep dependents on it's progress.
Any ideas how to solve it?
Thanks!
Change your DoWork method like below:
private void DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(100);
int i = (int)e.Argument;
do
{
i = GeneralProperties.General.currentStep;
backgroundWorker.ReportProgress((int)Math.Floor((decimal)(8 * i)));
if (i > GeneralProperties.General.thresholdStep)
backgroundWorker.ReportProgress(100);
}
while (i < GeneralProperties.General.thresholdStep);
}
Just make sure your are not getting thread synchronization problem with GeneralProperties.General object, if you are then use lock when accessing the object.
UPDATE:
For update problem:
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
System.Windows.Application.Current.Dispatcher.Invoke(new Action(() =>
{
progress.Value = e.ProgressPercentage;
}), null);
}
In my XML editor I want to be able to open multiple files at once using an index file. Obviously, depending on the amount of files, this can take a bit of time and I want to use a progress bar to notify the user that the programm is still loading and doing something.
From what I have researched the way to keep the UI progress bar updated is using a BackgroundWorker.
public MainWindow()
{
InitializeComponent();
tabList = new ObservableCollection<FileTab>();
tabControl.ItemsSource = tabList;
backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.WorkerReportsProgress = true;
}
(...)
private void OpenProjectButtonClick(object sender, RoutedEventArgs e)
{
openingProgressBar.Value = 0;
openingProgressBar.Visibility = System.Windows.Visibility.Visible;
backgroundWorker.RunWorkerAsync();
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int i = 0;
foreach (IndexFile file in indexManager.fileList)
{
this.Dispatcher.Invoke((Action)(() =>
{
tabList.Add(new FileTab(file.filePath));
}));
i++;
Console.WriteLine("-(DoWork)->" + i);
double percentage = (Convert.ToDouble(i) / Convert.ToDouble(indexManager.fileList.Count)) * 100;
Console.WriteLine("-(DoWork.percentage)-> "+ percentage);
backgroundWorker.ReportProgress((int)percentage);
}
}
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
openingProgressBar.Value = e.ProgressPercentage;
Console.WriteLine("-(ProgressChanged)->" + openingProgressBar.Value);
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
openingProgressBar.Visibility = System.Windows.Visibility.Collapsed;
backgroundWorker.Dispose();
Console.WriteLine("-(RunWorkerComplete)-> Done");
}
Since I'm accessing the tablist in the DoWork-Method I wrap that call in the Dispathcer.Invoke. In this form the code kind of does what I want. It makes the collapsed progressBar visible and updates it every once in a while. Sadly it doesn't update the percentage after every file loaded. From what I can see in the console, the ProgressChanged execution lags behind the DoWork. From my understanding it is called in every iteration of the loop though. And even if it fires the UI doesn't always respond to that.
So my question is: Am I still blocking the UI thread somehow and how could I fix it?
The Problem is that
you are do a coupling between UI and BackgroundWorker
The Solution
return on ReportProgress your FileTab object
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int i = 0;
foreach (IndexFile file in indexManager.fileList)
{
i++;
Console.WriteLine("-(DoWork)->" + i);
double percentage = (Convert.ToDouble(i) / Convert.ToDouble(indexManager.fileList.Count)) * 100;
Console.WriteLine("-(DoWork.percentage)-> "+ percentage);
backgroundWorker.ReportProgress((int)percentage,new FileTab(file.filePath));
}
}
then add your FileTab object in your backgroundWorker_ProgressChanged to the tabList
Try to move the reporting progress operation into the dispatcher's operations:
foreach (IndexFile file in indexManager.fileList)
{
this.Dispatcher.Invoke((Action)(() =>
{
tabList.Add(new FileTab(file.filePath));
Console.WriteLine("-(DoWork)->" + i);
double percentage = (Convert.ToDouble(i) / Convert.ToDouble(indexManager.fileList.Count)) * 100;
Console.WriteLine("-(DoWork.percentage)-> "+ percentage);
backgroundWorker.ReportProgress((int)percentage);
}));
i++;
}
It's possible that dispatcher runs in another thread...
I want to create a basic multi-thread application using a progress bar. Meaning that this progress bar will run on a different thread while the main thread is busy in the large process it is doing. I've seen a lot of tutorials about it. But the thing that they are multi-threading is the one that doing the large process. The progress bar in the other form is just showing a simple progress bar that runs and complete using a timer.
This is the code I have now.
For the thread:
public void thread()
{
Form6 for6 = new Form6();
for6.Show();
}
TH1 = new Thread(thread);
TH1.Start();
For the progress bar (Code inside form 6)
private void timer1_Tick(object sender, EventArgs e)
{
progressBar1.Increment(+1);
if (progressBar1.Value == 99)
{
this.Close();
}
}
private void Form6_Load(object sender, EventArgs e)
{
timer1.Start();
}
My problem is the thread in here doesn't run the Form6. Is there any way for me to do this?
Instead of using main thread for large processing you can use the Background worker for all the processing.
Here's a simple example to do it.
public partial class Form1 : Form
{
BackgroundWorker bgw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
label1.Text = "";
label2.Text = "";
}
private void button1_Click_1(object sender, EventArgs e)
{
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
bgw.WorkerReportsProgress = true;
bgw.RunWorkerAsync();
}
void bgw_DoWork(object sender, DoWorkEventArgs e)
{
int total = 57; //some number (this is your variable to change)!!
for (int i = 0; i <= total; i++) //some number (total)
{
System.Threading.Thread.Sleep(100);
int percents = (i * 100) / total;
bgw.ReportProgress(percents, i);
//2 arguments:
//1. procenteges (from 0 t0 100) - i do a calcumation
//2. some current value!
}
}
void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label1.Text = String.Format("Progress: {0} %", e.ProgressPercentage);
label2.Text = String.Format("Total items transfered: {0}", e.UserState);
}
void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//do the code when bgv completes its work
}
}
Instead of the ProgressBar, you should really move your long-running, non-UI code into a separate thread. The standard and easier way of doing this in WinForms is to use BackgroundWorker component, which can raise ProgressChanged event where you can update your UI. Important to note that ProgressChanged event is raised on the UI thread, not on the worker thread, so you don't even need to use Invoke() to perform UI operations (such as updating your ProgressBar).
you must use Control.Invoke for avoid cross-threading problem,but I prefer use BackgroundWorker for resolve it, create Form6 on a _field and use progressbar in ProgressChanged event for more information see this page
public void thread()
{
Form6 for6=null;
Application.OpenForms[0].Control.Invoke(delegate{
for6 = new Form6();
for6.Show();
});
}
I'm trying to make a progressbar advance using a BackgroundWorker. The final goal is to show the progress of a background search, but I first want to get to know the progress bar by doing a simple simulation. This is the code:
public MainWindow()
{
InitializeComponent();
worker = new BackgroundWorker(); // variable declared in the class
worker.WorkerReportsProgress = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Title += " DONE";
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
for(int j = 0; j <= 100; j++)
{
worker.ReportProgress(j);
Title += j.ToString();
Thread.Sleep(50);
}
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
searchProgressBar.Value = e.ProgressPercentage;
}
But when I run it, it skips right to the end, without altering the progressbar in any way. When I debug it step-by-step, the last step I get to is worker.ReportProgress(j);, then control returns to the program and worker_RunWorkerCompleted is called. Why?
In case you trying to change the UI content, you should put the calls on UI Dispatcher. You can't modify UI objects from background thread. Replace your lines with these -
App.Current.Dispatcher.Invoke((Action)delegate()
{
Title += j.ToString();
});
and
App.Current.Dispatcher.Invoke((Action)delegate()
{
Title = "Done";
});
You forgot to run the worker:
worker.RunWorkerAsync();
I have a background worker that stops after 100 iterations. Like this:
BackgroundWorker bgWorker = new BackgroundWorker();
bgWorker.WorkerReportsProgress = true;
bgWorker.WorkerSupportsCancellation = true;
bgWorker.DoWork += new OpenNETCF.ComponentModel.DoWorkEventHandler(this.bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new OpenNETCF.ComponentModel.RunWorkerCompletedEventHandler(this.bgWorker_RunWorkerCompleted);
bgWorker.ProgressChanged += new OpenNETCF.ComponentModel.ProgressChangedEventHandler(this.bgWorker_ProgressChanged);
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
for(i=0; i<300; i++)
{
bgWorker.ReportProgress(i, i);
}
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.labelProgress.Text = e.UserState.ToString();
}
private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("finished loading...");
}
What happens is that the labelProgress' value stops at 100, and the messagebox pops up and says "finished loading...".
Anybody have an idea of what is wrong. Why does the thread stop at the 101 iteration?
Thanks in advance.
Your code is crashing the BGW thread. Be sure to display the value of e.Error in the RunWorkerCompleted event handler if it isn't null.
From the MSDN Library article for Backgroundworker.ReportProgress:
percentProgress Type:
System.Int32
The percentage, from 0 to 100, of the
background operation that is complete.
Fix:
bgWorker.ReportProgress(0, i);
I've tried this (using .Net 4) and it works fine - the label displays 299 when the MessageBox appears. However I'm not using the OpenNETCF classes - maybe they are having some effect? Do you get the same behaviour if you replace OpenNETCF.ComponentModel with System.ComponentModel?