Question

J'ai une application de formulaire Windows qui utilise une classe partagée pour héberger tous les objets communs de l'application. La classe de paramètres possède une collection d'objets qui effectuent des tâches de manière périodique. Ensuite, quelque chose d'intéressant: ils doivent alerter le formulaire principal et le faire mettre à jour.

Je le fais actuellement par le biais d'événements sur les objets, et lorsque chaque objet est créé, j'ajoute un gestionnaire d'événements pour mapper l'événement au formulaire. Cependant, certains problèmes me donnent à penser que ces demandes ne se retrouvent pas toujours dans la copie principale de mon formulaire. Par exemple, mon formulaire comporte une icône de notification dans la barre des tâches, mais lorsque le formulaire enregistre et affiche un événement et tente d'afficher une bulle, aucune bulle ne s'affiche. Cependant, si je modifie ce code pour rendre l'icône visible (bien qu'il le soit déjà), puis affiche la bulle, une seconde icône apparaît et affiche la bulle correctement.

Quelqu'un a-t-il déjà rencontré ce problème? Existe-t-il un moyen de forcer la capture de tous mes événements par la seule instance du formulaire ou existe-t-il un moyen complètement différent de gérer cela? Je peux poster des exemples de code si nécessaire, mais je pense que c'est un problème de threading commun.

Plus d'informations: j'utilise actuellement Me.InvokeRequired dans le gestionnaire d'événements de mon formulaire et renvoie toujours FALSE dans ce cas. De plus, la deuxième icône de la barre de tâches créée lorsque je le rends visible à partir de ce formulaire ne comporte pas de menu contextuel, alors que l'option "réel" icône ne - cela signifie-t-il que quiconque?

Je vais me tirer les cheveux! Cela ne peut pas être si difficile!

SOLUTION : Merci à nobugz pour cet indice, qui m'a conduit au code que j'utilise actuellement (qui fonctionne à merveille, même si je ne peux m'empêcher de penser qu'il existe un meilleur moyen de le faire. ) J'ai ajouté une variable booléenne privée au formulaire appelé "IsPrimary" et ajouté le code suivant au constructeur de formulaire:

    Public Sub New()
        If My.Application.OpenForms(0).Equals(Me) Then
            Me.IsFirstForm = True
        End If
    End Sub

Une fois que cette variable est définie et que le constructeur a terminé, il se dirige directement vers le gestionnaire d'événements, et je le traite de cette façon (CAVEAT: Le formulaire que je recherche est le formulaire principal de l'application, My.Application. .OpenForms (0) obtient ce dont j'ai besoin. Si je cherchais la première instance d'un formulaire autre que de démarrage, je devrais parcourir jusqu'à ce que je le trouve):

    Public Sub EventHandler()
        If Not IsFirstForm Then
            Dim f As Form1 = My.Application.OpenForms(0)
            f.EventHandler()
            Me.Close()
        ElseIf InvokeRequired Then
            Me.Invoke(New HandlerDelegate(AddressOf EventHandler))
        Else
            ' Do your event handling code '
        End If
    End Sub

Tout d'abord, il vérifie s'il fonctionne sur le bon formulaire. Si ce n'est pas le cas, appelez le bon formulaire. Ensuite, il vérifie si le thread est correct et appelle le thread d'interface utilisateur si ce n'est pas le cas. Ensuite, il exécute le code de l'événement. Je n'aime pas le fait qu'il y ait potentiellement trois appels, mais je ne vois pas d'autre moyen de le faire. Cela semble bien fonctionner, même si c'est un peu lourd. Si quelqu'un a une meilleure façon de le faire, j'aimerais l'entendre!

Encore une fois, merci pour toute l'aide - cela allait me rendre fou!

Était-ce utile?

La solution

Je pense que c'est aussi un problème de threading. Utilisez-vous Control.Invoke () dans votre gestionnaire d’événements? .NET détecte généralement les violations lorsque vous déboguez l'application, mais dans certains cas, cela ne peut pas. NotifyIcon en fait partie, il n’existe aucun gestionnaire de fenêtre pour vérifier l’affinité des threads.

Modifier après la modification de la question OP:

Un piège VB.NET classique consiste à référencer une instance de formulaire par son nom de type. Comme Form1.NotifyIcon1.Quelque chose. Cela ne fonctionne pas comme prévu lorsque vous utilisez le threading. Cela créera une instance nouvelle de la classe Form1, sans utiliser l'instance existante. Cette instance n'est pas visible (Show () n'a jamais été appelée) et est autrement morte car elle est exécutée sur un thread qui ne pompe pas de boucle de message. Voir une deuxième icône apparaître est un cadeau mort. Il en va de même pour InvokeRequired = False lorsque vous savez que vous l’utilisez depuis un thread.

Vous devez utiliser une référence à l'instance de formulaire existante. Si cela est difficile à trouver (vous transmettez généralement "Moi" en tant qu'argument au constructeur de la classe), vous pouvez utiliser Application.OpenForms:

  Dim main As Form1 = CType(Application.OpenForms(0), Form1)
  if (main.InvokeRequired)
    ' etc...

Autres conseils

Utilisez Control.InvokeRequired pour déterminer si vous êtes sur le bon fil, puis utilisez Control.Invoke si vous ne l'êtes pas.

Vous devriez consulter la documentation de la méthode Invoke sur le formulaire. Cela vous permettra de faire en sorte que le code qui met à jour le formulaire soit exécuté sur le thread propriétaire du formulaire (ce qu'il doit faire, les formulaires Windows ne sont pas thread-safe). Quelque chose comme Sous-délégué délégué privé UpdateStatusDelegate (ByVal newStatus en tant que chaîne)

Public sub UpdateStatus (ByVal newStatus en tant que chaîne) Si Me.InvokeRequired Alors Dim d As New UpdateStatusDelegate (AddressOf UpdateStatus) Me.Invoke (d, nouvel objet () {newStatus}) Autre 'Mettre à jour le statut du formulaire Fin si

Si vous fournissez un exemple de code, je serais heureux de fournir un exemple plus adapté.

Modifiez après que OP ait indiqué qu'il utilise InvokeRequired.

Avant d'appeler InvokeRequired, vérifiez que le descripteur de formulaire a été créé. Vous y trouverez une propriété HandleCreated. InvokeRequired renvoie toujours la valeur false si le contrôle n'a pas actuellement de descripteur. Cela signifie alors que le code n'est pas thread-safe même si vous avez fait ce qu'il faut pour le faire. Mettez à jour votre question lorsque vous le saurez. Quelques exemples de code seraient également utiles.

en c #, cela ressemble à ceci:

private EventHandler StatusHandler = new EventHandler(eventHandlerCode)
void eventHandlerCode(object sender, EventArgs e)
    {
        if (this.InvokeRequired)
        {
            this.Invoke(StatusHandler, sender, e);
        }
        else
        {
          //do work
        }
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top