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

如何在C#中为数据库调用实现进度条和背景工作?

我有一些处理大量数据的方法。 他们是相对较长的运行操作,所以我想实现一个进度条,让用户知道事情正在发生。

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

我已经看到了一些例子,但他们处理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); } 

我正在寻找更好的例子。

有些人可能不喜欢它,但这是我所做的:

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

“选取框”样式需要启用VisualStyles,但它不需要更新就可以自行滚动。 我将其用于不报告其进度的数据库操作。

如果你不知道进度,你不应该通过滥用进度条来伪造它,而应该显示某种繁忙的图标,比如en.wikipedia.org/wiki/Throbber#Spinning_wheel当开始任务时显示它,当它被隐藏时完了。 这将使一个更“诚实”的graphics用户界面。

当您在后台线程上执行操作并且想要更新UI时,无法从后台线程调用或设置任何内容。 在WPF的情况下,你需要Dispatcher.BeginInvoke和WinForms的情况下,你需要调用方法。

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

为WinForms委托可能需要一些铸造或您可能需要一点帮助,现在不记得确切的语法。

与后台工作人员报告进度背后的想法是通过发送“完成百分比”事件。 你自己负责确定“多less”工作已经完成。 不幸的是,这往往是最困难的部分。

就你而言,大部分工作都是与数据库相关的。 据我所知,没有办法直接从DB获取进度信息。 然而,你可以尝试做什么,dynamic地分工。 例如,如果你需要阅读大量的数据,那么实现这一点的方法可能是天真的。

  • 确定要检索的行数(SELECT COUNT(*)FROM …)
  • 将实际阅读分成较小的块,每完成一个块就报告进度:

     for (int i = 0; i < count; i++) { bgWorker.ReportProgress((100 * i) / count); // ... (read data for step i) } 

我没有编译这个,因为它是为了一个概念的certificate。 这是我过去如何实现数据库访问的进度条。 此示例显示使用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); } 

这将帮助完成。易于实施,100%testing。

 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 }