From de6a2b2e2598e8842c17ce14635a918ec3dbaa74 Mon Sep 17 00:00:00 2001 From: Robin Krom Date: Wed, 5 Aug 2020 23:51:49 +0200 Subject: [PATCH] Fix for the DPI issue described in #26 --- src/NotifyIconWpf/Interop/SystemInfo.cs | 26 ++++++++++- src/NotifyIconWpf/Interop/TrayInfo.cs | 9 +--- src/NotifyIconWpf/Interop/WindowClass.cs | 1 - .../Interop/WindowMessageSink.cs | 24 +++++++--- src/NotifyIconWpf/Interop/WindowsMessages.cs | 17 ++++--- src/NotifyIconWpf/TaskbarIcon.cs | 8 ++-- src/Sample Project/Sample Project.csproj | 1 + src/Sample Project/app.manifest | 45 +++++++++++++++++++ .../Windowless Sample.csproj | 1 + src/Windowless Sample/app.manifest | 45 +++++++++++++++++++ .../WindowsFormsSample.csproj | 1 + src/WindowsFormsSample/app.manifest | 45 +++++++++++++++++++ 12 files changed, 197 insertions(+), 26 deletions(-) create mode 100644 src/Sample Project/app.manifest create mode 100644 src/Windowless Sample/app.manifest create mode 100644 src/WindowsFormsSample/app.manifest diff --git a/src/NotifyIconWpf/Interop/SystemInfo.cs b/src/NotifyIconWpf/Interop/SystemInfo.cs index 9671af6..6edeead 100644 --- a/src/NotifyIconWpf/Interop/SystemInfo.cs +++ b/src/NotifyIconWpf/Interop/SystemInfo.cs @@ -21,6 +21,7 @@ // // THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE +using System.Diagnostics.Contracts; using System.Windows.Interop; namespace Hardcodet.Wpf.TaskbarNotification.Interop @@ -30,12 +31,18 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// public static class SystemInfo { + /// + /// Make sure the initial value is calculated at the first access + /// static SystemInfo() { - UpdateFactors(); + UpdateDpiFactors(); } - internal static void UpdateFactors() + /// + /// This calculates the current DPI values and sets this into the DpiFactorX/DpiFactorY values + /// + internal static void UpdateDpiFactors() { using (var source = new HwndSource(new HwndSourceParameters())) { @@ -59,5 +66,20 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// Returns the DPI Y Factor /// public static double DpiFactorY { get; private set; } = 1; + + /// + /// Scale the supplied point to the current DPI settings + /// + /// + /// Point + [Pure] + public static Point ScaleWithDpi(this Point point) + { + return new Point + { + X = (int)(point.X / DpiFactorX), + Y = (int)(point.Y / DpiFactorY) + }; + } } } \ No newline at end of file diff --git a/src/NotifyIconWpf/Interop/TrayInfo.cs b/src/NotifyIconWpf/Interop/TrayInfo.cs index c762a68..3930a89 100644 --- a/src/NotifyIconWpf/Interop/TrayInfo.cs +++ b/src/NotifyIconWpf/Interop/TrayInfo.cs @@ -51,13 +51,6 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// /// Point /// Point - public static Point GetDeviceCoordinates(Point point) - { - return new Point - { - X = (int)(point.X / SystemInfo.DpiFactorX), - Y = (int)(point.Y / SystemInfo.DpiFactorY) - }; - } + public static Point GetDeviceCoordinates(Point point) => point.ScaleWithDpi(); } } \ No newline at end of file diff --git a/src/NotifyIconWpf/Interop/WindowClass.cs b/src/NotifyIconWpf/Interop/WindowClass.cs index 0a3c5e2..baba148 100644 --- a/src/NotifyIconWpf/Interop/WindowClass.cs +++ b/src/NotifyIconWpf/Interop/WindowClass.cs @@ -9,7 +9,6 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// public delegate IntPtr WindowProcedureHandler(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam); - /// /// Win API WNDCLASS struct - represents a single window. /// Used to receive window messages. diff --git a/src/NotifyIconWpf/Interop/WindowMessageSink.cs b/src/NotifyIconWpf/Interop/WindowMessageSink.cs index 97f0392..2ad3882 100644 --- a/src/NotifyIconWpf/Interop/WindowMessageSink.cs +++ b/src/NotifyIconWpf/Interop/WindowMessageSink.cs @@ -6,10 +6,10 @@ // modify it under the terms of the Code Project Open License (CPOL); // either version 1.0 of the License, or (at your option) any later // version. -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -70,7 +70,7 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// /// Handle for the message window. - /// + /// internal IntPtr MessageWindowHandle { get; private set; } /// @@ -223,9 +223,21 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// Provides information about the event. private void ProcessWindowMessage(uint msg, IntPtr wParam, IntPtr lParam) { - if (msg != CallbackMessageId) return; + // Check if it was a callback message + if (msg != CallbackMessageId) + { + // It was not a callback message, but make sure it's not something else we need to process + switch ((WindowsMessages) msg) + { + case WindowsMessages.WM_DPICHANGED: + Debug.WriteLine("DPI Change"); + SystemInfo.UpdateDpiFactors(); + break; + } + return; + } - var message = (WindowsMessages) lParam.ToInt32(); + var message = (WindowsMessages)lParam.ToInt32(); Debug.WriteLine("Got message " + message); switch (message) { @@ -338,7 +350,7 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue + // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); diff --git a/src/NotifyIconWpf/Interop/WindowsMessages.cs b/src/NotifyIconWpf/Interop/WindowsMessages.cs index 51ae1bf..506b3d3 100644 --- a/src/NotifyIconWpf/Interop/WindowsMessages.cs +++ b/src/NotifyIconWpf/Interop/WindowsMessages.cs @@ -6,10 +6,10 @@ // modify it under the terms of the Code Project Open License (CPOL); // either version 1.0 of the License, or (at your option) any later // version. -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -37,8 +37,8 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// /// Notifies a window that the user clicked the right mouse button (right-clicked) in the window. /// See WM_CONTEXTMENU message - /// - /// In case of a notify icon: + /// + /// In case of a notify icon: /// If a user selects a notify icon's shortcut menu with the keyboard, the Shell now sends the associated application a WM_CONTEXTMENU message. Earlier versions send WM_RBUTTONDOWN and WM_RBUTTONUP messages. /// See Shell_NotifyIcon function /// @@ -57,7 +57,7 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// Posted when the user presses the left mouse button while the cursor is in the client area of a window. /// If the mouse is not captured, the message is posted to the window beneath the cursor. /// Otherwise, the message is posted to the window that has captured the mouse. - /// + /// /// See WM_LBUTTONDOWN message /// WM_LBUTTONDOWN = 0x0201, @@ -134,6 +134,13 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// WM_MBUTTONDBLCLK = 0x0209, + /// + /// Sent when the effective dots per inch (dpi) for a window has changed. + /// The DPI is the scale factor for a window. + /// There are multiple events that can cause the DPI to change. + /// + WM_DPICHANGED = 0x02e0, + /// /// Used to define private messages for use by private window classes, usually of the form WM_USER+x, where x is an integer value. /// diff --git a/src/NotifyIconWpf/TaskbarIcon.cs b/src/NotifyIconWpf/TaskbarIcon.cs index 6222df5..33bf8e3 100644 --- a/src/NotifyIconWpf/TaskbarIcon.cs +++ b/src/NotifyIconWpf/TaskbarIcon.cs @@ -6,10 +6,10 @@ // modify it under the terms of the Code Project Open License (CPOL); // either version 1.0 of the License, or (at your option) any later // version. -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -561,7 +561,7 @@ namespace Hardcodet.Wpf.TaskbarNotification HasDropShadow = false, BorderThickness = new Thickness(0), Background = System.Windows.Media.Brushes.Transparent, - // setting the + // setting the StaysOpen = true, Content = TrayToolTip }; @@ -1049,7 +1049,7 @@ namespace Hardcodet.Wpf.TaskbarNotification // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue + // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); diff --git a/src/Sample Project/Sample Project.csproj b/src/Sample Project/Sample Project.csproj index d2e0cac..e46ca69 100644 --- a/src/Sample Project/Sample Project.csproj +++ b/src/Sample Project/Sample Project.csproj @@ -5,6 +5,7 @@ Samples Sample Project Sample Project + app.manifest diff --git a/src/Sample Project/app.manifest b/src/Sample Project/app.manifest new file mode 100644 index 0000000..cfc95aa --- /dev/null +++ b/src/Sample Project/app.manifest @@ -0,0 +1,45 @@ + + + + + + True/PM + PerMonitorV2,PerMonitor + true + false + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Windowless Sample/Windowless Sample.csproj b/src/Windowless Sample/Windowless Sample.csproj index 8b973fe..4d63818 100644 --- a/src/Windowless Sample/Windowless Sample.csproj +++ b/src/Windowless Sample/Windowless Sample.csproj @@ -5,6 +5,7 @@ Windowless_Sample Windowless Sample Windowless Sample + app.manifest diff --git a/src/Windowless Sample/app.manifest b/src/Windowless Sample/app.manifest new file mode 100644 index 0000000..432278f --- /dev/null +++ b/src/Windowless Sample/app.manifest @@ -0,0 +1,45 @@ + + + + + + True/PM + PerMonitorV2,PerMonitor + true + false + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/WindowsFormsSample/WindowsFormsSample.csproj b/src/WindowsFormsSample/WindowsFormsSample.csproj index 85bae38..e05627c 100644 --- a/src/WindowsFormsSample/WindowsFormsSample.csproj +++ b/src/WindowsFormsSample/WindowsFormsSample.csproj @@ -6,6 +6,7 @@ WindowsFormsSample WindowsFormsSample true + app.manifest diff --git a/src/WindowsFormsSample/app.manifest b/src/WindowsFormsSample/app.manifest new file mode 100644 index 0000000..432278f --- /dev/null +++ b/src/WindowsFormsSample/app.manifest @@ -0,0 +1,45 @@ + + + + + + True/PM + PerMonitorV2,PerMonitor + true + false + + + + + + + + + + + + + + + + + + + + + + + + +