How do I implement a progress bar in C#? - c#

How do I implement a progress bar and backgroundworker for database calls in C#?
I do have some methods that deal with large amounts of data. They are relatively long running operations, so I want to implement a progress bar to let the user know that something is actually happening.
I thought of using progress bar or status strip label, but since there is a single UI thread, the thread where the database-dealing methods are executed, UI controls are not updated, making the progress bar or status strip label are useless to me.
I've already seen some examples, but they deal with for-loops, ex:
for(int i = 0; i < count; i++)
{
System.Threading.Thread.Sleep(70);
// ... do analysis ...
bgWorker.ReportProgress((100 * i) / count);
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = Math.Min(e.ProgressPercentage, 100);
}
I'm looking for better examples.

Some people may not like it, but this is what I do:
private void StartBackgroundWork() {
if (Application.RenderWithVisualStyles)
progressBar.Style = ProgressBarStyle.Marquee;
else {
progressBar.Style = ProgressBarStyle.Continuous;
progressBar.Maximum = 100;
progressBar.Value = 0;
timer.Enabled = true;
}
backgroundWorker.RunWorkerAsync();
}
private void timer_Tick(object sender, EventArgs e) {
if (progressBar.Value < progressBar.Maximum)
progressBar.Increment(5);
else
progressBar.Value = progressBar.Minimum;
}
The Marquee style requires VisualStyles to be enabled, but it continuously scrolls on its own without needing to be updated. I use that for database operations that don't report their progress.

If you can't know the progress you should not fake it by abusing a progress bar, instead just display some sort of busy icon like en.wikipedia.org/wiki/Throbber#Spinning_wheel Show it when starting the task and hide it when it's finished. That would make for a more "honest" GUI.

When you perform operations on Background thread and you want to update UI, you can not call or set anything from background thread. In case of WPF you need Dispatcher.BeginInvoke and in case of WinForms you need Invoke method.
WPF:
// assuming "this" is the window containing your progress bar..
// following code runs in background worker thread...
for(int i=0;i<count;i++)
{
DoSomething();
this.Dispatcher.BeginInvoke((Action)delegate(){
this.progressBar.Value = (int)((100*i)/count);
});
}
WinForms:
// assuming "this" is the window containing your progress bar..
// following code runs in background worker thread...
for(int i=0;i<count;i++)
{
DoSomething();
this.Invoke(delegate(){
this.progressBar.Value = (int)((100*i)/count);
});
}
for WinForms delegate may require some casting or you may need little help there, dont remember the exact syntax now.

The idea behind reporting progress with the background worker is through sending a 'percent completed' event. You are yourself responsible for determining somehow 'how much' work has been completed. Unfortunately this is often the most difficult part.
In your case, the bulk of the work is database-related. There is to my knowledge no way to get progress information from the DB directly. What you can try to do however, is split up the work dynamically. E.g., if you need to read a lot of data, a naive way to implement this could be.
Determine how many rows are to be retrieved (SELECT COUNT(*) FROM ...)
Divide the actual reading in smaller chunks, reporting progress every time one chunk is completed:
for (int i = 0; i < count; i++)
{
bgWorker.ReportProgress((100 * i) / count);
// ... (read data for step i)
}

