2009-02-06 20 views
47

No puedo entender cómo hacer que una aplicación C# Windows Form escriba en un cuadro de texto desde un hilo. Por ejemplo, en los Program.cs tenemos la norma principal() que dibuja la forma:¿Escribir en un TextBox desde otro hilo?

static void Main() 
{ 
    Application.EnableVisualStyles(); 
    Application.SetCompatibleTextRenderingDefault(false); 
    Application.Run(new Form1()); 
} 

Entonces tenemos en los Form1.cs:

public Form1() 
{ 
    InitializeComponent(); 

    new Thread(SampleFunction).Start(); 
} 

public static void SampleFunction() 
{ 
    while(true) 
     WindowsFormsApplication1.Form1.ActiveForm.Text += "hi. "; 
} 

¿Voy sobre este completamente equivocado?

ACTUALIZACIÓN

Aquí está el ejemplo de código de trabajo previsto de bendewey:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     new Thread(SampleFunction).Start(); 
    } 

    public void AppendTextBox(string value) 
    { 
     if (InvokeRequired) 
     { 
      this.Invoke(new Action<string>(AppendTextBox), new object[] {value}); 
      return; 
     } 
     textBox1.Text += value; 
    } 

    void SampleFunction() 
    { 
     // Gets executed on a seperate thread and 
     // doesn't block the UI while sleeping 
     for(int i = 0; i<5; i++) 
     { 
      AppendTextBox("hi. "); 
      Thread.Sleep(1000); 
     } 
    } 
} 

Respuesta

47

En su MainForm hacer una función para establecer el cuadro de texto los controles del InvokeRequired

public void AppendTextBox(string value) 
{ 
    if (InvokeRequired) 
    { 
     this.Invoke(new Action<string>(AppendTextBox), new object[] {value}); 
     return; 
    } 
    ActiveForm.Text += value; 
} 

aunque en su método estático no puede simplemente llamar.

WindowsFormsApplication1.Form1.AppendTextBox("hi. "); 

usted tiene que tener una referencia estática a la Form1 en alguna parte, pero esto no es muy recomendable o necesario, puede que acaba de hacer su sampleFunction no es estático si es así, sólo se puede llamar

AppendTextBox("hi. "); 

Se agregará en un subproceso diferente y se organizará en la interfaz de usuario mediante la llamada Invoke, si es necesario.

muestra completa

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     new Thread(SampleFunction).Start(); 
    } 

    public void AppendTextBox(string value) 
    { 
     if (InvokeRequired) 
     { 
      this.Invoke(new Action<string>(AppendTextBox), new object[] {value}); 
      return; 
     } 
     textBox1.Text += value; 
    } 

    void SampleFunction() 
    { 
     // Gets executed on a seperate thread and 
     // doesn't block the UI while sleeping 
     for(int i = 0; i<5; i++) 
     { 
      AppendTextBox("hi. "); 
      Thread.Sleep(1000); 
     } 
    } 
} 
+0

Probé su código, y no es bastante trabajo para mí. Aprecio la respuesta compañero. –

+0

Más específicamente, compila, pero el TextBox no se escribe continuamente. –

+0

¿Puedes publicar el código que intentas? – bendewey

1

Tenga una mirada en Control.BeginInvoke método. El punto es nunca actualizar los controles de UI desde otro hilo. BeginInvoke enviará la llamada al subproceso de interfaz de usuario del control (en su caso, el Formulario).

Para tomar el formulario, elimine el modificador estático de la función de muestra y use this.BeginInvoke() como se muestra en los ejemplos de MSDN.

13

o puede hacer como

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     new Thread(SampleFunction).Start(); 
    } 

    void SampleFunction() 
    { 
     // Gets executed on a seperate thread and 
     // doesn't block the UI while sleeping 
     for (int i = 0; i < 5; i++) 
     { 
      this.Invoke((MethodInvoker)delegate() 
      { 
       textBox1.Text += "hi"; 
      }); 
      Thread.Sleep(1000); 
     } 
    } 
} 
2

Lo que es aún más fácil es que sólo tiene que utilizar el control BackgroundWorker ...

4

Es necesario para realizar la acción desde el subproceso que posee el control.

Así es como yo estoy haciendo eso sin añadir demasiado ruido código:

control.Invoke(() => textBox1.Text += "hi"); 

Dónde Invocar la sobrecarga es una simple extensión de Lokad Shared Libraries:

/// <summary> 
/// Invokes the specified <paramref name="action"/> on the thread that owns  
/// the <paramref name="control"/>.</summary> 
/// <typeparam name="TControl">type of the control to work with</typeparam> 
/// <param name="control">The control to execute action against.</param> 
/// <param name="action">The action to on the thread of the control.</param> 
public static void Invoke<TControl>(this TControl control, Action action) 
    where TControl : Control 
{ 
    if (!control.InvokeRequired) 
    { 
    action(); 
    } 
    else 
    { 
    control.Invoke(action); 
    } 
} 
10

me gustaría utilizar BeginInvoke en lugar de Invoke como a menudo como sea posible, a menos que realmente se requiera esperar hasta que se haya actualizado su control (lo cual en su ejemplo no es el caso).BeginInvoke publica el delegado en la cola de mensajes de WinForms y permite que el código de llamada proceda inmediatamente (en su caso, for-loop en el SampleFunction). Invoke no solo publica el delegado, sino que también espera hasta que se haya completado.

Así que en el método AppendTextBox de su ejemplo que reemplazaría Invoke con BeginInvoke así:

public void AppendTextBox(string value) 
{ 
    if (InvokeRequired) 
    { 
     this.BeginInvoke(new Action<string>(AppendTextBox), new object[] {value}); 
     return; 
    } 
    textBox1.Text += value; 
} 

Bueno y si desea obtener aún más elegante, también hay la clase SynchronizationContext, lo que le permite, básicamente, haga lo mismo que Control.Invoke/Control.BeginInvoke, pero con la ventaja de no necesitar una referencia de control WinForms para ser conocido. Here es un pequeño tutorial en SynchronizationContext.

3

más sencilla, sin preocuparse por los delegados

if(textBox1.InvokeRequired == true) 
    textBox1.Invoke((MethodInvoker)delegate { textBox1.Text = "Invoke was needed";}); 

else 
    textBox1.Text = "Invoke was NOT needed"; 
2

Aquí es el lo que he hecho para evitar CrossThreadException y escribir en el cuadro de texto desde otro hilo.

Aquí está la función de hacer clic en mi botón: quiero generar un número aleatorio de hilos y luego obtener sus ID llamando a getID(); método y el valor del cuadro de texto mientras está en ese hilo de trabajo.

private void btnAppend_Click(object sender, EventArgs e) { 

     Random n = new Random(); 

     for (int i = 0; i < n.Next(1,5); i++) 
     { 
      label2.Text = "UI Id" + ((Thread.CurrentThread.ManagedThreadId).ToString()); 
      Thread t = new Thread(getId); 
      t.Start(); 
     } 

    } 

Aquí es getId (WorkerThread) Código

public void getId() 
    { 
     int id = Thread.CurrentThread.ManagedThreadId; 
     //Note that, I have collected threadId just before calling 
     //*this.Invoke* 
     //method else it would be same as of UI thread inside the below code 
     //block 
     this.Invoke((MethodInvoker)delegate() 
     { 
      inpTxt.Text += "My id is" +"--"+id+Environment.NewLine; 
     }); 
Cuestiones relacionadas