使用 Timer 时未触发事件处理程序

Event handler not fired when using Timer

我希望仅在需要显示气球提示时才显示系统托盘图标,然后在气球提示关闭时隐藏该图标。

但是,一旦显示图标,我就无法让它消失,因为没有触发事件处理程序:

public partial class MainWindow : Window {

    public static NotifyIcon trayIcon = new NotifyIcon();

    public MainWindow() {
        InitializeTrayIcon();
    }

    void InitializeTrayIcon() {
        trayIcon.Text = "My App";
        trayIcon.Icon = MyApp.Properties.Resources.myIcon;
        trayIcon.Visible = false;

        //the following never gets fired:
        trayIcon.BalloonTipClosed += (sender, e) => {
            trayIcon.Visible = false; 
        };
      }

      public static void ShowTrayNotification(ToolTipIcon icon, string title, string text, int duration) {
          trayIcon.Visible = true;
          trayIcon.ShowBalloonTip(duration, title, text, icon);
      }
}

ShowTrayNotification() 是从一个由计时器触发的方法中调用的:

public abstract class Watcher {

    protected System.Timers.Timer myTimer = new System.Timers.Timer(1000);

    //the following is called in a subclass of Watcher, which is instantiated in MainWindow
    protected void SetupMyTimer() {
        myTimer.AutoReset = true;
        myTimer.Elapsed += myTimer_Elapsed;
        myTimer.Start();
    }

    protected virtual void myTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {
        myTimer.Enabled = false;
        MyTimerElapsedCallback();
        myTimer.Enabled = true;
    }

    void MyTimerElapsedCallback() {
        MainWindow.ShowTrayNotification(ToolTipIcon.Info, "Hello There!", "Balloon text here.", 5000);
    }
}

气球显示出来了。但是 MainWindow 中的 BalloonTipClosed 永远不会被解雇。

我试过:

因此,我认为问题与计时器有关,但我不知道它如何影响事件处理程序,也不知道如何解决。

有什么想法吗?

您的代码中存在线程错误,计时器的 Elapsed 事件在线程池线程上引发。当您执行此类操作时,您通常会收到 InvalidOperationException,但未针对 NotifyIcon 执行检查。

使其在错误线程上可见的副作用是在该错误线程上创建了一个用于接收事件通知的隐藏 window。它根本无法接收任何通知,线程池线程不会发送消息循环。糟糕的诊断,无一例外,也没有很好的方法来查看它为什么会出错。

您的 ShowTrayNotification() 方法必须 使用表单的 BeginInvoke() 方法,以便代码在 UI 线程上运行。因为该方法是静态的,所以很难做到这一点,在绝对紧要关头,您可以使用 Application.OpenForms[0].BeginInvoke()。但是让您的 Watcher class 引发事件而不是直接调用表单的方法肯定会更好。或者考虑使用普通的 Winforms 计时器,您可以在工具箱中找到它。正如发布的那样,Watcher class 没有可见的附加值。