I have not compiled this as it is meant for a proof of concept. This is how I have implemented a Progress bar for database access in the past. This example shows access to a SQLite database using the System.Data.SQLite module
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
using(SQLiteConnection cnn = new SQLiteConnection("Data Source=MyDatabase.db"))
{
cnn.Open();
int TotalQuerySize = GetQueryCount("Query", cnn); // This needs to be implemented and is not shown in example
using (SQLiteCommand cmd = cnn.CreateCommand())
{
cmd.CommandText = "Query is here";
using(SQLiteDataReader reader = cmd.ExecuteReader())
{
int i = 0;
while(reader.Read())
{
// Access the database data using the reader[]. Each .Read() provides the next Row
if(worker.WorkerReportsProgress) worker.ReportProgress(++i * 100/ TotalQuerySize);
}
}
}
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Notify someone that the database access is finished. Do stuff to clean up if needed
// This could be a good time to hide, clear or do somthign to the progress bar
}
public void AcessMySQLiteDatabase()
{
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(
backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged +=
new ProgressChangedEventHandler(
backgroundWorker1_ProgressChanged);
}

This will Helpfull.Easy to implement,100% tested.
for(int i=1;i<linecount;i++)
{
progressBar1.Value = i * progressBar1.Maximum / linecount; //show process bar counts
LabelTotal.Text = i.ToString() + " of " + linecount; //show number of count in lable
int presentage = (i * 100) / linecount;
LabelPresentage.Text = presentage.ToString() + " %"; //show precentage in lable
Application.DoEvents(); keep form active in every loop
}

You have to execute the process from a thread, and from the thread you invoke the progress bar and change its value, maybe this example helps you
public void main()
{
int count = 20;
progressbar.Maximum = count;
progressbar.Value = 0;
new Thread(() => Work(progressbar, count)).Start();
}
public static void Work(ProgressBar progressbar, int count)
{
for (int i = 0; i <= count; i++)
{
Thread.Sleep(70);
// ... do analysis ...
Application.Current.Dispatcher.Invoke(new Action(() =>
{
progressbar.Value = i;
}));
}
}

Related

Why ProgressBar does not displaying the current value / does not refreshing?

I use the following code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
progressBar1.Value = 0;
progressBar1.Step = 1;
progressBar1.Maximum = 100;
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
progressBar1.PerformStep();
label1.Text = (i + 1).ToString();
this.Refresh();
}
}
}
But, even after this.Refresh(); the value of the progress bar does not updated. Only the label updated. When the labels already show 100, for progress bar stil have more few steps to finish.
What i do wrong?
why the value of the progress bar is not updated?
How i should do it correct?
are you using Task, async, await? this is a common sample in winforms
see IProgress
public void DoWork(IProgress<int> progress)
{
// This method is executed in the context of
// another thread
for (int j = 0; j < 100000; j++)
{
//DO something
// Use progress to notify UI thread that progress has
// changed
if (progress != null)
progress.Report((j + 1) * 100 / 100000);
}
}
private async void button_Click(object sender, EventArgs e)
{
progressBar.Maximum = 100;
progressBar.Step = 1;
var progress = new Progress<int>(v =>
{
// This lambda is executed in context of UI thread,
// so it can safely update form controls
progressBar.Value = v;
});
// Run operation in another thread
await Task.Run(() => DoWork(progress));
}
I tried your code and it worked fine for me, did you add any special properties to your progress bar?
Assuming that it is all there is to it, try removing it and adding a new one without adjusting its default properties, you can also try adjusting the value in your Thread.Sleep() so that you can see the progress more

Why is the event RunWorkerCompletedEventArgs launched before I finished loading the progress bar in its entirety?

When I'm running a heavy task or a few data, at the end of the task the label showing the percentage reaches 100% correctly and displays the finalized message, but even the progressBar has not loaded in its entirety, when the completion message is displayed you can see the animation of the progress bar that completes your work.
I have not been able to solve this problem, and the natural way that a progress bar should work as in all the systems that I have seen, the progress is completed and then show a finished message.
Here I leave an example:
BackgroundWorker bg = new BackgroundWorker();
private void btnRun_Click(object sender, EventArgs e)
{
bg.WorkerReportsProgress = true;
bg.ProgressChanged += bg_ProgressChanged;
bg.DoWork += bg_DoWork;
bg.RunWorkerCompleted += bg_RunWorkerCompleted;
bg.RunWorkerAsync();
label1.Visible = true;
progressBar1.Visible = true;
}
Dowork Event:
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
int progress = 0, percent = 0;
for (int i = 0; i < ds.Tables[0].Rows.Count; i++) //Cycle that will represent the heavy task
{
totalRecords = ds.Tables[0].Rows.Count;
progress++;
percent = Convert.ToInt16((((double)progress / (double)totalRecords ) * 100.00));
System.Threading.Thread.Sleep(500);
bg.ReportProgress(percent );
}
}
ProgressChanged
private void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Step = 1;
progressBar1.Style = ProgressBarStyle.Continuous;
progressBar1.Minimum = 0;
progressBar1.Maximum = 100;
if (e.ProgressPercentage > 100)
{
label1.Text = "100%";
progressBar1.Value = progressBar1.Maximum;
}
else
{
label1.Text = Convert.ToString(e.ProgressPercentage) + "%";
progressBar1.Value = e.ProgressPercentage;
}
}
Finally, the RunWorkerCompleted event that is executed when the BackgroundWorker has finished:
private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Done...");
label1.Visible = false;
progressBar1.Visible = false;
}
How can I solve this progress bar animation problem?
Environment: Visual Studio 2010 (WindowsForms) & .NET NetFramework 4.
I think you're facing a classic problem of Windows' Aero animations.
Informations extracted from this page.
This lag happens when a progress bar is incremented. But it doesn't happen when the progress bar is decremented.
So basically, what you want to do is move past the actual value you should get to, then decrement to the actual value.
The author of the page uses an extension method, feel free to do as well; I'm just putting the relevant code here:
// To get around the progressive animation, we need to move the
// progress bar backwards.
if (value == pb.Maximum)
{
// Special case as value can't be set greater than Maximum.
pb.Maximum = value + 1; // Temporarily increase Maximum
pb.Value = value + 1; // Move past
pb.Maximum = value; // Reset maximum
}
else
{
pb.Value = value + 1; // Move past
}
pb.Value = value;

