{"id":65,"date":"2010-12-11T18:15:11","date_gmt":"2010-12-11T07:15:11","guid":{"rendered":"http:\/\/blog.quppa.net\/?p=65"},"modified":"2010-12-11T18:15:11","modified_gmt":"2010-12-11T07:15:11","slug":"windows-7-style-notification-area-applications-in-wpf-part-4-multiple-monitors","status":"publish","type":"post","link":"https:\/\/www.quppa.net\/blog\/2010\/12\/11\/windows-7-style-notification-area-applications-in-wpf-part-4-multiple-monitors\/","title":{"rendered":"Windows 7-style Notification Area Applications in WPF: Part 4 (Multiple Monitors)"},"content":{"rendered":"<blockquote><p><a href=\"https:\/\/github.com\/Quppa\/NotificationAreaIconSampleAppWPF\" title=\"GitHub: NotificationAreaIconSampleAppWPF\">View source on GitHub.<\/a><\/p><\/blockquote>\n<p>At the end of <a title=\"Windows 7-style Notification Area Applications in WPF: Part 3 (Taskbar Position)\" href=\"https:\/\/www.quppa.net\/blog\/2010\/12\/09\/windows-7-style-notification-area-applications-in-wpf-part-3-taskbar-position\/\">Part 3<\/a> 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 <a title=\"MSDN: System.Windows.Forms.Screen.WorkingArea Property\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.forms.screen.workingarea.aspx\">working area<\/a> (the space on the monitor excluding the taskbar and other docked items). WPF gives us the <a title=\"MSDN: System.Windows.SystemParameters.WorkArea Property\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.systemparameters.workarea.aspx\">System.Windows.SystemParameters.WorkArea property<\/a>, but this gives us the area of the primary display monitor\u2019s 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 <a title=\"MSDN: System.Windows.SystemParameters Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.systemparameters.aspx\">SystemParameters class<\/a> seems to be absent as of .NET 4.0.<\/p>\n<p>We could use the <a title=\"MSDN: System.Windows.Forms.Screen Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.forms.screen.aspx\">System.Windows.Forms.Screen class<\/a> to easily solve this problem: the <a title=\"MSDN: System.Windows.Forms.Screen.GetWorkingArea Method (Rectangle)\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/f0f103x9.aspx\">System.Windows.Forms.Screen.GetWorkingArea method has an overload<\/a> 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 <a title=\"MSDN: System.Windows.Forms.NotifyIcon Class\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.forms.notifyicon.aspx\">System.Windows.Forms.NotifyIcon<\/a>). In any case, there is something to be said for knowing what it is that all these .NET functions wrap around \ud83d\ude42<\/p>\n<p>The two functions we\u2019ll use are <a title=\"MSDN: MonitorFromRect Function\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd145063(VS.85).aspx\">MonitorFromRect<\/a> (to find the handle of the monitor containing the notify icon) and <a title=\"MSDN: GetMonitorInfo Function\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd144901(v=vs.85).aspx\">GetMonitorInfo<\/a> (to get the working area of that monitor).<\/p>\n<p>  <!--more-->  <\/p>\n<h1>MonitorFromRect<\/h1>\n<p>First, we\u2019ll get the handle of the monitor that displays the notify icon. The syntax for MonitorFromRect is:<\/p>\n<pre class=\"lang:c decode:true \">HMONITOR MonitorFromRect(\n  __in  LPCRECT lprc,\n  __in  DWORD dwFlags\n);<\/pre>\n<p>and the PInvoke signature in C# is (you\u2019ll also need to define the RECT struct if you haven\u2019t already \u2013 see <a title=\"Windows 7-style Notification Area Applications in WPF: Part 2 (Notify Icon Position)\" href=\"https:\/\/www.quppa.net\/blog\/2010\/12\/08\/windows-7-style-notification-area-applications-in-wpf-part-2-notify-icon-position\/\">Part 2<\/a> or <a title=\"Windows 7-style Notification Area Applications in WPF: Part 3 (Taskbar Position)\" href=\"https:\/\/www.quppa.net\/blog\/2010\/12\/09\/windows-7-style-notification-area-applications-in-wpf-part-3-taskbar-position\/\">Part 3<\/a>):<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;user32.dll&quot;)]\nprivate static extern IntPtr MonitorFromRect([In] ref RECT lprc, int dwFlags);<\/pre>\n<p>dwFlags specifies what the function should return if the rectangle doesn\u2019t intersect with any monitor. These are the possible options:<\/p>\n<pre class=\"lang:c# decode:true \">private const int MONITOR_DEFAULTTONULL = 0; \/\/ returns null\nprivate const int MONITOR_DEFAULTTOPRIMARY = 1; \/\/ returns primary monitor handle\nprivate const int MONITOR_DEFAULTTONEAREST = 2; \/\/ returns nearest monitor handle<\/pre>\n<p>I will choose to use MONITOR_DEFAULTTONEAREST for this project, but this argument shouldn\u2019t really matter; the notify icon rectangle should always intersect with a monitor.<\/p>\n<h1>GetMonitorInfo<\/h1>\n<p>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:<\/p>\n<pre class=\"lang:c decode:true \">BOOL GetMonitorInfo(\n  __in   HMONITOR hMonitor,\n  __out  LPMONITORINFO lpmi\n);<\/pre>\n<p>The PInvoke signature:<\/p>\n<pre class=\"lang:c# decode:true \">[DllImport(&quot;user32.dll&quot;)]\nprivate static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);<\/pre>\n<p>We need to also define the MONITORINFO struct:<\/p>\n<pre class=\"lang:c# decode:true \">[StructLayout(LayoutKind.Sequential)]\nprivate struct MONITORINFO\n{\n    public Int32 cbSize;\n    public RECT rcMonitor;\n    public RECT rcWork;\n    public Int32 dwFlags;\n}<\/pre>\n<h1>Putting it together<\/h1>\n<p>We can now write a method that puts these two functions together to find our notify icon rectangle\u2019s monitor\u2019s working area bounds.<\/p>\n<pre class=\"lang:c# decode:true \">IntPtr monitorhandle = MonitorFromRect(ref rect, MONITOR_DEFAULTTONEAREST);\nMONITORINFO monitorinfo = new MONITORINFO();\nmonitorinfo.cbSize = Marshal.SizeOf(monitorinfo);\nGetMonitorInfo(monitorhandle, ref monitorinfo);<\/pre>\n<p>monitorinfo.rcWork will hold a RECT with the correct working area bounds.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 &hellip; <a href=\"https:\/\/www.quppa.net\/blog\/2010\/12\/11\/windows-7-style-notification-area-applications-in-wpf-part-4-multiple-monitors\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Windows 7-style Notification Area Applications in WPF: Part 4 (Multiple Monitors)&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,6],"tags":[28,47,72,74,87,88,96,146,159,170,179,185],"class_list":["post-65","post","type-post","status-publish","format-standard","hentry","category-keiki","category-programming","tag-c","tag-dllimport","tag-interop","tag-keiki-2","tag-notification-area","tag-notifyicon","tag-pinvoke","tag-taskbar","tag-user32","tag-win32","tag-winforms","tag-wpf"],"_links":{"self":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts\/65","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/comments?post=65"}],"version-history":[{"count":0,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts\/65\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/media?parent=65"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/categories?post=65"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/tags?post=65"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}