Show bubble tooltips with the .NET Framework's NotifyIcon

Wow, it's been a looong time since my last blog. As you may recall, I was trying to decide wether to base my new project at work on MFC or to bite the bullet and go managed with .NET Windows Forms. I went with the managed solution. It's just so much faster to develop in, and any advanced functionality can be got at with relative ease using P/Invoke and managed C++ where needed.

As an example of this, I thought I'd outline a quick solution I rolled out this afternoon at the office. I had already written some WinForms code that used the System.Windows.Forms.NotifyIcon class to put an icon with tooltip into the tray. I'd set it up to handle double-click events and to show a context menu on a right-click of the mouse. This is all nice and easy and a no-brainer.

I then realized that I needed to show a notification to get the user's attention when some event or other happens within my application. So, bubble notifications immediately sprung to mind.

Ok, I said, I'm sure I saw that someplace in NotifyIcon. It must be there, everything else is...

No.

For some reason, it's not. That means messing with Shell_NotifyIcon to set the icon, create a context menu and respond to events from the mouse as well as create a bubble notification. Ouch. That seems like a lot of stuff to do, when 80% of it is already implemented in NotifyIcon. Can't I re-use this class and just augment it to handle the bubbles?

Hmm...unfortunately the NotifyIcon class is sealed, so no subclassing is possible. Well, lets see. I pull out Reflector (most usefull tool in my arsenal, by far) and have a quick peek at how NotifyIcon is implemented with respect to the instance variables holding HWND and id values used for Shell_NotifyIcon. As you may be aware, a lot of the .NET libraries fulfill their functionality by re-using native system libraries (and why not?) using P/Invoke and COM Interop in places (System.DirectoryServices springs to mind). So NotifyIcon is just a wrapper class, making calls to Shell_NotifyIcon beneath the covers.


Ok. This is what I found:

There is a window instance variable of type NativeWindow, and an id variable of type int. The window variable holds a reference to the NativeWindow subclass used to represent the hidden window of the tray icon. The int holds an integer representing a unique application defined value used for uniquely identifying an icon (in conjunction with the HWND handle held by the NativeWindow window variable).

Knowing these two values, the id and the HWND (from the NativeWindow) I can then just make a single Shell_NotifyIcon to create a bubble notification, using the existing NotifyIcon .net object already used to do the hard/tedious stuff, such as the icon, tooltip, menu and mouse click message processing. One problem. They are declared as private.

5 minutes and some knowledge of the System.Reflection namespace puts that baby to sleep!

Using reflection, it is possible to get at the values of those private variables hold (in the case of the NativeWindow reference, the actual type of the reference retrieved is of an internal type that we cant use directly, but it's base class is public so cast to NativeWindow and we are good to go; the base class implements get_Handle() which is what we need).

I put it all into a neat wrapper class that composes the NotifyIcon class and adds the bubble notification ability exposed in a nice easy set of properties and one method.

Hope this helps someone out there! BTW, the NOTIFYICONDATA struct definition was taken from www.pinvoke.net. For those of you who dont know, this site provides loads of already figured out P/Invoke signatures with the marshalling attributes already set etc. Great site.

Code can be downloaded from here.

Note: This is not production quality, just a work in progress/proof of concept. I dont recommend you use it without first adding some more comprehensive error handling. Otherwise it works great!

Comments