How do I keep a BackgroundWorker from blocking the UI thread?

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...

Adding items to a ListBox in a parallel way

I'm writing a simple application (for testing purposes) that adds 10 M elements to a ListBox. I'm using a BackgroundWorker to do the work and a ProgressBar control to display the progress.
Each element is a just a "Hello World!" string with and index that I'm adding during the process. My program takes ~ 7-8 seconds to fill the ListBox, and I thought if it's possible to speed up this, by using all the available cores on my PC (8).
To achieve that, I've tried to use the TPL library, more precise the Parallel.For loop, but the results are unpredictable or it doesn't work as I want it to.
Here's the code of my application:
private BackgroundWorker worker = new BackgroundWorker();
private Stopwatch sw = new Stopwatch();
private List<String> numbersList = new List<String>();
public MainWindow()
{
InitializeComponent();
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
sw.Start();
int max = 10000000;
int oldProgress = 0;
for (int i = 1; i <= max; i++)
{
numbersList.Add("Hello World! [" + i + "]");
int progressPercentage = Convert.ToInt32((double)i / max * 100);
// Only report progress when it changes
if (progressPercentage != oldProgress)
{
worker.ReportProgress(progressPercentage);
oldProgress = progressPercentage;
}
}
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pb.Value = e.ProgressPercentage;
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lstLoremIpsum.ItemsSource = numbersList;
lblCompleted.Content = "OK";
lblCompleted.Content += " (" + numbersList.Count + " elements added" + ")";
lblElementiLista.Content += " (" +sw.Elapsed.TotalSeconds + ")";
worker.Dispose();
}
}
And the parallel implementation that I've tried to write (this goes in DoWork):
Parallel.For(1, max, i =>
{
lock (lockObject)
{
numbersList.Add("Hello World! [" + i + "]");
}
int progressPercentage = Convert.ToInt32((double)i / max * 100);
// Only report progress when it changes
if (progressPercentage != oldProgress)
{
worker.ReportProgress(progressPercentage);
oldProgress = progressPercentage;
}
});
The results is that the application freezes, and takes about 15 seconds to fill up my ListBox. (The elements are also unordered)
What can be done in this case and will parallelism speed up the "filling" process?
The lock statement in your thread basically reduces your parallel processing to sequential processing, but with the overhead of acquiring a lock (making it effectively slower).
Also there are a limited number of thread pool threads which can be used here so you won't get your full 10m concurrently adding.
I think a better way is to use a non UI thread to populate the list and then bind it afterwards - this will ensure the UI isn't frozen/unusable while the 10 million iteration loop is running:
public MainWindow()
{
InitializeComponent();
Task.Factory.StartNew(PopList);
}
Then you can call the UI thread when needed:
private void PopList()
{
sw.Start();
int max = 10000000;
int oldProgress = 0;
for (int i = 1; i <= max; i++)
{
numbersList.Add("Hello World! [" + i + "]");
int progressPercentage = Convert.ToInt32((double)i / max * 100);
// Only report progress when it changes
if (progressPercentage != oldProgress)
{
Dispatcher.BeginInvoke(new Action(() => { pb.Value = progressPercentage; }));
oldProgress = progressPercentage;
}
}
Dispatcher.BeginInvoke(new Action(() => { lstLoremIpsum.ItemsSource = numbersList; }));
}
In an MVVM world you can just set the bound IEnumerable instead of the ItemsSource as shown in the above example.
You are locking the list on each add, and all the process load is just that, adding an element to the list, so instead of speeding up things you are slowing them because there are really no parallel work.
If your list of items is of a known size (as it seems), instead of a list create an array with the appropiated size and then in the parallel for loop set the appropiated item to it's value, in this way no locking is performed and it should be faster.
Also, in your code you don't show when the list view is populated, just the list, so I suppose you are using this list as datasource, before setting it do a listView.BeginUpdate() and after setting it listView.EndUpdate(), it may speed up things a bit,m the listview is a bit slow when adding elements.
If you use Parallel.For, then you don't need a BackgroundWorker. And the Worker doesn't work anymore as expected anyway, since you're trying to access it from another thread.
Remove the BackgroundWorker and do the Parallel.For directly, using Interlocked methods to update the progress bar:
private int ProgressPercentage { get; set; }
private void DoWork()
{
Parallel.For(1, max, i =>
{
lock (lockObject)
{
numbersList.Add("Hello World! [" + i + "]");
}
int progressPercentage = Convert.ToInt32((double)i / max * 100);
// Only report progress when it changes
if (progressPercentage != oldProgress)
{
Interlocked.Exchange(ProgressPercentage, progressPercentage);
ShowProgress();
}
});
}
private void ShowProgress()
{
pb.Value = ProgressPercentage;
}

