Windows 7-style Notification Area Applications in WPF: Part 4 (Multiple Monitors)

View source on GitHub.

At the end of Part 3 in this series, I noted that the window positioning logic depends on accurately getting the bounds of the monitor where the notify icon is located. Specifically, we require the bounds of the working area (the space on the monitor excluding the taskbar and other docked items). WPF gives us the System.Windows.SystemParameters.WorkArea property, but this gives us the area of the primary display monitor’s working area, and the taskbar (and thus notify icon) might be located on a different monitor. Unfortunately, support for accessing information about anything other than the primary monitor with the SystemParameters class seems to be absent as of .NET 4.0.

We could use the System.Windows.Forms.Screen class to easily solve this problem: the System.Windows.Forms.Screen.GetWorkingArea method has an overload for finding the working area of the monitor that contains a given rectangle, which is exactly what we need to do. However, I am going to opt to use Win32, instead, simply to avoid depending on WinForms (yes, I realise that sounds a bit strange given that this project revolves around a System.Windows.Forms.NotifyIcon). In any case, there is something to be said for knowing what it is that all these .NET functions wrap around 🙂

The two functions we’ll use are MonitorFromRect (to find the handle of the monitor containing the notify icon) and GetMonitorInfo (to get the working area of that monitor).

MonitorFromRect

First, we’ll get the handle of the monitor that displays the notify icon. The syntax for MonitorFromRect is:

and the PInvoke signature in C# is (you’ll also need to define the RECT struct if you haven’t already – see Part 2 or Part 3):

dwFlags specifies what the function should return if the rectangle doesn’t intersect with any monitor. These are the possible options:

I will choose to use MONITOR_DEFAULTTONEAREST for this project, but this argument shouldn’t really matter; the notify icon rectangle should always intersect with a monitor.

GetMonitorInfo

Now we will define the GetMonitorInfo class, which will tell us the working area rectangle of the monitor we find with MonitorFromRect. The syntax is:

The PInvoke signature:

We need to also define the MONITORINFO struct:

Putting it together

We can now write a method that puts these two functions together to find our notify icon rectangle’s monitor’s working area bounds.

monitorinfo.rcWork will hold a RECT with the correct working area bounds.

Leave a comment

Your email address will not be published. Required fields are marked *