So I have a quick file mover program and it works fine and the progress bar show the right percentage while working but when i run it a second time. The progress bar starts at the last value. Even though I start by updating the progressbar1.value = 0;
Only way to make the progressbar re-start at zero is to close the program and start it again
private void button_move_Click(object sender, RoutedEventArgs e)
{
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(Worker_DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(Worker_ProgressChanged);
worker.WorkerReportsProgress = true;
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
//neither updating the Value to 0 or ReportProgress to 0 worked
//progressbar1.Value = 0;
//worker.ReportProgress(0);
moveFiles(sender, e, dirFiless);
}
catch (Exception ex)
{
Console.WriteLine("Error trying to Move files: " + ex);
}
}
private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressbar1.Value = e.ProgressPercentage;
progressbar1.Value = e.ProgressPercentage - 1;
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressbar1.Value = 0;
return;
}
//this works and the progress bar gets updated incrementally
public void moveFiles(object sender, DoWorkEventArgs e, string[] dirFiles)
{
totalFiles = dirFiles.Length;
foreach (string file in dirFiles)
{
filecount++;
percentage = (int)((filecount * 100) / totalFiles);
worker.ReportProgress(percentage);
//move file
}
worker.ReportProgress(100);
}
Are you resetting your filecount variable back to 0? In the code you posted it appears that it just keeps counting up with each run which would cause the progress bar to jump back up even after you previously set it back to 0.
Related
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 wanted to make every 30 percent in progress bar, the text changes.
What i wanted to do is, when the progress bar hit 25 percent, the text changes and it stop for a second, and it goes back to 50, and the text changes again, it keep going until it hit 100 percent.
Here is my code:
public WelcomeScreen()
{
InitializeComponent();
_timer.Interval = 2000;
label1.ForeColor = Color.White;
}
private void WelcomeScreen_Load(object sender, EventArgs e)
{
var worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerAsync();
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var percentComplete = e.ProgressPercentage;
var userState = (string)e.UserState;
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
progressBar1.Value = 100;
progressBar1.Step = 25;
if (progressBar1.Step <= 25)
{
label1.Text = "Preparing Setup";
}
else if (progressBar1.Step <= 50)
{
label1.Text = "Preparing Application";
}
else if (progressBar1.Step <= 75)
{
label1.Text = "Preparing Database";
}
else if (progressBar1.Step <= 100)
{
label1.Text = "Preparing Contents";
}
else
{
label1.Text = "Launch Application";
}
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_timer.Enabled = true;
_timer.Tick += new EventHandler(Timer_Tick);
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
var currentWorker = (BackgroundWorker)sender;
currentWorker.ReportProgress(0, "Preparing Setup");
currentWorker.ReportProgress(25, "Preparing Application");
currentWorker.ReportProgress(50, "Preparing Database");
currentWorker.ReportProgress(75, "Preparing Contents");
currentWorker.ReportProgress(100, "Launch Application");
}
void Timer_Tick(object sender, EventArgs e)
{
_timer.Enabled = false;
this.Hide();
_login.ShowDialog();
this.Close();
}
When the progress bar hit 100 percent, i order the application to wait until 2 seconds before show another form after the text changes to "Launch Application".
All the code in your WelcomeScreen_Load will block the UI thread until it is complete. This means that no matter what you do to your progress bar, it will never show it's changes until it is complete (which it will "jump" to the last settings).
You will want to look into Background Workers. These let you do your code async, and report back every-so-often with what the current state is (ie: the percent complete).
So, a really short example:
private void WelcomeScreen_Load(object sender, EventArgs e)
{
var worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerAsync();
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var percentComplete = e.ProgressPercentage;
var userState = (string)e.UserState;
//do something with these values, like moving your progress bar
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
progressBar1.Value = percentComplete;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// do something when the worker completes, like start your timer
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
// do the "work" for the background worker
var currentWorker = (BackgroundWorker)sender;
currentWorker.ReportProgress(0, "Just Starting");
// do your first task
currentWorker.ReportProgress(25, "Finish First Task");
// ...
}
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.
I am trying to Design a WinForms control in C# which will get some data from a database while it's loading.
I want to use a progress bar to show the progress.
I tried this code (and also many others):
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Show();
progressBar1.Value = 10;
int n;
n = 50;//number of records in DB ,
double progress = 0;
double progstep = 25 / n;
for (int i = 1; i <= n; i++)
{
//getting
if (progress <= 100)
progressBar1.Value = (int)progress;
}
progressBar1.Value = 35;
n = 100;//number of records in DB for another data reading from DB ,
progress = 35;
progstep = 65 / n;
for (int i = 1; i <= n; i++)
{
//getting data from DB
dataGridView1.Rows.Add(....);
//Adding that data to a datagrid -- parametrs removed.
progress += progress;
if (progress <= 100)
progressBar1.Value = (int)progress;
}
}
But, the problem is that the form will wait until data reading progress is completed, and I can see just a full progress bar and all data loaded.
What should I do to fix this?
Since this is winforms, i'd recommend using a BackgroundWorker.
Basic example:
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged += new DoWorkEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerAsync(//pass in object to process)
Which would then kickoff:
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
//Do all of your work here
bgWorker.ReportProgress(); //percent done calculation
}
Then the Progress changed event would fire to update the UI safely:
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
Add a backgroundWorker1 to your form.
Then add a YourForm_Shown event
private void YourForm_Shown(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
Add on form's constructor after InitializeComponent()
backgroundWorker1.WorkerReportsProgress = true;
// This event will be raised on the worker thread when the worker starts.
backgroundWorker1.DoWork += new
DoWorkEventHandler(backgroundWorker1_DoWork);
// This event will be raised when we call ReportProgress.
backgroundWorker1.ProgressChanged += new
ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
And last add the voids of backgroundWorker1:
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
And:
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= n; i++)
{
//getting data from DB.
dataGridView1.Rows.Add(....);
//Adding that data to a datagrid -- parametrs removed.
backgroundWorker1.ReportProgress(i);
// Simulate long task
}
}
This is simple mockup to show you how to work with background worker:
First in your OnLoad create background worker and attach 2 events to it:
var bw = new BackgroundWorker();
bw.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(WorkCompleted);
bw.DoWork += new DoWorkEventHandler(DoWork);
bw.RunWorkerAsync(data); // Assume data is list of numbers.
private void WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
// After work completed remove event handlers and dispose.
{
var bw = (BackgroundWorker)sender;
bw.RunWorkerCompleted -= WorkCompleted;
bw.DoWork -= DoWork; bw.Dispose();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
var data = (List<int>)e.Argument;
foreach (var number in data)
{
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke((MethodInvoker)delegate
{ this.ProcessNumber(number); });
}
else
{
ProcessNumber(number);
}
}
}
private void ProcessNumber(int i)
{
progressBar1.PerformStep();
//do something with i
}
Take a look at BackgroundWorker control. During form load invoke;
backgroundWorker.RunWorkerAsync();
and override event DoWork to do the dirty work (load data from database) and ProgressChanged to update progress bar. In the event body (lets say the event signature will be something like this):
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
var worker = (BackgroundWorker)sender;
// time consuming operation
worker.ReportProgress(10, null);
// ... another stuff
}
private void backgroundWorker_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
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();