How do i use backgroundworker with timer and progressBar?

I have a timer tick event that it's interval set to 10000
private void timer1_Tick(object sender, EventArgs e)
{
Update();
}
In Update i have:
public int Update()
{
counter += 1;
int position = (int)Math.Round((counter / updateTime) * 100);
xpProgressBar1.Text = counter.ToString() + " %";
xpProgressBar1.Position = counter;
if (counter == 10)
{
DownloadingHtml();
ScrollNews();
counter = 0;
}
return position;
}
Then in the backgroundworker do work:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int position = NewsUpdate();
object param = "report";
backgroundWorker1.ReportProgress(position, param);
}
And the backgroundworker progress event:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
xpProgressBar1.Text = e.ProgressPercentage.ToString() + " %";
xpProgressBar1.Position = e.ProgressPercentage;
if (counter == 10)
{
DownloadingHtml();
ScrollNews();
counter = 0;
}
}
What i want to do in the first step is that the Update method will be called each 10 seconds but through the backgroundworker.
In the second step i want to add to the backgroundworker another method:
public void ScrollNews()
{
label3.Text = SaveOldHtml.HtmlLoadedFileNumber.ToString();
richTextBox1.Clear();
combindedString = string.Join(Environment.NewLine, ListsExtractions.myList);
richTextBox1.SelectAll();
richTextBox1.SelectionAlignment = HorizontalAlignment.Right;
richTextBox1.Text = combindedString;
scrollerText = string.Join(Environment.NewLine, ListsExtractions.myListWithoutLinks);
scroller1.TextToScroll = scrollerText;
if (NewsLevels.newsLevel && NewsLevels.shouldStart)
{
scroller1.Start();
NewsLevels.shouldStart = false;
}
if (NewsLevels.newsLevel == false && NewsLevels.shouldStart)
{
scroller1.Start();
NewsLevels.shouldStart = false;
}
string[] rlines = richTextBox1.Lines;
richTextBox1.SelectionStart = 0;
richTextBox1.SelectionLength = rlines[0].Length;
richTextBox1.SelectionColor = Color.Red;
richTextBox1.Select(rlines[0].Length, rlines[1].Length + 1);
richTextBox1.SelectionColor = Color.Green;
}
The ScrollNews method is being called from the Update method and it's updating richTextBox1 and Scroller1 with text.
And in the end i want to add the last method in Update:
private void DownloadingHtml()
{
using (var webClient = new WebClient())
{
webClient.Encoding = System.Text.Encoding.GetEncoding(1255);
page = webClient.DownloadString("http://rotter.net/scoopscache.html");
}
StreamWriter w = new StreamWriter(#"d:\rotterhtml\rotterscoops.html");
w.Write(page);
w.Close();
page = #"d:\rotterhtml\rotterscoops.html";
listsext.Ext(page);
count++;
}
All this methods i want to be working from the backgroundworker.
In the form1 constructor i did that first it will call the DownloadingHtml method once then call the ScrollNews method once then activate the backgroundworker and then start the timer1.
Seems like you are misusing BackgroundWorker class. It is usually used to perform a single time-consuming action that should not block th UI. All time consuming operations should take place in OnDoWork event that is executed on a separate thread. Report progress is executed on UI thread and is used to update progress bar and other UI elements that show progress.
timer1_Tick is executed on the UI thread and blocks your UI while executing. It's not a good idea to perform any downloading or processing there if you don't want your UI to hang.
You could start TPL Task, Thread or just start BackgroundWorker anew in every timer1_Tick execution. This Task or Thread can then report progress and update current UI state, calling form's thread-safe methods. BackgroundWorker can use it's own ReportProgress mechanism for this purpose.
In case of using separate Task or Thread, each method called from a separate thread should check Form's InvokeRequired and call BeginInvoke to perform thread-safe UI update. This is described well here: beginInvoke, GUI and thread and in many other similar questions.

Categories

Resources