From 4a656bb24e5766c83f4a4c5e1225bab675abb9c5 Mon Sep 17 00:00:00 2001 From: Philipp Sumi Date: Fri, 22 Nov 2013 19:12:16 +0000 Subject: [PATCH] CHG Getting physical cursor position on Vista and above. FIX Calculating device coordinates into logical ones used by WPF. Needed in case of custom DPI settings. CHG Renamed timer to be more explicit. NTFY-22 git-svn-id: https://svn.evolvesoftware.ch/repos/evolve.net/WPF/NotifyIcon@194 9f600761-6f11-4665-b6dc-0185e9171623 --- Source/NotifyIconWpf/Interop/WinApi.cs | 6 ++- Source/NotifyIconWpf/TaskbarIcon.cs | 63 ++++++++++++++++++++++---- Source/NotifyIconWpf/Util.cs | 1 + 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/Source/NotifyIconWpf/Interop/WinApi.cs b/Source/NotifyIconWpf/Interop/WinApi.cs index c9f8ffd..8aef54a 100644 --- a/Source/NotifyIconWpf/Interop/WinApi.cs +++ b/Source/NotifyIconWpf/Interop/WinApi.cs @@ -79,8 +79,10 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// /// Gets the screen coordinates of the current mouse position. /// - /// - /// + [DllImport("USER32.DLL", SetLastError = true)] + public static extern bool GetPhysicalCursorPos(ref Point lpPoint); + + [DllImport("USER32.DLL", SetLastError = true)] public static extern bool GetCursorPos(ref Point lpPoint); } diff --git a/Source/NotifyIconWpf/TaskbarIcon.cs b/Source/NotifyIconWpf/TaskbarIcon.cs index f7c0a16..e01b0c0 100644 --- a/Source/NotifyIconWpf/TaskbarIcon.cs +++ b/Source/NotifyIconWpf/TaskbarIcon.cs @@ -59,7 +59,7 @@ namespace Hardcodet.Wpf.TaskbarNotification /// An action that is being invoked if the /// fires. /// - private Action delayedTimerAction; + private Action singleClickTimerAction; /// /// A timer that is used to differentiate between single @@ -105,6 +105,8 @@ namespace Hardcodet.Wpf.TaskbarNotification } } + private double scalingFactor = double.NaN; + #endregion #region Construction @@ -216,6 +218,7 @@ namespace Hardcodet.Wpf.TaskbarNotification popup.StaysOpen = true; Point position = TrayInfo.GetTrayLocation(); + position = GetDeviceCoordinates(position); popup.HorizontalOffset = position.X - 1; popup.VerticalOffset = position.Y - 1; @@ -381,7 +384,17 @@ namespace Hardcodet.Wpf.TaskbarNotification //get mouse coordinates Point cursorPosition = new Point(); - WinApi.GetCursorPos(ref cursorPosition); + if (messageSink.Version == NotifyIconVersion.Vista) + { + //physical cursor position is supported for Vista and above + WinApi.GetPhysicalCursorPos(ref cursorPosition); + } + else + { + WinApi.GetCursorPos(ref cursorPosition); + } + + cursorPosition = GetDeviceCoordinates(cursorPosition); bool isLeftClickCommandInvoked = false; @@ -391,7 +404,7 @@ namespace Hardcodet.Wpf.TaskbarNotification if (me == MouseEvent.IconLeftMouseUp) { //show popup once we are sure it's not a double click - delayedTimerAction = () => + singleClickTimerAction = () => { LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this); ShowTrayPopup(cursorPosition); @@ -413,7 +426,7 @@ namespace Hardcodet.Wpf.TaskbarNotification if (me == MouseEvent.IconLeftMouseUp) { //show context menu once we are sure it's not a double click - delayedTimerAction = () => + singleClickTimerAction = () => { LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this); ShowContextMenu(cursorPosition); @@ -432,8 +445,10 @@ namespace Hardcodet.Wpf.TaskbarNotification if (me == MouseEvent.IconLeftMouseUp && !isLeftClickCommandInvoked) { //show context menu once we are sure it's not a double click - delayedTimerAction = - () => LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this); + singleClickTimerAction = () => + { + LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this); + }; singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite); } } @@ -650,7 +665,6 @@ namespace Hardcodet.Wpf.TaskbarNotification //open popup TrayPopupResolved.IsOpen = true; - IntPtr handle = IntPtr.Zero; if (TrayPopupResolved.Child != null) { @@ -703,7 +717,7 @@ namespace Hardcodet.Wpf.TaskbarNotification ContextMenu.IsOpen = true; IntPtr handle = IntPtr.Zero; - + //try to get a handle on the context itself HwndSource source = (HwndSource) PresentationSource.FromVisual(ContextMenu); if (source != null) @@ -830,11 +844,11 @@ namespace Hardcodet.Wpf.TaskbarNotification if (IsDisposed) return; //run action - Action action = delayedTimerAction; + Action action = singleClickTimerAction; if (action != null) { //cleanup action - delayedTimerAction = null; + singleClickTimerAction = null; //switch to UI thread this.GetDispatcher().Invoke(action); @@ -939,6 +953,35 @@ namespace Hardcodet.Wpf.TaskbarNotification #endregion + /// + /// Recalculates OS coordinates in order to support WPFs coordinate + /// system if OS scaling (DPIs) is not 100%. + /// + /// + /// + private Point GetDeviceCoordinates(Point point) + { + if (double.IsNaN(scalingFactor)) + { + //calculate scaling factor in order to support non-standard DPIs + var presentationSource = PresentationSource.FromVisual(this); + if (presentationSource == null) + { + scalingFactor = 1; + } + else + { + var transform = presentationSource.CompositionTarget.TransformToDevice; + scalingFactor = 1/transform.M11; + } + } + + //on standard DPI settings, just return the point + if(scalingFactor == 1.0) return point; + + return new Point() {X = (int) (point.X*scalingFactor), Y = (int) (point.Y*scalingFactor)}; + } + #region Dispose / Exit /// diff --git a/Source/NotifyIconWpf/Util.cs b/Source/NotifyIconWpf/Util.cs index 6334250..6c3f171 100644 --- a/Source/NotifyIconWpf/Util.cs +++ b/Source/NotifyIconWpf/Util.cs @@ -25,6 +25,7 @@ using System; using System.ComponentModel; using System.Drawing; +using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media;