2017-09-28 2 views
0

J'utilise le code suivantEn utilisant async dans le contexte statique pour mettre à jour l'interface utilisateur ne fonctionne pas

public static class Program 
{ 
    [STAThread] 
    public static void Main(string[] args) 
    { 
     CancellationTokenSource tokenSource = new CancellationTokenSource(); 

     Task timerTask = Program.RunPeriodically(() => { Program.SendNotification($"foo", BalloonIcon.Info); }, TimeSpan.FromSeconds(10), tokenSource.Token); 
     timerTask.Wait(tokenSource.Token); 
    } 

    private static void SendNotification(string message, BalloonIcon balloonIcon) 
    { 
     new TaskbarIcon().ShowBalloonTip("Title", message, balloonIcon); 
    } 

    private static async Task RunPeriodically(Action action, TimeSpan interval, CancellationToken token) 
    { 
     while (true) 
     { 
      action(); 
      await Task.Delay(interval, token); 
     } 
    } 
} 

et ce que je veux faire est de lancer la méthode SendNotification toutes les 10 secondes. Pour ce faire, j'appelle la méthode RunPeriodically.

Cependant, l'appel à SendNotifcation jette un InvalidOperationException, en disant:

Le thread appelant doit être STA, parce que de nombreux composants de l'interface utilisateur exigent.

J'ai aussi essayé some suggestions utilisant le Dispatcher comme celui-ci

private static void SendNotification(string message, BalloonIcon balloonIcon) 
{ 
    Dispatcher.CurrentDispatcher.Invoke(() => 
    { 
     new TaskbarIcon().ShowBalloonTip("Title", message, balloonIcon); 
    }); 
} 

mais il n'a pas fait un changement. Ma seule supposition est que le code ne fonctionne pas car il n'est pas appelé à partir d'une instance Window, mais plutôt dans un contexte statique d'une classe qui n'a pas this.Dispatcher, mais je ne sais pas comment le faire fonctionner dans ce cas et j'apprécie toute aide.

+1

Vous * n'avez * pas * d'application de console si vous avez une interface utilisateur à mettre à jour. Les applications Windows Forms et WPF commencent à partir d'une fonction Main, même si elle n'est pas apparente. Cela ne fait pas les applications * console * –

+0

@PanagiotisKanavos oui, vous avez raison. Je vous remercie. Je vais modifier le titre de la question pour effacer cette idée fausse. –

+0

En ce qui concerne votre problème spécifique, juste * ne pas * mettre à jour l'interface utilisateur à partir d'un autre thread. Utilisez async/await pour * attendre * tout ce qui s'exécute en arrière-plan et mettez à jour l'interface utilisateur après l'appel de 'await'. 'await' ne fait rien tourner en arrière-plan. Il * attend * qu'une tâche d'arrière-plan/travail se termine, puis retourne au thread d'interface utilisateur. Vous n'avez pas besoin de dispatchers et de 'Invoke()' lorsque vous êtes déjà sur le thread d'interface utilisateur –

Répondre

1

cet exemple légèrement modifié ci-dessous fonctionne plutôt bien pour moi.

J'espère que ça aide!

public static class Program 
{ 
    private static NotifyIcon notifyIcon; 

    [STAThread] 
    public static void Main(string[] args) 
    { 
     CancellationTokenSource tokenSource = new CancellationTokenSource(); 

     notifyIcon = new NotifyIcon(); 
     notifyIcon.Icon = SystemIcons.Information; 
     notifyIcon.BalloonTipTitle = "Title"; 
     notifyIcon.Visible = true; 

     Task timerTask = Program.RunPeriodically(() => { Program.SendNotification(DateTime.Now.ToString(), ToolTipIcon.Info); }, TimeSpan.FromSeconds(10), tokenSource.Token); 
     timerTask.Wait(tokenSource.Token); 
    } 

    private static void SendNotification(string message, ToolTipIcon balloonIcon) 
    { 
     notifyIcon.BalloonTipIcon = balloonIcon; 
     notifyIcon.BalloonTipText = message; 
     notifyIcon.ShowBalloonTip(500); 
    } 

    private static async Task RunPeriodically(Action action, TimeSpan interval, CancellationToken token) 
    { 
     while (true) 
     { 
      action(); 
      await Task.Delay(interval, token); 
     } 
    } 
} 
+0

Cela a résolu pour moi. Fonctionne comme un charme, merci! –