¿Cómo implemento una barra de progreso en C #?

¿Cómo implemento una barra de progreso y un trabajador de fondo para las llamadas a la base de datos en C #?

Tengo algunos métodos que tratan con grandes cantidades de datos. Son operaciones de ejecución relativamente larga, por lo que quiero implementar una barra de progreso para que el usuario sepa que algo realmente está sucediendo.

Pensé en utilizar la barra de progreso o etiqueta de estado, pero dado que hay una única interfaz de usuario, el hilo donde se ejecutan los métodos de la base de datos, los controles de la interfaz no se actualizan, por lo que la barra de progreso o la etiqueta de la línea de estado me resultan inútiles.

Ya he visto algunos ejemplos, pero se ocupan de for-loops, por ejemplo:

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

Estoy buscando mejores ejemplos.

Algunas personas pueden no gustar, pero esto es lo que hago:

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

El estilo Marquee requiere que VisualStyles esté habilitado, pero se desplaza continuamente por sí mismo sin necesidad de actualizarse. Lo uso para operaciones de bases de datos que no informan su progreso.

Si no puede conocer el progreso, no debe fingir abusando de una barra de progreso; en su lugar, simplemente muestre algún tipo de ícono ocupado, como en.wikipedia.org/wiki/Throbber#Spinning_wheel. Muéstrelo al iniciar la tarea y ocúltela cuando esté terminado. Eso haría una GUI más “honesta”.

Cuando realiza operaciones en el subproceso en segundo plano y desea actualizar la IU, no puede llamar ni configurar nada desde el hilo de fondo. En el caso de WPF necesita Dispatcher.BeginInvoke y en el caso de WinForms necesita el método Invoke.

WPF:

 // assuming "this" is the window containing your progress bar.. // following code runs in background worker thread... for(int i=0;i 

WinForms:

 // assuming "this" is the window containing your progress bar.. // following code runs in background worker thread... for(int i=0;i 

para el delegado de WinForms puede requerir algo de conversión o puede que necesite poca ayuda allí, no recuerde la syntax exacta ahora.

La idea de informar el progreso con el trabajador de segundo plano es a través del envío de un evento ‘por ciento completado’. Usted es el responsable de determinar de alguna manera “cuánto” se ha completado el trabajo. Desafortunadamente, esta es a menudo la parte más difícil.

En su caso, la mayor parte del trabajo está relacionado con la base de datos. Según mi conocimiento, no hay forma de obtener información de progreso directamente de la base de datos. Sin embargo, lo que puedes intentar hacer es dividir el trabajo dinámicamente. Por ejemplo, si necesita leer muchos datos, una forma ingenua de implementar esto podría ser.

  • Determine cuántas filas se van a recuperar (SELECT COUNT (*) FROM …)
  • Divida la lectura real en fragmentos más pequeños, informando el progreso cada vez que se completa un fragmento:

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

No he comstackdo esto ya que está destinado a una prueba de concepto. Así es como he implementado una barra de progreso para el acceso a la base de datos en el pasado. Este ejemplo muestra el acceso a una base de datos SQLite utilizando el módulo System.Data.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); } 

Esto será útil. Fácil de implementar, 100% probado.

 for(int i=1;i