Quel est le problème avec mon appel inter-thread dans Windows Forms?
-
27-09-2019 - |
Question
Je rencontre un problème avec une application Windows Forms.
Un formulaire doit être affiché à partir d'un autre thread. Ainsi, dans la classe de formulaire, je le code suivant:
private delegate void DisplayDialogCallback();
public void DisplayDialog()
{
if (this.InvokeRequired)
{
this.Invoke(new DisplayDialogCallback(DisplayDialog));
}
else
{
this.ShowDialog();
}
}
Maintenant, chaque fois que je lance cela, un InvalidOperationException
est jeté sur la ligne this.ShowDialog();
:
"opération de la Croix-fil non valide. Control 'SampleForm' accessible à partir d'un autre thread que le thread il a été créé"
Le mal de Qu'est-ce avec ce morceau de code? Est-il pas un moyen valable de faire des appels inter-fil? Y at-il quelque chose de spécial avec ShowDialog()
?
La solution
Essayez celui-ci:
private delegate void DisplayDialogCallback();
public void DisplayDialog()
{
if (this.InvokeRequired)
{
this.Invoke(new DisplayDialogCallback(DisplayDialog));
}
else
{
if (this.Handle != (IntPtr)0) // you can also use: this.IsHandleCreated
{
this.ShowDialog();
if (this.CanFocus)
{
this.Focus();
}
}
else
{
// Handle the error
}
}
}
S'il vous plaît noter que le rendement des InvokeRequired
true si la poignée du contrôle était créé sur un fil différent de celui du thread appelant (indiquant que vous doit faire des appels au contrôle par une méthode d'appel); sinon, false.
et, par conséquent, si le contrôle n'a pas été créée, la valeur de retour sera false
!
Autres conseils
Vous êtes en train d'exécuter ce code probablement avant que le formulaire a été démontré.
Par conséquent, InvokeRequired
revient false
.
Je crois que ce qui se passe ici est que ce code est en cours d'exécution avant que le Form
est jamais montré.
Quand un Form
est créé en .Net il ne gagne pas immédiatement une affinité pour un fil particulier. Que lorsque certaines opérations sont effectuées comme montrant ou saisir la poignée-t-il une affinité gain. Avant cela arrive, il est difficile pour InvokeRequired
de fonctionner correctement.
Dans ce cas particulier aucune affinité est établie et aucun contrôle parent existe donc retourne InvokeRequired
faux car il ne peut pas déterminer le fil d'origine.
La façon de résoudre ce problème est d'établir une affinité pour votre contrôle quand il est créé sur le thread d'interface utilisateur. La meilleure façon de le faire est juste de demander le contrôle de sa propriété de poignée.
var notUsed = control.Handle;
vous obtenez probablement ce code avant que le formulaire a été montré et donc la poignée de fenêtre n'a pas été créée.
Vous pouvez ajouter ce code avant votre code et tout devrait être bon:
if (! this.IsHandleCreated)
this.CreateHandle();
Modifier Il y a un autre problème avec votre code. Une fois que le formulaire est affiché, vous ne pouvez pas appeler ShowDialog () à nouveau. Vous obtiendrez une exception d'opération non valide. Vous pouvez modifier cette méthode comme d'autres l'ont proposé.
Vous pourriez être mieux servi d'appeler le ShowDialog () directement à partir de la classe d'appel et une autre méthode pour BringToFront () ou quelque chose comme ça ...
Vous pouvez toujours essayer de tester contre un autre contrôle.
Par exemple, vous pouvez accéder à des collections de Application.Forms
public Control GetControlToInvokeAgainst()
{
if(Application.Forms.Count > 0)
{
return Application.Forms[0];
}
return null;
}
Ensuite, dans votre méthode DisplayDialog (), appelez le GetControlToInvokeAgainst () et test nul avant d'essayer d'effectuer un appel InvokeRequired.
Très probablement la poignée du contrôle est pas encore créé, dans lequel le rendement de cas Control.InvokeRequired
false
.
Vérifiez la propriété Control.IsHandleCreated
pour voir si tel est le cas.
Je pense aussi SLaks est correct. De msdn ( http://msdn.microsoft .com / fr-fr / bibliothèque / system.windows.forms.control.invokerequired.aspx ):
Si aucune poignée appropriée peut être trouvée, la méthode renvoie InvokeRequired faux.
S'il est possible dans votre cas, je voudrais essayer de combiner la création et montrant le contrôle dans une seule méthode, i.e.:.
public DisplayDialog static Show()
{
var result = new DisplayDialog; //possibly cache instance of the dialog if needed, but this could be tricky
result.ShowDialog();
return result;
}
vous pouvez appeler Afficher à partir d'un autre thread