2011-06-12 14 views
5

Necesito ayuda tratando de entender lo que estoy haciendo mal. Estoy tratando de obtener una colección de elementos del registro del sistema en un hilo separado para evitar que el formulario se congele durante el proceso de recopilación. Puedo conseguir que el trabajador de segundo plano los agarre a todos, pero estoy teniendo algunos problemas para agregarlos al ListBox en el formulario.Trabajando con hilos C#

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 

    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries) 
    { 
    listBox1.Items.Add(
     entry.EntryType.ToString() + " - " + 
     entry.TimeWritten + "  - " + 
     entry.Source); 
    } 
} 

Obviamente, esto no funciona como se esperaba, ya que hay 2 hilos separados, y no se puede cambiar objetos en diferentes temas, como lo he descubierto. Entonces, si alguien pudiera guiarme en la dirección correcta, estaría agradecido.

+1

echar un vistazo a esta respuesta: http://stackoverflow.com/questions/1136399/how-to -update-textbox-on-gui-from-another-thread-in-C# – Klinger

Respuesta

4

No debe acceder a los elementos de la IU desde un hilo que no sea UI. Ejecute ReportProgress, que se sincronizará con el subproceso de interfaz de usuario.

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries) 
    { 
     var newEntry = entry.EntryType + " - " + entry.TimeWritten + "  - " + entry.Source; 
     backgroundWorker1.ReportProgress(0, newEntry); 
    } 
} 

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    var newEntry = (string)e.UserState; 
    listBox1.Items.Add(newEntry); 
} 

Asegúrese de habilitar WorkerReportsProgress.

backgroundWorker1.WorkerReportsProgress = true; 

y se ha suscrito a ProgressChanged

backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged; 

Otro enfoque es llamar Control.Invoke dentro

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries) 
    { 
     var newEntry = entry.EntryType.ToString() + " - " + entry.TimeWritten + "  - " + entry.Source; 
     Action action =() => listBox1.Items.Add(newEntry); 
     Invoke(action); 
    } 
} 

Sin embargo, con este método no es necesario BackgroundWorker como punto entero de que es usar ProgressChanged y RunWorkerCompleted controlador de eventos que se sincronizan con el subproceso de interfaz de usuario.

1

Debe usar la opción de progreso del informe del trabajador en segundo plano. google this

0

Solo el hilo de la GUI puede modificar los elementos de la GUI. Si no respeta esta regla, obtendrá una excepción. Es posible:

  1. utilizar un MethodInvoker para desencadenar una llamada desde dentro de la interfaz gráfica de usuario de hilo
  2. utilizar la función de informe de situación (que en realidad hace 1. Para ti)
  3. tienda de los objetos de otra estructura de datos (bloqueo) y use un temporizador en el hilo de la GUI para extraer los objetos y mostrarlos.
1

No debe haber ningún problema ya que está usando BackgroundWorker. Toda la llamada al método de devolución de llamada se ejecuta en el mismo contexto de interfaz de usuario.

EDIT:

si desea informar de los progresos, es necesario almacenar SynchronizationContext.Current de preferencia de inicio. o puede usar el patrón IsInvokeRequired. Así es como yo utilizo SynchronizationContext

private SynchronizationContext uiContext; 
     public Form1() 
     { 
      uiContext = SynchronizationContext.Current; 
      InitializeComponent(); 
      FillItem(); 
     } 

He siguiente código, y está trabajando como encanto.

public void FillItem() 
      { 
       BackgroundWorker worker = new BackgroundWorker(); 
       worker.WorkerReportsProgress = true; 
       worker.DoWork += (a, b) => 
            { 
             int i = 0; //Percentage complete, roll your own logic. 
             foreach (var eventLog in EventLog.GetEventLogs()) 
             { 
              foreach (EventLogEntry entry in eventLog.Entries) 
              { 
               this.listBox1.Items.Add(entry.Message); 
uiContext.Post(z=>worker.ReportProgress(i++),null); 

              } 
             } 


            }; 
       worker.RunWorkerAsync(); 
       worker.ProgressChanged += (a, b) => this.progressBar1.Value = b.ProgressPercentage; 


      } 
1

Prueba de esto, de manera muy sencilla de invocar una acción en el subproceso del control:

private void Form1_Load(object sender, EventArgs e) 
{ 
    var bw = new BackgroundWorker(); 
    bw.DoWork += DoWork; 
    bw.RunWorkerAsync(); 
} 
private void DoWork(object sender, DoWorkEventArgs e) 
{ 
    var itemList = new List<int> {1, 22, 3, 4}; 
    var func = new Action<int>(itemToAdd => listBox1.Items.Add(itemToAdd)); 
    foreach (var item in itemList) 
    { 
     listBox1.Invoke(func, item); 
    } 
}