2017-09-02 4 views
0

Je travaille actuellement sur un projet avec deux formulaires et une icône de plateau. Cependant, j'ai de la difficulté à afficher le deuxième formulaire, Form2, qui gèle tout simplement environ 30 secondes après avoir été affiché d'une certaine manière.C# - Congeler le formulaire après environ 30 secondes - Multithreading

La première forme (Form1) est toujours affichée en arrière-plan, avec seulement deux boutons rouges fins couvrant environ 7 pixels de chaque côté du moniteur de l'utilisateur et l'espace entre les deux est transparent. Les boutons sont destinés à clignoter quand une alarme se déclenche et ils le font avec succès en utilisant le code ci-dessous.

Une fois cette tâche exécutée, un nouveau Form2 est créé. Form2 affiche des informations sur l'alarme qui se déclenche et permet l'acquittement, ce qui empêche également les barres rouges de clignoter. Form2 est créé juste après l'exécution de la tâche Opacité de Form1. Le code ci-dessous est la fonction principale de Program.cs

 redBarForms = new List<Form1>(); 
     foreach (var screen in Screen.AllScreens) 
     { 
      var form = new Form1(); 
      form.Opacity = 0d; 
      form.Show(); 

      form.WindowState = FormWindowState.Normal; 
      form.Location = screen.WorkingArea.Location; 
      form.WindowState = FormWindowState.Maximized; 

      redBarForms.Add(form); // Only one is added right now, working from one screen 
     } 

     // Begin the loop which checks to see if the bars should be flashing. 
     Task.Run(() => 
     { 
      while (true) 
      { 
       while (flashingAlertBars) 
       { 
        for (int i = 1; i <= 100; i++) 
        { 
         foreach (var form in redBarForms) 
         { 
          form.Invoke(new Action(() => 
          { 
           form.Opacity = i/100d; 
          })); 
          i += 9; 
         } 
         Thread.Sleep(50); 
        } 

        for (int i = 100; i >= 1; i--) 
        { 
         foreach (var form in redBarForms) 
         { 
          form.Invoke(new Action(() => 
          { 
           form.Opacity = i/100d; 
          })); 
          i -= 9; 
         } 
         Thread.Sleep(50); 
        } 
       } 
      } 
     }); 

     MainForm = new Form2(); 

     // Start the updater on a background worker so that it doesn't 
      affect the UI thread 
     BackgroundWorker bgUpdater = new BackgroundWorker(); 
     bgUpdater.WorkerSupportsCancellation = true; 
     bgUpdater.DoWork += Updater_BGWorker_DoWork; 
     bgUpdater.RunWorkerAsync(); 

     // Start Form1 
     if (!redBarForms[0].IsDisposed) 
     { 
      redBarForms[0].Invoke(new Action(() => 
      { 
       Application.Run(redBarForms[0]); 
      })); 
     } 

Après que les deux formes sont créées, j'ai une classe, Updater, qui est exécuté sur un BackgroundWorker (bgUpdater), qui obtient une liste des alarmes qui sortent d'une API web toutes les 10 secondes, puis rend ce membre public dans sa classe. Form2 a un BackgroundWorker lui-même qui extrait ensuite ce membre de Updater sur un autre intervalle de 10 secondes et l'utilise pour créer des étiquettes cliquables et afficher des informations sur chaque alarme actuelle sous sa forme. Updater dispose d'une fonction permettant d'envoyer une notification de toast lorsqu'une alarme est détectée, et dispose d'un bouton pour ouvrir Form2 sur la page correcte où un accusé de réception peut être envoyé. L'événement de clic du bouton de notification toast exécute la méthode suivante pour ouvrir le formulaire à la bonne page:

static public void ShowManagementUI(Result monitor) 
    { 
     if (Application.OpenForms.OfType<Form2>().Count() > 0) 
     { 
      if (MainForm.InvokeRequired) 
      { 
       MainForm.Invoke(new Action(() => 
       { 
        MainForm.ShowWithResult(monitor); //This just does an Activate() 
       })); 
      } 
     } 
     else 
     { 
      if (MainForm.IsDisposed) 
       MainForm = new Form2(); 
      MainForm.Show(); 
      MainForm.ShowWithResult(monitor); //This just does an Activate() 
     } 

    } 

Lors de l'affichage Form2 juste en ouvrant la forme normalement, il n'y a pas de problème et la forme (Form2.Activate()) ne se fige pas, cependant lors de l'affichage du formulaire en utilisant ShowManagementUI (moniteur de résultat), Form2 se fige après n'importe où entre 10-30 secondes, mais Form1 continue à clignoter ses barres rouges. Il ne lance aucune exception, mais j'ai le sentiment qu'il essaie probablement d'invoquer une opération sur le thread de l'interface utilisateur, mais il est verrouillé pour une raison ou une autre.

Je ne peux pas pour la vie de moi trouver pourquoi il le ferait. Est-ce que quelqu'un a des suggestions sur le suivi d'un gel comme celui-ci?

+2

DonT utiliser Invoke() car il peut conduire à une impasse d'application. Utilisez BeginInvoke à la place – MickyD

+0

Merci pour le conseil! Il est probablement préférable d'utiliser BeginInvoke, alors? J'ai remplacé toutes les instances de Invoke() par BeginInvoke(), mais le problème est toujours présent –

Répondre

0

Appelez Application.DoEvents dans l'opacité en augmentant la boucle for afin de permettre au thread UI de rendre les modifications. De plus, l'intervalle de 50 ms est petit. Essayez de plus gros nombres.

Mais en général, ce n'est pas une bonne idée pour l'animation: P

+0

Merci pour les pointeurs! J'ai mis le Application.DoEvents() dans la boucle for comme vous l'avez mentionné, mais le problème existe toujours. J'ai également changé l'intervalle à 100 millisecondes, mais le même résultat s'est produit. Pouvez-vous clarifier ce que vous entendez par «ce n'est pas une bonne idée pour l'animation»? Avez-vous des suggestions sur la façon de le faire d'une manière plus efficace? –