如何在C#中实现进度条?

时间:2022-09-02 14:48:30

How do I implement a progress bar and backgroundworker for database calls in C#?

如何在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.

我想过使用进度条或状态条标签,但由于有一个UI线程,执行数据库处理方法的线程,UI控件没有更新,使得进度条或状态条标签对我来说没用。

I've already seen some examples, but they deal with for-loops, ex:

我已经看过一些例子,但它们处理for循环,例如:

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.

我正在寻找更好的例子。

6 个解决方案

#1


18  

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.

Marquee样式需要启用VisualStyles,但它会不断滚动,无需更新。我将其用于不报告其进度的数据库操作。

#2


11  

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.

如果你不知道进展你不应该通过滥用进度条来伪造它,而只是显示某种忙碌的图标,如en.wikipedia.org/wiki/Throbber#Spinning_wheel在启动任务时显示它并在它的时候隐藏它完了。这将成为一个更“诚实”的GUI。

#3


6  

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.

当您在后台线程上执行操作并且想要更新UI时,您无法从后台线程调用或设置任何内容。对于WPF,您需要Dispatcher.BeginInvoke,如果是WinForms,则需要Invoke方法。

WPF:

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:

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

对于WinForms委托可能需要一些演员或你可能需要一点帮助,不记得现在的确切语法。

#4


5  

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.

在您的情况下,大部分工作与数据库相关。据我所知,无法直接从DB获取进度信息。然而,你可以尝试做的是动态地分解工作。例如,如果您需要阅读大量数据,可以采用一种天真的方式来实现这一点。

  • Determine how many rows are to be retrieved (SELECT COUNT(*) FROM ...)
  • 确定要检索的行数(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)
    }
    

#5


2  

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

我没有编译它,因为它是用于概念证明。这就是我过去为数据库访问实现进度条的方法。此示例显示使用System.Data.SQLite模块访问SQLite数据库

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);
}

#6


0  

This will Helpfull.Easy to implement,100% tested.

这将有助于实现,100%经过测试。

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
}

#1


18  

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.

Marquee样式需要启用VisualStyles,但它会不断滚动,无需更新。我将其用于不报告其进度的数据库操作。

#2


11  

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.

如果你不知道进展你不应该通过滥用进度条来伪造它,而只是显示某种忙碌的图标,如en.wikipedia.org/wiki/Throbber#Spinning_wheel在启动任务时显示它并在它的时候隐藏它完了。这将成为一个更“诚实”的GUI。

#3


6  

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.

当您在后台线程上执行操作并且想要更新UI时,您无法从后台线程调用或设置任何内容。对于WPF,您需要Dispatcher.BeginInvoke,如果是WinForms,则需要Invoke方法。

WPF:

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:

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

对于WinForms委托可能需要一些演员或你可能需要一点帮助,不记得现在的确切语法。

#4


5  

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.

在您的情况下,大部分工作与数据库相关。据我所知,无法直接从DB获取进度信息。然而,你可以尝试做的是动态地分解工作。例如,如果您需要阅读大量数据,可以采用一种天真的方式来实现这一点。

  • Determine how many rows are to be retrieved (SELECT COUNT(*) FROM ...)
  • 确定要检索的行数(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)
    }
    

#5


2  

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

我没有编译它,因为它是用于概念证明。这就是我过去为数据库访问实现进度条的方法。此示例显示使用System.Data.SQLite模块访问SQLite数据库

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);
}

#6


0  

This will Helpfull.Easy to implement,100% tested.

这将有助于实现,100%经过测试。

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
}