2012-01-05 34 views
9

Tengo una aplicación de winform (un formulario), en este formulario hay un RichTextBox. En el constructor de este formulario creo una instancia de la clase MyClass. En el "Form_Load" llamo al método Initialisation desde la instancia MyClass.BackgroundWorker - Operación entre hilos no válida

En la forma constructor

myClass = new MyClass(RichTextBox richTextBox); 

En el Form_Load

myClass.Initialisation(); 

En el método Initialisation, en un bucle, leí algunas parmeters hacen otras materias. Para no congelar la aplicación (porque algunas operaciones pueden demorar algunos segundos), utilizo un BackgroundWorker. Lo uso así (ver el código a continuación).

Cuando ejecuto, me sale este error: operación de la Cruz-hilo no válido: ‘RichTextbox’ Control de accede desde un subproceso distinto del subproceso que se creó el.

¿Podría decirme cómo resolver esto? Trabajo perfecto cuando no acceda al richTextBox

public Class MyClass 
{ 
    static BackgroundWorker _bw; 
    public MyClass() 
    { 
     _bw = new BackgroundWorker 
     { 
      WorkerReportsProgress = true, 
      WorkerSupportsCancellation = true 
     }; 
     _bw.DoWork += bw_DoWork; 
     _bw.ProgressChanged += bw_ProgressChanged; 
     _bw.RunWorkerCompleted += bw_RunWorkerCompleted; 
    } 
    static void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     foreach (....) 
     { 
      if (....) 
      { 
       richtextBox.Text.AppendText("MyText"); 
      } 
     } 
     e.Result = true; 
    } 
    static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){} 
    static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e){} 
} 

Respuesta

29

Usando BackgroundWorker no le exime de las reglas normales de roscado - como la única el hilo de interfaz de usuario puede acceder a los componentes de interfaz de usuario.

Si desea actualizar la interfaz de usuario de una BackgroundWorker que no sea usando los eventos de progreso/finalización (que son elevadas en el hilo de interfaz de usuario) es necesario utilizar Control.Invoke/Control.BeginInvoke igual que lo haría en otras situaciones. Por ejemplo:

if (....) 
{ 
    Action action =() => richtextBox.Text.Add("MyText"); 
    richtextBox.Invoke(action); // Or use BeginInvoke 
} 
11

probar este código,

BeginInvoke((MethodInvoker)delegate 
{ 
    richtextBox.Text.Add("MyText"); 
}); 
+0

Esto funciona, gracias – mili

3

Con el componente BackgroundWorker, solo los eventos ProgressChanged y RunWorkerCompleted le permiten invocar métodos/propiedades en los controles de la interfaz de usuario (lo que siempre debe hacerse en el hilo de la interfaz de usuario). A medida que actualiza la interfaz de usuario en el evento DoWork que se ejecuta en un subproceso no relacionado con la interfaz de usuario que está recibiendo este error, probablemente debería actualizar los controles de la interfaz de usuario utilizando los métodos Invoke o BeginInvoke en el evento DoWork si lo desea.

0

Añadir:

e.Result = "MyText"; 

En su bw_DoWork Y:

richTextBox1.AppendText((string)e.Result); 

En su bw_RunWorkerCompleted

(alterarlo para adaptarse a su código)

EDIT:

Si se hace muchas veces durante el trabajo del BackgroundWorker 's, se puede añadir:

_bw.ReportProgress(0, "MyText"); 

a la bw_DoWork y:

richTextBox1.AppendText((string)e.UserState); 

a la bw_ProgressChanged.

1

Para hacerlo más limpio y en base a la sugerencia de Jon Skeet, hice un método de extensión que hace lo mismo, se puede cambiar el "this Label control" a this TextBox control o simplemente utilizar "this Control control" (y básicamente permitir que cada control para ser fácilmente actualizado):

internal static class ExtensionMethods 
{ 
    /// <summary> 
    /// Updates the label text while being used in a multithread app. 
    /// </summary> 
    /// <param name="control">The control.</param> 
    /// <param name="text">The text.</param> 
    internal static void UpdateThreadedText(this Label control, string text) 
    { 
     Action action =() => control.Text = text; 
     control.Invoke(action); 
    } 

    /// <summary> 
    /// Refreshes the threaded. 
    /// </summary> 
    /// <param name="control">The control.</param> 
    internal static void RefreshThreaded(this Label control) 
    { 
     Action action = control.Refresh; 
     control.Invoke(action); 
    } 
} 

Y a continuación, el uso es bastante simple:

this.yourLabelName.UpdateThreadedText("This is the text"); 
this.yourTextBoxName.UpdateThreadedText("This is the text"); 
this.yourControlName.UpdateThreadedText("This is the text"); 

o

this.yourLabelName.RefreshThreaded(); 

Funciona para mí muy bien :)

0

También puedes probar esto.

this.Invoke(new MethodInvoker(delegate() 
{ 
    richtextBox.Text.Add("MyText"); 
})); 
Cuestiones relacionadas