Reputation:
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.
Upvotes: 20
Views: 153524
Reputation: 31
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;
}));
}
}
Upvotes: 0
Reputation: 411
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
}
Upvotes: 0
Reputation: 2314
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.
Upvotes: 19
Reputation: 2531
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);
}
Upvotes: 2
Reputation: 674
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.
Upvotes: 11
Reputation: 26772
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.
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)
}
Upvotes: 5
Reputation: 39916
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.
Upvotes: 6