2010-10-22 7 views
3

Je travaille avec une interface graphique assez complexe et j'essaie de transmettre beaucoup de données de l'interface graphique à un backgroudWorker. Le problème que je rencontre est l'accès à certaines des valeurs de l'interface graphique de l'arrière-plan. Par exemple, si j'essaie d'obtenir ComboBox.Text, je reçois un InvalidOperationException en raison d'un croisement. Cependant, si je dis TextBox.Text, tout semble fonctionner correctement. Certes, je suis assez nouveau à C#, donc je ne comprends pas très bien pourquoi certains d'entre eux sont OK et d'autres échouent.Comment bien transmettre les valeurs de l'interface graphique à l'arrière-plan?

J'ai trouvé plusieurs façons de résoudre mes problèmes, mais je cherche la meilleure pratique de quelqu'un qui a de l'expérience en C#.

Voici quelques façons que je peux penser de fixer ce

  1. créer la classe/struct de toutes les valeurs que vous souhaitez passer au travailleur de fond et de transmettre quand vous appelez RunworkAsync. Je n'ai pas trouvé cela très attrayant car je devais construire une classe/struct pour chaque page sur mon interface graphique pour passer à l'arrière-plan.

  2. Créer un tas de différents travailleurs d'arrière-plan qui avaient une tâche spécifique. J'avais encore quelques problèmes avec les données, mais la quantité de données que je devais passer a été réduite un peu. Cependant, le nombre de DoWork/ProgressChanged/RunworkerCompleted a considérablement augmenté, ce qui était loin d'être idéal.

  3. (cela me conduit à ce que je suis en train de faire)

créer un délégué et méthode pour capturer les informations

private delegate string ReadComboDelegate(ComboBox c); 

private string ReadComboBox(ComboBox c) 
{ 
    if(c.InvokeRequired) 
    { 
     ReadComboDelegate del = new ReadComboDelegate(this.ReadComboBox); 
     return (string) c.Invoke(del,c); 
    } 
    else 
    { 
     return c.Text 
    } 
} 

puis au sein DoWork, faire somthing comme string txt = this.ReadComboBox(this.comboBox1);

Lorsque vous avez une interface graphique simple et que vous n'avez pas à transmettre beaucoup de données, c'est un problème assez simple. Cependant, plus le nombre d'éléments et d'éléments complexes de l'interface graphique est élevé, plus ce problème devient important. Si quelqu'un a des informations qui faciliteraient cela, je l'apprécierais.

Merci

+0

Quelle version de .NET utilisez-vous? –

+2

Je pense que vous êtes sur la bonne voie pour # 1 et # 2. Une unité de travail par thread et une classe personnalisée pour gérer une page sont des principes de conception décents. Est-ce que chaque page ne fait qu'une seule opération? Pouvez-vous éventuellement combiner certains contrôles? Pouvez-vous charger des informations dans les classes de paramètres de l'arrière-plan du travail au fur et à mesure qu'elles sont entrées? Si votre interface graphique est si complexe que vous ne pouvez même pas rassembler toutes les données dont vous avez besoin, il est peut-être nécessaire de revoir la conception graphique. –

+0

Je pense que Ryan a raison. Ce code semble être une grosse boule de boue. – YWE

Répondre

2

La question de la Croix-Threading vous exécutez en est en raison de l'exigence que seul le thread d'interface utilisateur est autorisé aux contrôles de l'interface utilisateur « touch ».

Je pense que la méthode la plus acceptée de transmettre des données à un travailleur de fond est votre solution # 1 - créer une structure simple qui contient toutes les données nécessaires pour effectuer le traitement.

Ceci est beaucoup plus simple que de créer des méthodes ReadXXX pour chaque commande dans l'interface utilisateur, et il définit ce que le processus d'arrière-plan doit effectuer sa tâche ...

1

Il est plutôt par hasard que TextBox ne cause pas exception. Sa propriété Text est mise en cache dans une chaîne. Ce n'est pas le cas pour ComboBox.Text et la grande majorité des autres propriétés de contrôle, il demande au contrôle Windows natif et à ce stade Windows Forms découvre que vous essayez d'utiliser un contrôle d'un thread autre que le thread UI. Ne peut faire.

Vous devez absolument penser à un moyen de restructurer ce code. Ce n'est pas seulement illégal, il est incroyablement coûteux et fondamentalement dangereux parce que l'interface utilisateur pourrait être mise à jour pendant que votre travailleur fonctionne. Collectez les informations des contrôles dont vous avez besoin dans une petite classe d'aide, transmettez-la en tant qu'argument à la surcharge RunWorkerAsync (objet).Et récupérez-le dans DoWork à partir de e.Argument.

0

Je voudrais absolument éviter # 3. Malgré la ferveur sur l'utilisation Control.Invoke pour coordonner les threads de travail et d'interface utilisateur, il est souvent surutilisé et habituellement une stratégie sous-optimale au mieux. Je préfère de loin # 1 et # 2 à # 3. Voici les raisons pour lesquelles j'ai tendance à éviter # 3. Il couple étroitement l'interface utilisateur et les threads de travail.

  • Le thread de travail peut dicter le travail effectué par le thread de l'interface utilisateur.
  • Le thread de travail doit attendre que le thread de l'interface utilisateur réponde avant de continuer.
  • C'est une opération coûteuse. Je sais que cela peut nécessiter un effort supplémentaire de votre part pour obtenir le # 1 ou le # 2, mais le résultat final sera meilleur à long terme. En corollaire à ma réponse, la méthode Control.Invoke a tendance à être surutilisée lorsque les données doivent également suivre la direction opposée (du thread de travail au thread de l'interface utilisateur comme dans le cas de l'envoi d'informations de progression à l'interface utilisateur). Malheureusement, c'est la méthode que BackgroundWorker utilise en interne avec sa méthode ReportProgress. Il est généralement mieux d'avoir le sondage thread d'interface utilisateur une structure de données partagées pour ces informations pour certaines des mêmes raisons que ci-dessus plus:

    • Le thread d'interface utilisateur arrive à dicter quand et à quelle fréquence la mise à jour devrait avoir lieu .
    • Il met la responsabilité de mettre à jour le thread UI sur le thread de l'interface utilisateur où il devrait appartenir quand même.
    • Il n'y a pas de risque de saturation de la pompe de messages de l'interface utilisateur comme cela serait le cas avec les techniques de marshaling lancées par le thread de travail.

    Cependant, avec cela dit, je suis pas ce qui suggère que vous abandonnez entièrement BackgroundWorker. Gardez juste certains de ces points en tête.