Comment puis-je utiliser le thread d'interface utilisateur pour mettre à jour l'interface utilisateur lors du traitement par lots dans une application WinForm?

StackOverflow https://stackoverflow.com/questions/106036

  •  01-07-2019
  •  | 
  •  

Question

J'ai une application WinForms écrite en C # avec .NET 3.5. Il exécute un long processus de traitement par lots. Je veux que l'application mette à jour le statut de ce que fait le traitement par lots. Quel est le meilleur moyen de mettre à jour l'interface utilisateur?

Était-ce utile?

La solution

BackgroundWorker ressemble à l'objet que vous souhaitez.

Autres conseils

La méthode rapide et incorrecte consiste à utiliser Application.DoEvents () . Toutefois, cela peut entraîner des problèmes de gestion des événements de commande. Donc, ce n'est pas recommandé

Le problème n'est probablement pas que vous devez céder le thread ui mais que vous effectuez le traitement sur le thread ui en le bloquant pour le traitement des messages. Vous pouvez utiliser le composant backgroundworker pour effectuer le traitement par lots sur un autre thread sans bloquer le thread de l'interface utilisateur.

Exécutez le long processus sur un fil d’arrière-plan. La classe de travailleurs d'arrière-plan est un moyen simple de procéder. Elle fournit un support simple pour l'envoi de mises à jour de progression et d'événements d'achèvement pour lesquels les gestionnaires d'événements sont appelés sur le bon thread pour vous. Le code reste ainsi propre et concis.

Pour afficher les mises à jour, les barres de progression ou le texte de la barre d'état sont deux des approches les plus courantes.

La chose essentielle à retenir est que si vous faites des choses sur un fil d’arrière-plan, vous devez basculer sur le fil d’interface utilisateur afin de mettre à jour les contrôles Windows, etc.

Pour illustrer ce que les gens disent de DoEvents, voici une description de ce qui peut arriver.

Supposons que vous ayez un formulaire avec des données et que votre événement de longue durée l'enregistre dans la base de données ou génère un rapport basé sur celui-ci. Vous commencez à enregistrer ou à générer le rapport, puis vous appelez périodiquement DoEvents pour que l'écran continue à peindre.

Malheureusement, l'écran ne se limite pas à la peinture, il réagit également aux actions de l'utilisateur. En effet, DoEvents arrête ce que vous faites maintenant pour traiter tous les messages Windows en attente de traitement par votre application Winforms. Ces messages incluent des demandes de redessinage, ainsi que tout utilisateur tapant, cliquant, etc.

Ainsi, par exemple, pendant que vous enregistrez les données, l'utilisateur peut effectuer des actions telles que faire afficher par l'application une boîte de dialogue modale totalement indépendante de la tâche à exécution longue (par exemple, Help- > About). Vous réagissez maintenant aux nouvelles actions des utilisateurs à l'intérieur de la tâche d'exécution longue déjà en cours d'exécution. DoEvents sera renvoyé lorsque tous les événements en attente au moment de votre appel seront terminés, puis votre tâche à exécution longue se poursuivra.

Que se passe-t-il si l'utilisateur ne ferme pas la boîte de dialogue modale? Votre longue tâche attend toujours que cette boîte de dialogue soit fermée. Si vous vous engagez dans une base de données et que vous effectuez une transaction, vous maintenez maintenant une transaction ouverte pendant que l'utilisateur boit un café. Soit la transaction expire et vous perdez votre travail de persistance, soit la transaction n’expédie pas et vous risquez de bloquer les autres utilisateurs de la base de données.

Ce qui se passe ici, c'est que Application.DoEvents rend votre code réentrant. Voir la définition de wikipedia ici . Notez quelques points en haut de l’article: pour que le code soit réentrant, c’est:

  • Ne doit contenir aucune donnée statique constante (ni globale).
  • Ne doit fonctionner que sur les données fournies par l'appelant.
  • Ne devez pas vous fier aux verrous des ressources singleton.
  • Ne doit pas appeler des programmes ou des routines informatiques non réentrants.

Il est très peu probable qu'une longue application de code dans une application WinForms ne fonctionne que sur les données transmises à la méthode par l'appelant, ne contient pas de données statiques, ne contient aucun verrou et n'appelle que d'autres méthodes réentrantes.

Comme beaucoup de gens le disent ici, DoEvents peut conduire à des scénarios très étranges en code. Les bogues auxquels il peut conduire peuvent être très difficiles à diagnostiquer, et votre utilisateur n’est pas susceptible de vous dire "Cela est peut-être arrivé parce que j’ai cliqué sur ce bouton sans lien entre-deux en l’attendant pour le sauvegarder".

Utilisez le composant backgroundworker pour exécuter votre traitement par lots dans un thread séparé. Cela n'aura alors aucun impact sur le thread d'interface utilisateur.

Je tiens à répéter ce que mes précédents commentaires ont noté: évitez autant que possible DoEvents (), car il s'agit presque toujours d'une forme de "hack". et provoque des cauchemars d'entretien.

Si vous choisissez la route BackgroundWorker (ce que je suggère), vous devrez gérer les appels inter-threads à l'interface utilisateur si vous souhaitez appeler des méthodes ou des propriétés de Controls, car elles sont affinières et doivent être appelé uniquement à partir du fil sur lequel ils ont été créés. Utilisez Control.Invoke () et / ou Control.BeginInvoke () selon le cas.

Si vous exécutez un thread en arrière-plan / travailleur, vous pouvez appeler Control.Invoke sur l'un de vos contrôles d'interface utilisateur pour exécuter un délégué dans le thread d'interface utilisateur.

Control.Invoke est synchrone (attend le retour du délégué). Si vous ne voulez pas attendre, utilisez .BeginInvoke () pour ne mettre que la commande en file d'attente.

La valeur de retour de .BeginInvoke () vous permet de vérifier si la méthode est terminée ou d'attendre qu'elle soit terminée.

Utilisez Backgroundworker , et si vous essayez également de mettre à jour le thread d'interface graphique en gérant l'événement ProgressChanged (comme pour un ProgressBar ). Assurez-vous également de définir WorkerReportsProgress = true , sinon le thread qui fait rapport de la progression meurt la première fois qu'il tente d'appeler ReportProgress ...

une exception est levée, mais vous risquez de ne pas la voir sauf si vous avez activé la case à cocher "une fois levé", et le résultat ne fera que montrer que le fil est sorti.

Application.DoEvents () ou éventuellement exécuter le lot sur un thread séparé?

DoEvents () était ce que je recherchais, mais j'ai également voté pour les réponses des personnes qui ont travaillé en arrière-plan, car cela me semble une bonne solution que je vais étudier de plus près.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top