2009-09-04 6 views
5

Possible en double:
Getting Cross-thread operation not valid
Cross-thread operation not validopération de la Croix-fil non valide tout en écoutant un port COM

Je suis en train d'écouter le port COM pour que je créer un nouveau gestionnaire pour l'événement SerialPort.DataReceived. La logique est simple - j'écris quelque chose à TextBox1, j'appuie sur Button1 et mon texte devrait le montrer dans Label1. Mais mon application ne veut pas fonctionner, car elle provoque une erreur d'opération 'Cross thread operation not valid'. J'ai fait quelques recherches et trouvé l'objet Invoke - comment puis-je l'utiliser dans mon exemple? Pourquoi ai-je besoin d'inclure la logique Invoke?

namespace WindowsApplication1 
{ 
public partial class Form1 : Form 
{ 
    SerialPort sp = new SerialPort(); 

    public Form1() 
    { 
     InitializeComponent(); 
     sp.DataReceived += MyDataReceivedHandler; 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 

    } 

    private void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e) 
    { 
     try 
     { 
      //sp.PortName = "COM3"; 
      //sp.Open(); 
      Label1.Text = sp.ReadLine(); 
     } 
     catch (Exception exception) 
     { 
      RichTextBox1.Text = exception.Message + "\n\n" + exception.Data; 
     } 
     finally 
     { 
      sp.Close(); 
     } 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     try 
     { 
      sp.PortName = "COM3"; 
      sp.Open(); 
      sp.WriteLine(TextBox1.Text); 
     } 
     catch (Exception exception) 
     { 
      RichTextBox1.Text = exception.Message + "\n\n" + exception.Data; 
     } 
     finally 
     { 
      sp.Close(); 
     } 
    } 
} 

}

+0

@ Peter: Le port COM est l'interface série RS232. Malgré l'USB et tout ce qu'il y a encore un certain nombre de dispositifs (par exemple, GPS, Medical) qui utilisent le port série pour la communication PC. – Sesh

+0

@_simon_: Juste curieux: à quoi sert le port COM dans cette application particulière? –

+0

@_simon_: J'ai mis à jour ma réponse –

Répondre

17

Je pense que MyDataReceivedHandler est en cours d'exécution sur un thread différent de l'interface graphique. Pour résoudre ce problème, vous devez appeler les setters Text sur le bon thread. Ceci est un exemple de le faire:

public void SetControlText(Control control, string text) 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new Action<Control,string>(SetControlText), new object[] { control, text }); 
    } 
    else 
    { 
     control.Text = text; 
    } 
} 

private void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e) 
{ 
    try 
    { 
     //sp.PortName = "COM3"; 
     //sp.Open(); 
     SetControlText(Label1, sp.ReadLine()); 
    } 
    catch (Exception exception) 
    { 
     SetControlText(RichTextBox1, exception.Message + "\n\n" + exception.Data); 
    } 
    finally 
    { 
     sp.Close(); 
    } 
} 

Si vous utilisez .NET Framework 2.0, le délégué Action<T1, T2> ci-dessus ne sont pas disponibles, vous devrez définir votre propre un:

private delegate void SetControlTextHandler(Control control, string text); 

public void SetControlText(Control control, string text) 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new SetControlTextHandler(SetControlText), new object[] { control, text }); 
    } 
    else 
    { 
     control.Text = text; 
    } 
} 

Le SetControlText méthode peut être plus courte (et même statique) comme celui-ci (cela fonctionne dans les deux 2.0 et 3.5):

public static void SetControlText(Control control, string text) 
{ 
    ´control.Invoke((MethodInvoker)delegate { control.Text = text; }); 
} 

vous n'avez pas besoin de faire la vérification des InvokeRequired à chaque fois, mais vous devrez envelopper l'appel dans un délégué même si ce n'est pas nécessaire. Je pense que dans une méthode GUI comme celle-ci, toute différence de performance entre les deux est négligeable, donc j'ai tendance à utiliser la forme la plus courte, simplement parce que c'est moins de code à écrire.

+0

Il semble que cela ne fonctionne que dans 3.5. J'utilise Visual Studio 2005, maintenant j'ai installé 3.5 SP1. Où puis-je définir quel framework .NET utiliser Visual Studio 2005? – sventevit

+0

@_simon_: J'ai mis à jour la réponse avec les versions 2.0 compatibles –

+0

Remarque: Si l'opération effectuée dans le délégué est longue, elle peut toujours bloquer l'interface utilisateur car l'invocation provoquera simplement le thread d'interface utilisateur à traiter l'opération.L'utilisation de BeginInvoke sur tous les contrôles qui l'implémentent effectuera l'opération de manière asynchrone, sans blocage. –

0

Vous pouvez également effectuer les opérations suivantes à chaque fois que l'accès à un contrôle d'interface utilisateur à partir d'un fil différent de celui qu'il a été créé le:

(.NET 3.5)

myControl.BeginInvoke(new MethodInvoker(() => myControl.whatever = whatever;)); 

ou (.NET 2.0)

myControl.BeginInvoke(new MethodInvoker(delegate { myControl.whatever = whatever;)); 

modifier> Parfois, en utilisant Invoke pour une longue opération de fonctionnement peut/va encore accrocher l'interface, en utilisant BeginInvoke exécute évidemment cette opération de manière asynchrone, un d l'ui ne pendra pas.

Questions connexes