Reputation: 3205
I would like to have a system tray icon appear only when I need to show a balloon tip, then hide the icon when the balloon tip is closed.
However, once the icon is shown, I can't get it to disappear because the event handler is not fired:
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);
}
}
The ShowTrayNotification()
is called from a method that is triggered by a timer:
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);
}
}
So the balloon is shown. But BalloonTipClosed
in MainWindow
is never fired.
I have tried:
putting the (1) creation of the NotifyIcon
, (2) displaying of balloon, and (3) setting BalloonTipClosed
all in MainWindow
, and it works fine (i.e. BalloonTipClosed
is fired)
putting (1), (2), and (3) in SetupMyTimer()
and it works fine as well
putting (1), (2), and (3) in MyTimerElapsedCallback()
and it does not work (i.e. BalloonTipClosed
is not fired)
changing BalloonTipClosed
to BalloonTipClicked
and it does not work as well.
using non-lambda BalloonTipClosed EventHandler, does not work.
with this, I am thinking the problem has to do with the Timer, but I don't know how it's affecting the event handler, nor how to fix.
Any ideas?
Upvotes: 0
Views: 716
Reputation: 942178
You have a threading bug in your code, the timer's Elapsed event is raised on a threadpool thread. You normally get an InvalidOperationException when you do this sort of thing but the check is not implemented for NotifyIcon.
The side-effect of making it visible on the wrong thread is that an otherwise hidden window that is used to receive the event notifications is created on that bad thread. It cannot receive any notifications at all, the threadpool thread does not pump a message loop. Lousy diagnostic, no exception and no good way to see why it goes wrong.
Your ShowTrayNotification() method must use the form's BeginInvoke() method so that the code runs on the UI thread. You make that difficult because the method is static, in an absolute pinch you could use Application.OpenForms[0].BeginInvoke(). But it would certainly be better to have your Watcher class raise an event instead of calling the form's method directly. Or consider using a plain Winforms' Timer, the one you find back in the toolbox. As posted, the Watcher class has no visible added value.
Upvotes: 2