2009-08-11 40 views
19

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

Tengo algunos métodos que se ocupan de 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.

I pensado en utilizar barra de progreso o etiqueta de la tira de estado, pero puesto que hay un solo hilo de interfaz de usuario, el hilo donde se ejecutan los métodos de base de datos de negociación, los controles de interfaz de usuario no se actualizan, haciendo que la barra de progreso o etiqueta de la tira de estado se inútil para mí

ya he visto algunos ejemplos, pero los que tratan de-bucles, 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.

+17

Aquí hay una muy buena regla general: si usted se encuentra escribiendo Thread.Sleep (n), pulse la tecla de retroceso hasta que ya no existe más. Existen usos legítimos para Thread.Sleep, pero generalmente causa más problemas de los que resuelve. – MusiGenesis

+2

¿qué base de datos está utilizando? Según el tipo de acceso a la base de datos, es posible que pueda determinar el progreso. Por ejemplo, si usa un lector de datos, puede ejecutar una consulta de 'recuento' para ver cuánto se devolverá, luego use eso para determinar el% completado a medida que lee la respuesta a través del lector de datos. Si utiliza un DataAdapter para llenar una tabla, puede usar un evento en la tabla que notifique cuándo se agrega una fila y usar eso para actualizar el% completado del relleno. – galford13x

Respuesta

18

que algunas personas no les gusta, 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.

+0

que parecía ser más rápido y más limpio en el código por el momento, ya que este problema llevó mucho tiempo, pero seguiré buscando una mejor solución basada en lo que @Akash Kava discutió. así que estoy marcando esto como una buena respuesta temporal. –

+18

Los componentes de la interfaz gráfica de usuario tienen un significado y una barra de progreso indica "la cantidad de tarea que se realiza", también conocido como "progreso". Mientras que usted no es ciertamente el único que lo hace, si no se puede conocer el progreso que no debería fingir por abusar de una barra de progreso, en lugar de simplemente mostrar algún tipo de ocupados icono como http://en.wikipedia.org/wiki/Throbber # Spinning_wheel Muéstralo al iniciar la tarea y escóndela cuando haya terminado. Eso haría una GUI más "honesta". – user282727

+0

Su declaración '' if' en timer_Tick' nunca será cierto, ya que este código generará un ArgumentException si 'progressBar.Value + 5' 'es mayor que progressBar.Maximum' – alldayremix

5

Cuando lleva a cabo operaciones en el subproceso en segundo plano y desea actualizar la IU, no puede llamar o 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<count;i++) 
{ 
    DoSomething(); 
    this.Dispatcher.BeginInvoke((Action)delegate(){ 
     this.progressBar.Value = (int)((100*i)/count); 
    }); 
} 

Windows Forms:

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

para WinForms delegado puede requerir algo de fundición o puede que necesite algo de ayuda allí, no recuerdo ahora la sintaxis exacta.

+0

@ Akash-go-goo-go no está utilizando una explicidad de subprocesos en segundo plano, están usando un componente BackgroundWorker. – RichardOD

+0

thnx, pero el ciclo for utilizado en el ejemplo es inútil para mí, ya que el método HacerAlgo() es una actualización llamada, digamos 150.000 registros en la base de datos, y nadie quiere hacer esto para los tiempos de 'count'. –

+0

Desafortunadamente, winforms Invoke no permite delegados anónimos en absoluto. Debe usar '' new' con un tipo delegate' :( –

5

La idea detrás del informe de progreso con el trabajador de fondo es mediante el 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.

  • determinar cuántas filas se van a recuperar (SELECT COUNT (*) DE ...)
  • Divida la lectura real en trozos más pequeños, los informes de progreso cada vez que se completa un trozo:

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

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

+0

Una buena respuesta de usabilidad. – Rexxo

2

no he recopilado esta ya que está destinado para 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 usando el módulo System.Data.SQLite probado

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

Esto Helpfull.Easy de implementar, 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 
} 
Cuestiones relacionadas