2010-10-25 14 views
7

Comment est-ce possible? J'ai le contrôle Windows Form, dérivé de System.Windows.Forms.Form avec le contrôle WebBrowser contenu dans ce formulaire. L'instance d'objet Webbrowser est créée dans le constructeur de formulaire (dans la méthode InitializeComponent()). Puis dans le fil d'arrière-plan je manipule avec le contenu de WebBrowser, et j'ai trouvé que dans certains cas, Form.InvokeRequired == false, alors que WebBrowser.InvokeRequired == true. Comment ça peut être?InvokeRequired de Form == false et InvokeRequired du contrôle contenu == true

+0

Est-ce que cela se produit au démarrage ou à la fermeture du formulaire ou tout le temps? –

+0

Il se produit lorsque le formulaire a déjà été créé, mais pas affiché (formulaire entier, pas seulement le navigateur). Je ne montre pas la forme juste après la création. –

Répondre

9

Form.InvokeRequired renvoie false avant que le formulaire ne s'affiche.

J'ai fait un test simple:

Form2 f2 = new Form2(); 
Thread t = new Thread(new ThreadStart(() => PrintInvokeRequired(f2))); 
t.Start(); 
t.Join(); 

f2.Show(); 

t = new Thread(new ThreadStart(() => PrintInvokeRequired(f2))); 
t.Start(); 
t.Join(); 

avec l'aide

private void PrintInvokeRequired(Form form) 
{ 
    Console.WriteLine("IsHandleCreated: " + form.IsHandleCreated + ", InvokeRequired: " + form.InvokeRequired); 
} 

la sortie est

IsHandleCreated: Faux, InvokeRequired: Faux
IsHandleCreated: True, InvokeRequired : Vrai

Notez également que cela est un peu documenté sur MSDN:

Si la poignée de contrôle ne pas encore existe, InvokeRequired recherche la chaîne mère de contrôle jusqu'à ce qu'il trouve un contrôle ou une forme qui a un Poignée de fenêtre. Si aucun handle approprié ne peut être trouvé, la méthode InvokeRequired renvoie false.

Cela signifie que InvokeRequired peut return false si Invoke n'est pas nécessaire (l'appel se produit sur le même fil), ou si le contrôle a été créé sur un thread différent, mais poignée de contrôle n'a pas encore été créé .

Dans le cas où n'a pas encore été créé la poignée du contrôle, vous devez pas simplement appeler propriétés, méthodes, événements ou sur le contrôle. Cela pourrait créer la poignée du contrôle créé sur le thread d'arrière-plan, isoler le contrôle sur un thread sans une pompe de message et rendre l'application instable.

Vous pouvez protéger contre ce cas par vérifier également la valeur de IsHandleCreated lorsque InvokeRequired fausses déclarations sur un fil d'arrière-plan. Si la poignée de contrôle n'a pas encore été créée, vous devez attendre qu'elle ait créé avant d'appeler Invoke ou BeginInvoke. Typiquement, cela se produit que si un fil de fond est créée dans le constructeur de la forme primaire d'application (comme dans Application.Run (nouveau MainForm()), avant que le formulaire a été montré ou Application.Run a été appelé.

Votre solution doit également vérifier IsHandleCreated.

Edit:
Le Handle peuvent être créées à tout moment interne dans le contrôle WebBrowser ou externe. Cela ne crée pas automatiquement le handle du formulaire parent.

J'ai créé un exemple:

public Form2() 
{ 
    InitializeComponent(); 

    Button button1 = new Button(); 
    this.Controls.Add(button1); 

    Console.WriteLine("button1: " + button1.IsHandleCreated + " this: " + this.IsHandleCreated); 
    var tmp = button1.Handle; // Forces the Handle to be created. 
    Console.WriteLine("button1: " + button1.IsHandleCreated + " this: " + this.IsHandleCreated); 
} 

avec la sortie:

button1: Faux ceci: Faux
button1: Vrai ceci: Faux

+0

Mais je vérifie la propriété InvokeRequired de contrôle contenu (WebBrowser), et il est True alors que form.InvokeRequired est False. Selon la logique, InvokeRequired de tous les contrôles enfants de la hiérarchie doit être identique à InvokeRequired du contrôle parent si les contrôles enfants ont été créés dans le même thread que le contrôle parent. Et dans ma situation, le contrôle de navigateur web est créé dans le constructeur de la forme, c'est-à-dire qu'ils ont été créés en un seul thread. Peut-être avons-nous un comportement spécifique du contrôle WebBrowser? –

+1

@Dmitry: Non. Le handle d'un contrôle peut être créé indépendamment du handle du formulaire parent. J'ai mis à jour ma réponse avec un exemple. –

+0

Oh, merci! Très intéressant, et cela clarifie tout! –

0

J'ai enquêté sur ce même comportement étrange. Je dois faire fonctionner certaines commandes à partir de différents threads (par exemple, afficher des informations sur un périphérique connecté à l'hôte ou déclencher des actions en fonction des différents états des périphériques).

Ce lien m'a donné un bon conseil: http://csharpfeeds.com/post/2898/Control.Trifecta_InvokeRequired_IsHandleCreated_and_IsDisposed.aspx

Je ne sais toujours pas comment les gens MS destinés à faire usage de leurs propres trucs (et ne suis pas d'accord dans de nombreux aspects), mais, dans un application j'ai dû faire la solution de contournement sale et sale suivante:

  • Créer le contrôle/formulaire dans le thread principal (assurez-vous que c'est le thread principal).
  • Dans la même procédure, vérifiez la poignée de contrôle. Ce simple test va le forcer à être créé et dans le bon fil!

Comment moche, n'est pas? J'aimerais savoir si quelqu'un d'autre a une meilleure façon de le faire.

_my_control = new ControlClass(); 
_my_control.Owner = this; 

IntPtr hnd; 

// Force Handle creation by reading it. 
if (!_my_control.IsHandleCreated || _my_control.Handle == IntPtr.Zero) 
    hnd = _my_control.Handle; 

(Désolé pour l'affichage dans ce billet un peu vieux, mais je pensais que ce pourrait être utile à quelqu'un).

+0

Notez que dans votre exemple, le handle sera effectivement créé par le contrôle _my_control.Handle == IntPtr.Zero. Une méthode plus courte pourrait être _myControl = new ControlClass {Owner = this}; var temp = _myControl.Handle; –