web-dev-qa-db-fra.com

System.Windows.Threading.Dispatcher et WinForms?

Est-ce qu'un System.Windows.Threading.Dispatcher fonctionne sur le thread d'interface utilisateur d'une application WinForms?

Si oui pourquoi? Il provient de WindowsBase.dll qui semble être un composant WPF.

Sinon, comment puis-je rappeler des unités de travail sur le thread d'interface utilisateur? J'ai trouvé Control.BeginInvoke() , mais il semble difficile de créer un contrôle uniquement pour référencer le thread d'origine.

25
David Schmitt

Vous pouvez utiliser Dispatcher même dans une application WinForms.

Si vous êtes sûr de vous trouver sur un thread d'interface utilisateur (par exemple, dans un gestionnaire button.Click), Dispatcher.CurrentDispatcher vous donne le répartiteur de thread d'interface utilisateur que vous pourrez utiliser ultérieurement pour envoyer des threads d'arrière-plan au thread d'interface utilisateur, comme d'habitude.

18
Martin Konicek

Dispatcher est un composant WPF, pas un composant WinForms.

Si vous souhaitez répartir des éléments de travail sur le thread d'interface utilisateur, vous devez alors utiliser Control.BeginInvoke comme vous l'avez déjà trouvé ou réagir à ResetEvents/WaitObjects sur plusieurs threads.

Invoquer des éléments de travail sur le thread d'interface utilisateur est une mauvaise chose, sauf s'il s'agit d'un travail d'interface utilisateur (par exemple, la mise à jour du contenu d'un contrôle), auquel cas Control.BeginInvoke () serait suffisant.

12
OJ.

J'ai fourni un exemple d'utilisation de System.Windows.Threading.Dispatcher dans Windows Form dans ma réponse à la question "Programmation parallèle utilisant TPL sur WinForms" depuis la réponse précédente à votre question

Si vous êtes sûr d’être dans le fil de l’interface utilisateur (par exemple, dans un gestionnaire button.Click), Dispatcher.CurrentDispatcher vous donne le répartiteur de thread d'interface utilisateur que vous pouvez utiliser pour envoyer du fil d’arrière-plan au fil d’interface utilisateur, comme d’habitude.

est trompeur ou déroutant ou manque du contexte d'utilisation concret: 

  • button.Click handler n'assure pas d'être sur le thread d'interface utilisateur; 
  • si vous n'êtes pas sur le thread d'interface utilisateur, il est toujours possible d'utiliser le programme de suppression de thread d'interface utilisateur de WinForms 

On peut obtenir un répartiteur de thread WinForm UI: 

Dispatcher dispatcherUI = Dispatcher.CurrentDispatcher;

dans un gestionnaire d’événements clic ou ailleurs (dans le constructeur de formulaire) 

Et ensuite, utilisez-le pour exécuter l'interface utilisateur à partir d'autres threads, voir plus de détails sur l'exemple ci-dessous dans ma réponse

private void button1_Click(object sender, EventArgs e)
{
  Dispatcher dispUI = Dispatcher.CurrentDispatcher;
  for (int i = 2; i < 20; i++)
  {
    int j = i;
    var t = Task.Factory.StartNew
           (() =>
      {
        var result = SumRootN(j);
        dispUI.BeginInvoke
            (new Action
                 (() => richTextBox1.Text += "root " + j.ToString()
                       + " " + result.ToString() + Environment.NewLine
                 )
             , null
            );
      }
           );
}

Utilisez le thread de travail en arrière-plan en tant que pompe de message d'interface utilisateur sensible, Cet article MSDN bien que la plupart du temps à propos de WPF indique que le BWT est sensible à l'interface utilisateur même pour les formulaires Windows.

1
Preet Sangha

Parfois, un composant Timer est utile et facile à configurer dans WinForms, définissez simplement son intervalle, puis activez-le, puis assurez-vous que la première chose que vous faites dans son gestionnaire d'événements Tick est de se désactiver. 

Je pense que Timer exécute le code dans son propre thread. Vous devrez donc peut-être toujours faire un BeginInvoke (appelé sur l'objet WinForm [this]) pour exécuter votre action.

private WebBrowserDocumentCompletedEventHandler handler; //need to make it a class field for the handler below (anonymous delegates seem to capture state at point of definition, so they can't capture their own reference)
private string imageFilename;
private bool exit;

public void CaptureScreenshot(Uri address = null, string imageFilename = null, int msecDelay = 0, bool exit = false)
{
  handler = (s, e) =>
   {
     webBrowser.DocumentCompleted -= handler; //must do first

     this.imageFilename = imageFilename;
     this.exit = exit;

     timerScreenshot.Interval = (msecDelay > 0)? msecDelay : 1;
     timerScreenshot.Enabled = true;
   };

  webBrowser.DocumentCompleted += handler;
  Go(address); //if address == null, will use URL from UI
}

private void timerScreenshot_Tick(object sender, EventArgs e)
{
  timerScreenshot.Enabled = false; //must do first

  BeginInvoke((Action)(() => //Invoke at UI thread
  { //run in UI thread

    BringToFront();
    Bitmap bitmap = webBrowser.GetScreenshot();

    if (imageFilename == null)
      imageFilename = bitmap.ShowSaveFileDialog();

    if (imageFilename != null)
    {
      Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(imageFilename))); //create any parent directories needed
      bitmap.Save(imageFilename);
    }

    bitmap.Dispose(); //release bitmap resources

    if (exit)
      Close(); //this should close the app, since this is the main form

  }), null);
}

vous pouvez voir ce qui précède en action dans l'outil WebCapture ( http://gallery.clipflair.net/WebCapture , le code source à l'adresse: http://ClipFlair.codeplex.com , voir le dossier Tools/WebCapture ) qui capture des captures d'écran de sites Web. En passant, si vous voulez appeler le fichier exécutable depuis la ligne de commande, assurez-vous d’aller dans Propriétés du projet et dans l’onglet Sécurité, désactivez la sécurité ClickOnce (sinon, il n’a pas accès à la ligne de commande).

0
George Birbilis

Jetez un coup d'œil à backgrounder et voyez si cela répond à vos besoins.

0
rosenfield