2011-04-26 24 views
14

Estoy atascado tratando de actualizar una barra de progreso de otros hilos ejecutados en una clase diferente. Para explicar lo que hago, creo que una imagen será mejor. Quiero actualizar la barra de progreso en el // punto AQUÍ: enter image description hereWPF C# - Actualizar la barra de progreso desde otra secuencia

He intentado usar un delegado, intenté con ReportProgress y creo que básicamente he tratado de usar todo lo que google informó en los primeros 100 resultados, sin éxito . Todavía estoy aprendiendo WPF y esta podría ser una manera tonta de proceder, estoy buscando una manera rápida y sucia para hacer el trabajo, pero no dudes en decirme lo que debería rediseñar para una aplicación más limpia.

EDIT: Más código.

En ExecutorWindow.xaml.cs:

public void RunExecutor() 
{ 
    // CREATE BACKGROUNDWORKER FOR EXECUTOR 
    execBackground.DoWork += new DoWorkEventHandler(execBackground_DoWork); 
    execBackground.RunWorkerCompleted += new RunWorkerCompletedEventHandler(execBackground_RunWorkerCompleted); 
    execBackground.ProgressChanged += new ProgressChangedEventHandler(execBackground_ProgressChanged); 
    execBackground.WorkerReportsProgress = true; 
    execBackground.WorkerSupportsCancellation = true; 
    // RUN BACKGROUNDWORKER 
    execBackground.RunWorkerAsync(); 
} 
private void execBackground_DoWork(object sender, DoWorkEventArgs e) 
{ 
    myExecutor = new Executor(arg1, arg2); 
    myExecutor.Run();    
} 

private void execBackground_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    MessageBox.Show("RunWorkerCompleted execBackground"); 
} 

private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    ExecutorProgressBar.Value += 1; 
} 

// TESTING 
private void updateProgressBar(int i) 
{ 
    ExecutorProgressBar.Value += i; 
} 

public delegate void callback_updateProgressBar(int i); 

En Executor.cs:

public void Run() 
{ 
    string[] options = new string[2]; 
    int i = 0; 

    while (LeftToRun > 0) 
    { 
     if (CurrentRunningThreads < MaxThreadsRunning) 
     { 
      BackgroundWorker myThread = new BackgroundWorker(); 
      myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork); 
      myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted); 
      myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged); 
      myThread.WorkerReportsProgress = true; 
      myThread.WorkerSupportsCancellation = true; 

      myThread.RunWorkerAsync(new string[2] {opt1, opt2}); 

      // HERE ? 
      CurrentRunningThreads++; 
      i++; 
      LeftToRun--; 

     } 
    } 

    while (CurrentRunningThreads > 0) { } 
    logfile.Close(); 
    MessageBox.Show("All Tasks finished"); 
} 

private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e) 
{ 
    BackgroundWorker myBackgroundWorker = sender as BackgroundWorker; 
    string[] options = (string[])e.Argument; 
    string machine = options[0]; 
    string script = options[1]; 
    // UPDATE HERE PROGRESSBAR ? 
    RemoteProcess myRemoteProcess = new RemoteProcess(machine, script); 
    string output = myRemoteProcess.TrueExec(); 
    // UPDATE HERE PROGRESSBAR ? 
    this.logfile.WriteLine(output); 
} 

private void backgroundWorkerRemoteProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    CurrentRunningThreads--; 
} 

private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    //myExecWindow.ExecutorProgressBar.Value = e.ProgressPercentage; // TESTING 
    //ExecutorWindow.callback_updateProgressBar(1); // TESTING 
} 

EDIT 2: lo tengo! Simple de hecho, pero creo que he estado buscando demasiado cerca para descubrirlo.

En mi clase ExecutorWindow:

private void execBackground_DoWork(object sender, DoWorkEventArgs e) 
{ 
    myExecutor = new Executor(arg1, arg2); 
    myExecutor.Run(sender); 
} 

private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    ExecutorProgressBar.Value += 1; 
} 

Y en mi clase Ejecutor:

private BackgroundWorker myExecutorWindow; 

[...] 

public void Run(object sender) 
{ 
      myExecutorWindow = sender as BackgroundWorker; 
      string[] options = new string[2]; 
      int i = 0; 

      while (LeftToRun > 0) 
      { 
       if (CurrentRunningThreads < MaxThreadsRunning) 
       { 
        BackgroundWorker myThread = new BackgroundWorker(); 
        myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork); 
        myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted); 
        myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged); 
        myThread.WorkerReportsProgress = true; 
        myThread.WorkerSupportsCancellation = true; 

        myThread.RunWorkerAsync(new string[2] {opt1, opt2}); 

        CurrentRunningThreads++; 
        i++; 
        LeftToRun--;  
       } 
      } 

[...] 

private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e) 
     { 
      BackgroundWorker myBackgroundWorker = sender as BackgroundWorker; 
      myBackgroundWorker.ReportProgress(1); 
      // PROCESSING MY STUFF HERE 
      myBackgroundWorker.ReportProgress(1); 
     } 

     private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      myExecutorWindow.ReportProgress(1); 
     } 

