2009-02-06 14 views
47

Ich kann nicht herausfinden, wie Sie eine C# Windows Form-Anwendung aus einem Thread in ein Textfeld schreiben können. Zum Beispiel in der Program.cs haben wir die Standard-main(), die die Form zieht:Schreiben in eine TextBox von einem anderen Thread?

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

Dann haben wir in der Form1.cs haben:

public Form1() 
{ 
    InitializeComponent(); 

    new Thread(SampleFunction).Start(); 
} 

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

Bin ich über diese völlig falsch gehen?

UPDATE

Hier ist das Arbeitscodebeispiel aus bendewey bereitgestellt:

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

Antwort

47

Auf dem Mainform eine Funktion macht das Textfeld zu setzen, die Kontrollen den InvokeRequired

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

obwohl In Ihrer statischen Methode können Sie nicht einfach anrufen.

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

Sie haben irgendwo einen statischen Verweis auf das Form1 haben, aber das ist nicht wirklich zu empfehlen oder notwendig ist, können Sie einfach Ihre sample nicht statisch machen, wenn ja, dann können Sie einfach

nennen
AppendTextBox("hi. "); 

Es wird an einen anderen Thread angehängt und mithilfe des Aufrufs Aufruf an die Benutzeroberfläche geleitet, falls erforderlich.

Beispiel anzeigen

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

ich Ihren Code versucht, und es ist nicht ganz für mich zu arbeiten. Ich schätze die Antwort, Kumpel. –

+0

Genauer kompiliert es, aber die TextBox wird nicht kontinuierlich geschrieben. –

+0

Kannst du den Code eingeben, den du versuchst? – bendewey

1

Werfen Sie einen Blick auf Control.BeginInvoke Methode. Der Punkt ist, niemals UI-Steuerelemente von einem anderen Thread zu aktualisieren. BeginInvoke wird den Aufruf an den UI-Thread des Steuerelements (in Ihrem Fall das Formular) senden.

Um das Formular zu greifen, entfernen Sie den statischen Modifikator aus der Beispielfunktion und verwenden Sie dies.BeginInvoke() wie in den Beispielen von MSDN gezeigt.

13

oder Sie tun können, wie

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

Was noch einfacher ist, ist nur die Kontrolle Background verwenden ...

4

Sie müssen die Aktion aus dem Thread auszuführen, der die Kontrolle besitzt.

Das ist, wie ich tue, dass zu viel Code Rauschen ohne Zugabe:

/// <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

ich würde BeginInvoke statt Invoke wie:

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

Wo Überlastung Invoke eine einfache Erweiterung von Lokad Shared Libraries ist so oft wie möglich, es sei denn, Sie müssen wirklich warten, bis Ihr Steuerelement aktualisiert wurde (was in Ihrem Beispiel nicht der Fall ist).BeginInvoke sendet den Delegaten in der WinForms-Nachrichtenwarteschlange und lässt den aufrufenden Code sofort weiterlaufen (in Ihrem Fall die for-Schleife in SampleFunction). Invoke postet nicht nur den Delegaten, sondern wartet auch, bis es abgeschlossen ist.

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

Nun, und wenn Sie noch mehr Lust bekommen möchten, gibt es auch die SynchronizationContext-Klasse, die Sie im Grunde lässt:

So in dem Verfahren AppendTextBox aus Ihrem Beispiel würden Sie Invoke mit BeginInvoke so ersetzen Machen Sie dasselbe wie Control.Invoke/Control.BeginInvoke, aber mit dem Vorteil, dass keine WinForms-Steuerreferenz bekannt sein muss. Here ist ein kleines Tutorial auf SynchronizationContext.

3

meist einfach, ohne die Delegierten der Sorge um

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

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

Hier ist das, was ich CrossThreadException zu vermeiden gemacht und von einem anderen Thread in das Textfeld zu schreiben.

Hier ist meine Schaltfläche Klick-Funktion- Ich möchte eine zufällige Anzahl von Threads generieren und dann ihre IDs durch den Aufruf von getID() erhalten; Methode und der TextBox-Wert in diesem Worker-Thread.

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

    } 

Hier ist getId (workerThread) -Code

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; 
     }); 
Verwandte Themen