{"id":10,"date":"2010-12-07T15:20:47","date_gmt":"2010-12-07T04:20:47","guid":{"rendered":"http:\/\/blog.quppa.net\/?p=7"},"modified":"2010-12-07T15:20:47","modified_gmt":"2010-12-07T04:20:47","slug":"windows-7-style-notification-area-applications-in-wpf-part-1-removing-resize","status":"publish","type":"post","link":"https:\/\/www.quppa.net\/blog\/2010\/12\/07\/windows-7-style-notification-area-applications-in-wpf-part-1-removing-resize\/","title":{"rendered":"Windows 7-style Notification Area Applications in WPF: Part 1 (Removing Resize)"},"content":{"rendered":"<blockquote><p><a href=\"https:\/\/github.com\/Quppa\/NotificationAreaIconSampleAppWPF\" title=\"GitHub: NotificationAreaIconSampleAppWPF\">View source on GitHub.<\/a><\/p><\/blockquote>\n<p><a title=\"Keiki Usage Meter\" href=\"http:\/\/www.quppa.net\/keiki\/\">Keiki<\/a>, my <a title=\"Optus Broadband Webpage\" href=\"http:\/\/www.optus.com.au\/home\/broadband\/\">OptusNet<\/a> Usage Meter, is designed to sit in the notification area (or system tray, <a title=\"Wikipedia: Notification area\" href=\"http:\/\/en.wikipedia.org\/wiki\/Notification_area#Taskbar_elements\">if you prefer<\/a>) and behave similarly to the default system \u2018applets\u2019 (Volume\/Network\/Action Centre\/Power). That is, the application becomes visible with a single left click on the notify (tray) icon, and is hidden again when focus is lost.<img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; border-right-width: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; float: right; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px\" title=\"Notification Area\" border=\"0\" alt=\"Notification Area\" align=\"right\" src=\"https:\/\/www.quppa.net\/blog\/wp-content\/uploads\/Notification-Area.png\" width=\"250\" height=\"229\" \/><\/p>\n<p>I have recently started to refactor Keiki\u2019s code, as I have learnt a lot about WPF since I first wrote the application. While refactoring I\u2019ve also tried to polish a few rough edges: one of these is the main window\u2019s resize behaviour. The Windows 7 (and Vista before it) tray applications (excuse the ever-changing terminology) have no title bar and are not resizable. It turns out that this window style isn\u2019t trivial to implement with WPF.<\/p>\n<p><em><strong>Updated: <\/strong>improved code to focus application when border is clicked.<\/em><\/p>\n<p>  <!--more-->  <\/p>\n<p>Intuitively, we would simply set WindowStyle to \u2018None\u2019 and ResizeMode to \u2018NoResize\u2019:<\/p>\n<pre class=\"lang:xaml decode:true \">&lt;Window ... WindowStyle=&quot;None&quot; ResizeMode=&quot;NoResize&quot;&gt;<\/pre>\n<p>However, this results in a window without a border at all. Using \u2018CanResize\u2019 as the ResizeMode gives us the correct appearance, but now we\u2019re stuck with a resizable window.<\/p>\n<p>My earlier solution (as found in the current version of Keiki) was to simply set the window\u2019s MinWidth\/MinHeight and MaxWidth\/MaxHeight to the same value when the window is loaded:<\/p>\n<pre class=\"lang:c# decode:true \">this.MinWidth = this.ActualWidth;\nthis.MaxWidth = this.ActualWidth;\nthis.MinHeight = this.ActualHeight;\nthis.MaxHeight = this.ActualHeight;<\/pre>\n<p>This has a few drawbacks:<\/p>\n<ol>\n<li>The resize cursors still appear when the mouse is over the window border. <\/li>\n<li>In Windows 7, <a title=\"Aero Snap Webpage\" href=\"http:\/\/www.microsoft.com\/windows\/windows-7\/features\/snap.aspx\">Aero Snap<\/a> still kind-of works, resulting in some strange behaviour (the window moves to the top right corner of the screen). <\/li>\n<\/ol>\n<p>Fortunately, <a title=\"C&amp;D Pending: Aero Glass Border without Resize in WPF\" href=\"http:\/\/dmullaney.tumblr.com\/post\/971504547\/aero-glass-border-without-resize-in-wpf\">Dave Mullaney has a better solution<\/a>, which I will reproduce here with some changes.<\/p>\n<p>Firstly, set the window\u2019s WindowStyle to \u2018None\u2019 and ResizeMode to \u2018CanResize\u2019.<\/p>\n<p>We now need to add some code-behind that handles the messages sent to our window. I will try not to embarrass myself by displaying my level of ignorance regarding the Win32 API and leave it at that.<\/p>\n<p>Simply copy the following into your window\u2019s code-behind:<\/p>\n<pre class=\"lang:c# decode:true \">using System.Windows.Interop;\nusing System.Runtime.InteropServices;<\/pre>\n<p>\u2026<\/p>\n<pre class=\"lang:c# decode:true \">#region Disable Window Resize\nprotected override void OnSourceInitialized(EventArgs e)\n{\n    base.OnSourceInitialized(e);\n    HwndSource source = PresentationSource.FromVisual(this) as HwndSource;\n    source.AddHook(WndProc);\n}\n\/\/ system constants\nprivate const int WM_NCHITTEST = 0x0084;\nprivate const int WM_SETCURSOR = 0x0020;\nprivate const int WM_LBUTTONDOWN = 0x0201;\nprivate const int WM_RBUTTONDOWN = 0x0204;\nprivate const int WM_MBUTTONDOWN = 0x0207;\nprivate const int WM_XBUTTONDOWN = 0x020B; \/\/ back\/forward buttons\nprivate const int HTCLIENT = 0x1;\n[DllImport(&quot;user32.dll&quot;)]\nprivate static extern IntPtr DefWindowProc(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam);\nprivate IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)\n{\n    switch (msg)\n    {\n        case WM_NCHITTEST:\n            \/\/ if the mouse pointer is not over the client area of the tab\n            \/\/ ignore it - this disables resize on the glass chrome\n            if (!IsOverClientArea(hwnd, wParam, lParam))\n                handled = true;\n            break;\n        case WM_SETCURSOR:\n            if (!IsOverClientArea(hwnd, wParam, lParam))\n            {\n                \/\/ the high word of lParam specifies the mouse message identifier\n                \/\/ we only want to handle mouse down messages on the border\n                int hiword = (int)lParam &gt;&gt; 16;\n                if (hiword == WM_LBUTTONDOWN\n                    || hiword == WM_RBUTTONDOWN\n                    || hiword == WM_MBUTTONDOWN\n                    || hiword == WM_XBUTTONDOWN)\n                {\n                    handled = true;\n                    this.Focus(); \/\/ focus the window\n                }\n            }\n            break;\n    }\n    return IntPtr.Zero;\n}\nprivate bool IsOverClientArea(IntPtr hwnd, IntPtr wParam, IntPtr LParam)\n{\n    IntPtr uHitTest = DefWindowProc(hwnd, WM_NCHITTEST, wParam, LParam);\n    if (uHitTest.ToInt32() == HTCLIENT) \/\/ check if we're over the client area\n        return true;\n    return false;\n}\n#endregion<\/pre>\n<p>With that, your window should now be non-resizable despite still having a border.<\/p>\n<p><strike><strong>Note:<\/strong> since we\u2019re ignoring any window messages when the mouse pointer isn\u2019t over the client area, the application will no longer gain focus when its borders are clicked (it won\u2019t lose focus, either). I\u2019m interested to hear of any solutions to this problem.<\/strike> <strong>Update: <\/strong>Code improved to give focus when the border is clicked.<\/p>\n<p>In a future post I will explore positioning windows in relation to notification icons with managed code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>View source on GitHub. Keiki, my OptusNet Usage Meter, is designed to sit in the notification area (or system tray, if you prefer) and behave similarly to the default system \u2018applets\u2019 (Volume\/Network\/Action Centre\/Power). That is, the application becomes visible with a single left click on the notify (tray) icon, and is hidden again when focus &hellip; <a href=\"https:\/\/www.quppa.net\/blog\/2010\/12\/07\/windows-7-style-notification-area-applications-in-wpf-part-1-removing-resize\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Windows 7-style Notification Area Applications in WPF: Part 1 (Removing Resize)&#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,72,74,87,170,185],"class_list":["post-10","post","type-post","status-publish","format-standard","hentry","category-keiki","category-programming","tag-c","tag-interop","tag-keiki-2","tag-notification-area","tag-win32","tag-wpf"],"_links":{"self":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts\/10","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=10"}],"version-history":[{"count":0,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/posts\/10\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/media?parent=10"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/categories?post=10"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.quppa.net\/blog\/wp-json\/wp\/v2\/tags?post=10"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}