Gracias!

+0

Excelente descripción, +1. ¿Dibujó eso manualmente o con la ayuda de alguna herramienta de código a diagrama? –

+0

Mala descripción. El código en imagen no se puede escalar ni copiar/pegar. El código también parece incompleto (en puntos cruciales). ¿Dónde está DoWork? –

+0

@Teoman Soygul: Lo hice usando Visio. @Henk Holterman: Es cierto, puedo editar con el código DoWork y otras funciones, pero no creía que fuera relevante para el tema, es por eso que no las escribí. –

Respuesta

0

I got it! Simple de hecho, pero creo que he estado buscando demasiado cerca para descubrirlo.

En mi clase ExecutorWindow:

private void execBackground_DoWork(object sender, DoWorkEventArgs e) 
{ 
    myExecutor = new Executor(arg1, arg2); 
    myExecutor.Run(sender); 
} 

private void execBackground_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    ExecutorProgressBar.Value += 1; 
} 

Y en mi clase Ejecutor:

private BackgroundWorker myExecutorWindow; 

[...] 

public void Run(object sender) 
{ 
      myExecutorWindow = sender as BackgroundWorker; 
      string[] options = new string[2]; 
      int i = 0; 

      while (LeftToRun > 0) 
      { 
       if (CurrentRunningThreads < MaxThreadsRunning) 
       { 
        BackgroundWorker myThread = new BackgroundWorker(); 
        myThread.DoWork += new DoWorkEventHandler(backgroundWorkerRemoteProcess_DoWork); 
        myThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerRemoteProcess_RunWorkerCompleted); 
        myThread.ProgressChanged += new ProgressChangedEventHandler(backgroundWorkerRemoteProcess_ProgressChanged); 
        myThread.WorkerReportsProgress = true; 
        myThread.WorkerSupportsCancellation = true; 

        myThread.RunWorkerAsync(new string[2] {opt1, opt2}); 

        CurrentRunningThreads++; 
        i++; 
        LeftToRun--;  
       } 
      } 

[...] 

private void backgroundWorkerRemoteProcess_DoWork(object sender, DoWorkEventArgs e) 
     { 
      BackgroundWorker myBackgroundWorker = sender as BackgroundWorker; 
      myBackgroundWorker.ReportProgress(1); 
      // PROCESSING MY STUFF HERE 
      myBackgroundWorker.ReportProgress(1); 
     } 

     private void backgroundWorkerRemoteProcess_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      myExecutorWindow.ReportProgress(1); 
     } 
10

Puede ejecutar cualquier método en el subproceso de interfaz de usuario con este ejemplo muy básico

this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate() 
{  
    this.progressBar.Value= 20; // Do all the ui thread updates here 
})); 

Ejecución de comandos dentro de la Dispatcher.Invoke (...), en realidad se puede interactuar con la interfaz de usuario desde cualquier subproceso de trabajo, donde de lo contrario obtendrías una excepción.

Si realmente necesita tener el máximo control sobre el fondo roscas & principales (UI) actualizaciones de hilo, aquí es un tutorial fantástico en que: http://blog.decarufel.net/2009/03/good-practice-to-use-dispatcher-in-wpf.html

+0

Esta sería la solución para un hilo normal (Pool). El Bgw tiene soporte especial para el progreso (para evitar este patrón Invoke). –

1

Usted debe ser capaz de utilizar el método Dispatcher.Invoke

por ejemplo

Dispatcher.Invoke(
     new System.Action(() => myProgressBar.Value = newValue) 
     ); 
0

he encontrado una solución muy simple para crear un hilo para ejecutar cualquier bloque de código, así como Invocación mango volver al hilo principal para cambiar las propiedades del control. Funciona de la caja con .NET 4.5 y la llamada lambda en el Dispatcher podría adaptarse para funcionar con versiones anteriores de .NET. El principal beneficio es que es simplemente increíblemente simple y perfecto cuando solo necesitas un hilo rápido para un poco de código realmente básico.

Así que asumiendo que tiene una barra de progreso en alguna parte de su diálogo en el ámbito de hacer esto:

progBar.Minimum = 0; 
progBar.Maximum = theMaxValue; 
progBar.Value = 0; 

Dispatcher disp = Dispatcher.CurrentDispatcher; 

new Thread(() => { 
    // Code executing in other thread 
    while (progBar.Value < theMaxValue) 
    { 
     // Your application logic here 


     // Invoke Main Thread UI updates 
     disp.Invoke(
      () => 
      { 

       progBar.Value++; 
      } 
     ); 

    } 
}).Start(); 

También es necesario asegurarse de que tiene una referencia a WindowsBase.dll

Si quieres una más reutilizables fragmento de código ejecutándose como inicio de subproceso podría usar un método como delegado, pero encuentro que el lambda en línea es tan fácil para tareas simples y no es necesario tratar con eventos como con los enfoques de Trabajador de fondo.

Cuestiones relacionadas