J'ai un scénario. (Windows Forms, C #, .NET)
- Il existe un formulaire principal qui héberge un certain contrôle utilisateur.
- Le contrôle utilisateur effectue une opération de données volumineuse, de sorte que si j'appelle directement la
UserControl_Load
méthode, l'interface utilisateur ne répond plus pendant la durée d'exécution de la méthode de chargement. - Pour surmonter cela, je charge des données sur différents threads (en essayant de modifier le code existant aussi peu que possible)
- J'ai utilisé un thread de travail en arrière-plan qui chargera les données et, une fois terminé, notifiera à l'application qu'elle a fait son travail.
- Maintenant est venu un vrai problème. Toute l'interface utilisateur (formulaire principal et ses contrôles utilisateur enfants) a été créée sur le thread principal principal. Dans la méthode LOAD de usercontrol, je récupère des données en fonction des valeurs de certains contrôles (comme la zone de texte) sur userControl.
Le pseudocode ressemblerait à ceci:
CODE 1
UserContrl1_LoadDataMethod()
{
if (textbox1.text == "MyName") // This gives exception
{
//Load data corresponding to "MyName".
//Populate a globale variable List<string> which will be binded to grid at some later stage.
}
}
L'exception qu'il a donnée était
Opération inter-thread non valide: contrôle accessible à partir d'un thread autre que le thread sur lequel il a été créé.
Pour en savoir plus, j'ai fait une recherche sur Google et une suggestion est venue comme utiliser le code suivant
CODE 2
UserContrl1_LoadDataMethod()
{
if (InvokeRequired) // Line #1
{
this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));
return;
}
if (textbox1.text == "MyName") // Now it wont give an exception
{
//Load data correspondin to "MyName"
//Populate a globale variable List<string> which will be binded to grid at some later stage
}
}
MAIS MAIS MAIS ... il semble que je suis de retour à la case départ. L'Application ne répond plus. Cela semble être dû à l'exécution de la ligne # 1 si condition. La tâche de chargement est à nouveau effectuée par le thread parent et non par le troisième que j'ai généré.
Je ne sais pas si j'ai perçu ce bien ou ce mal. Je suis nouveau dans le filetage.
Comment puis-je résoudre ce problème et quel est également l'effet de l'exécution de la ligne # 1 en cas de blocage?
La situation est la suivante : je veux charger des données dans une variable globale basée sur la valeur d'un contrôle. Je ne veux pas modifier la valeur d'un contrôle à partir du thread enfant. Je ne vais jamais le faire à partir d'un thread enfant.
Donc, uniquement accéder à la valeur afin que les données correspondantes puissent être extraites de la base de données.
la source
Réponses:
Selon le commentaire de mise à jour de Prerak K (depuis supprimé):
La solution que vous souhaitez alors devrait ressembler à:
Effectuez votre traitement sérieux dans le thread séparé avant d'essayer de revenir au thread du contrôle. Par exemple:
la source
Modèle de thread dans l'interface utilisateur
Veuillez lire le modèle de thread dans les applications d'interface utilisateur (l' ancien lien VB est ici ) afin de comprendre les concepts de base. Le lien accède à la page qui décrit le modèle de thread WPF. Cependant, Windows Forms utilise la même idée.
Le fil d'interface utilisateur
Méthodes BeginInvoke et Invoke
Invoquer
BeginInvoke
Solution de code
Lisez les réponses à la question Comment mettre à jour l'interface graphique à partir d'un autre thread en C #? . Pour C # 5.0 et .NET 4.5, la solution recommandée est ici .
la source
Vous souhaitez uniquement utiliser
Invoke
ouBeginInvoke
pour le strict minimum de travail requis pour modifier l'interface utilisateur. Votre méthode "lourde" devrait s'exécuter sur un autre thread (par exemple viaBackgroundWorker
) mais en utilisantControl.Invoke
/Control.BeginInvoke
juste pour mettre à jour l'interface utilisateur. De cette façon, votre thread d'interface utilisateur sera libre de gérer les événements d'interface utilisateur, etc.Voir mon article de discussion pour un exemple WinForms - bien que l'article ait été écrit avant d'
BackgroundWorker
arriver sur la scène, et je crains de ne pas l'avoir mis à jour à cet égard.BackgroundWorker
simplifie simplement un peu le rappel.la source
Je sais que c'est trop tard maintenant. Cependant, même aujourd'hui, si vous rencontrez des problèmes pour accéder aux contrôles croisés? Ceci est la réponse la plus courte jusqu'à la date: P
C'est ainsi que j'accède à n'importe quel contrôle de formulaire à partir d'un fil.
la source
Invoke or BeginInvoke cannot be called on a control until the window handle has been created
. Je l'ai résolu iciJ'ai eu ce problème avec le
FileSystemWatcher
et j'ai constaté que le code suivant a résolu le problème:fsw.SynchronizingObject = this
Le contrôle utilise ensuite l'objet de formulaire actuel pour gérer les événements et sera donc sur le même thread.
la source
.SynchronizingObject = Me
Je trouve que le code check-and-invoke qui doit être jonché dans toutes les méthodes liées aux formulaires est beaucoup trop verbeux et inutile. Voici une méthode d'extension simple qui vous permet de vous en débarrasser complètement:
Et puis vous pouvez simplement faire ceci:
Plus besoin de déconner - simple.
la source
t
dans ce cas seratextbox1
- il est passé en argumentLes contrôles dans .NET ne sont généralement pas thread-safe. Cela signifie que vous ne devez pas accéder à un contrôle à partir d'un thread autre que celui où il se trouve. Pour contourner ce problème, vous devez appeler le contrôle, ce que tente votre deuxième échantillon.
Cependant, dans votre cas, tout ce que vous avez fait est de renvoyer la méthode de longue durée au thread principal. Bien sûr, ce n'est pas vraiment ce que vous voulez faire. Vous devez repenser cela un peu pour que tout ce que vous faites sur le thread principal soit de définir une propriété rapide ici et là.
la source
La solution la plus propre (et appropriée) pour les problèmes de cross-threading de l'interface utilisateur consiste à utiliser SynchronizationContext, voir Synchronisation des appels à l'interface utilisateur dans un article d' application multi-thread , il l'explique très bien.
la source
Un nouveau look avec Async / Await et les rappels. Vous n'avez besoin que d'une seule ligne de code si vous conservez la méthode d'extension dans votre projet.
Vous pouvez ajouter d'autres choses à la méthode Extension, comme l'encapsuler dans une instruction Try / Catch, permettant à l'appelant de lui dire quel type renvoyer une fois terminé, un rappel d'exception à l'appelant:
Ajout de Try Catch, de la journalisation des exceptions automatiques et de CallBack
la source
Ce n'est pas la méthode recommandée pour résoudre cette erreur mais vous pouvez la supprimer rapidement, elle fera l'affaire. Je préfère cela pour les prototypes ou les démos. ajouter
dans
Form1()
constructeur.la source
Suivez la manière la plus simple (à mon avis) de modifier des objets d'un autre thread:
la source
Vous devez regarder l'exemple Backgroundworker:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx Surtout comment il interagit avec la couche d'interface utilisateur. D'après votre publication, cela semble répondre à vos problèmes.
la source
J'ai trouvé un besoin pour cela lors de la programmation d'un contrôleur d'application monotouch iOS-Phone dans un projet prototype Winforms de Visual Studio en dehors de Xamarin Stuidio. Préférant programmer autant que possible dans VS sur xamarin studio, je souhaitais que le contrôleur soit complètement découplé de la structure du téléphone. De cette façon, l'implémentation pour d'autres cadres comme Android et Windows Phone serait beaucoup plus facile pour les utilisations futures.
Je voulais une solution où l'interface graphique pourrait répondre aux événements sans avoir à gérer le code de commutation croisé derrière chaque clic de bouton. Fondamentalement, laissez le contrôleur de classe gérer cela pour garder le code client simple. Vous pouvez éventuellement avoir de nombreux événements sur l'interface graphique où comme si vous pouviez le gérer en un seul endroit dans la classe serait plus propre. Je ne suis pas un expert multi-theading, faites-moi savoir si cela est défectueux.
Le formulaire GUI ignore que le contrôleur exécute des tâches asynchrones.
la source
Voici une alternative si l'objet avec lequel vous travaillez n'a pas
Ceci est utile si vous travaillez avec le formulaire principal dans une classe autre que le formulaire principal avec un objet qui se trouve dans le formulaire principal, mais qui n'a pas InvokeRequired
Cela fonctionne de la même manière que ci-dessus, mais c'est une approche différente si vous n'avez pas d'objet avec invokerequired, mais avez accès au MainForm
la source
Dans le même sens que les réponses précédentes, mais un ajout très court qui permet d'utiliser toutes les propriétés de contrôle sans avoir d'exception d'invocation croisée.
Méthode d'assistance
Exemple d'utilisation
la source
la source
Par exemple, pour obtenir le texte d'un contrôle du thread d'interface utilisateur:
la source
Même question: comment-mettre-à-jour-le-gui-d'un-autre-thread-en-c
Deux façons:
Renvoyez la valeur dans e.result et utilisez-la pour définir la valeur de votre zone de texte dans l'événement backgroundWorker_RunWorkerCompleted
Déclarez une variable pour conserver ce type de valeurs dans une classe distincte (qui fonctionnera comme détenteur de données). Créez une instance statique de cette classe et vous pouvez y accéder via n'importe quel thread.
Exemple:
la source
Utilisez simplement ceci:
la source
Action y; // déclaré à l'intérieur de la classe
label1.Invoke (y = () => label1.Text = "texte");
la source
Un moyen simple et réutilisable de contourner ce problème.
Méthode d'extension
Exemple d'utilisation
la source
Il existe deux options pour les opérations entre fils.
et le second est d'utiliser
Control.InvokeRequired n'est utile que lorsque les contrôles de travail hérités de la classe Control alors que SynchronizationContext peut être utilisé n'importe où. Certaines informations utiles sont les liens suivants
Interface utilisateur de mise à jour croisée | .Net
Interface de mise à jour croisée des threads à l'aide de SynchronizationContext | .Net
la source