From 0f7f9778c16d459a487dda0f476792991a92f816 Mon Sep 17 00:00:00 2001 From: "hemn.still" Date: Fri, 30 Jan 2015 15:16:24 +0400 Subject: [PATCH 1/3] Fix GetDeviceCoordinates. In some cases PresentationSource.FromVisual(this) may return null. --- .../NotifyIconWpf/Interop/SystemInfo.cs | 39 +++++++++++++++++++ .../Source/NotifyIconWpf/NotifyIconWpf.csproj | 1 + .../Source/NotifyIconWpf/TaskbarIcon.cs | 20 +--------- 3 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/SystemInfo.cs diff --git a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/SystemInfo.cs b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/SystemInfo.cs new file mode 100644 index 0000000..8274196 --- /dev/null +++ b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/SystemInfo.cs @@ -0,0 +1,39 @@ +using System; +using System.Windows.Interop; + +namespace Hardcodet.Wpf.TaskbarNotification.Interop +{ + public static class SystemInfo + { + private static Tuple dpiFactors; + + private static Tuple DpiFactors + { + get + { + if (dpiFactors == null) + using (var source = new HwndSource(new HwndSourceParameters())) + dpiFactors = Tuple.Create(source.CompositionTarget.TransformToDevice.M11, source.CompositionTarget.TransformToDevice.M22); + return dpiFactors; + } + } + + public static double DpiXFactor + { + get + { + var factors = DpiFactors; + return factors != null ? factors.Item1 : 1; + } + } + + public static double DpiYFactor + { + get + { + var factors = DpiFactors; + return factors != null ? factors.Item2 : 1; + } + } + } +} diff --git a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/NotifyIconWpf.csproj b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/NotifyIconWpf.csproj index 981fc5e..4e7a2af 100644 --- a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/NotifyIconWpf.csproj +++ b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/NotifyIconWpf.csproj @@ -47,6 +47,7 @@ + Code diff --git a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs index 884eedc..e59a5d5 100644 --- a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs +++ b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs @@ -962,25 +962,7 @@ namespace Hardcodet.Wpf.TaskbarNotification /// 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)}; + return new Point() { X = (int)(point.X / SystemInfo.DpiXFactor), Y = (int)(point.Y / SystemInfo.DpiYFactor) }; } #region Dispose / Exit From 111b48153ee2b8a473cafd8b7de6cdd1bd02ed07 Mon Sep 17 00:00:00 2001 From: "hemn.still" Date: Mon, 2 Feb 2015 18:45:16 +0400 Subject: [PATCH 2/3] GetTrayLocation now return with GetDeviceCoordinates. Fix in the case of multi-monitor. Add extension point CustomPopupPosition to show Balloon on custom position. AppBarInfo.WorkArea now return appbar workarea. Before it returns screen workarea. --- .../Source/NotifyIconWpf/Interop/TrayInfo.cs | 47 ++++++++++--------- .../Source/NotifyIconWpf/TaskbarIcon.cs | 25 +++++----- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/TrayInfo.cs b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/TrayInfo.cs index 468a833..1516e1c 100644 --- a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/TrayInfo.cs +++ b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/Interop/TrayInfo.cs @@ -3,6 +3,7 @@ using System; using System.Drawing; using System.Runtime.InteropServices; +using System.Windows; namespace Hardcodet.Wpf.TaskbarNotification.Interop @@ -18,6 +19,7 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop /// Tray coordinates. public static Point GetTrayLocation() { + int space = 2; var info = new AppBarInfo(); info.GetSystemTaskBarPosition(); @@ -26,31 +28,42 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop int x = 0, y = 0; if (info.Edge == AppBarInfo.ScreenEdge.Left) { - x = rcWorkArea.Left + 2; + x = rcWorkArea.Right + space; y = rcWorkArea.Bottom; } else if (info.Edge == AppBarInfo.ScreenEdge.Bottom) { x = rcWorkArea.Right; - y = rcWorkArea.Bottom; + y = rcWorkArea.Bottom - rcWorkArea.Height - space; } else if (info.Edge == AppBarInfo.ScreenEdge.Top) { x = rcWorkArea.Right; - y = rcWorkArea.Top; + y = rcWorkArea.Top + rcWorkArea.Height + space; } else if (info.Edge == AppBarInfo.ScreenEdge.Right) { - x = rcWorkArea.Right; + x = rcWorkArea.Right - rcWorkArea.Width - space; y = rcWorkArea.Bottom; } - return new Point {X = x, Y = y}; + return GetDeviceCoordinates(new Point {X = x, Y = y}); + } + + /// + /// Recalculates OS coordinates in order to support WPFs coordinate + /// system if OS scaling (DPIs) is not 100%. + /// + /// + /// + public static Point GetDeviceCoordinates(Point point) + { + return new Point() { X = (int)(point.X / SystemInfo.DpiXFactor), Y = (int)(point.Y / SystemInfo.DpiYFactor) }; } } - internal class AppBarInfo + public class AppBarInfo { [DllImport("user32.dll")] private static extern IntPtr FindWindow(String lpClassName, String lpWindowName); @@ -80,27 +93,15 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop get { return (ScreenEdge) m_data.uEdge; } } - public Rectangle WorkArea { - get - { - Int32 bResult = 0; - var rc = new RECT(); - IntPtr rawRect = Marshal.AllocHGlobal(Marshal.SizeOf(rc)); - bResult = SystemParametersInfo(SPI_GETWORKAREA, 0, rawRect, 0); - rc = (RECT) Marshal.PtrToStructure(rawRect, rc.GetType()); - - if (bResult == 1) - { - Marshal.FreeHGlobal(rawRect); - return new Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top); - } - - return new Rectangle(0, 0, 0, 0); - } + get { return GetRectangle(m_data.rc); } } + private Rectangle GetRectangle(RECT rc) + { + return new Rectangle(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top); + } public void GetPosition(string strClassName, string strWindowName) { diff --git a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs index e59a5d5..26af691 100644 --- a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs +++ b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs @@ -145,6 +145,14 @@ namespace Hardcodet.Wpf.TaskbarNotification #endregion #region Custom Balloons + public delegate Point GetCustomPopupPosition(); + + public GetCustomPopupPosition CustomPopupPosition; + + public Point GetPopupTrayPosition() + { + return TrayInfo.GetTrayLocation(); + } /// /// Shows a custom control as a tooltip in the tray location. @@ -217,8 +225,8 @@ namespace Hardcodet.Wpf.TaskbarNotification popup.Placement = PlacementMode.AbsolutePoint; popup.StaysOpen = true; - Point position = TrayInfo.GetTrayLocation(); - position = GetDeviceCoordinates(position); + + Point position = this.CustomPopupPosition != null ? this.CustomPopupPosition() : this.GetPopupTrayPosition(); popup.HorizontalOffset = position.X - 1; popup.VerticalOffset = position.Y - 1; @@ -394,7 +402,7 @@ namespace Hardcodet.Wpf.TaskbarNotification WinApi.GetCursorPos(ref cursorPosition); } - cursorPosition = GetDeviceCoordinates(cursorPosition); + cursorPosition = TrayInfo.GetDeviceCoordinates(cursorPosition); bool isLeftClickCommandInvoked = false; @@ -954,16 +962,7 @@ 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) - { - return new Point() { X = (int)(point.X / SystemInfo.DpiXFactor), Y = (int)(point.Y / SystemInfo.DpiYFactor) }; - } + #region Dispose / Exit From c2876ee7585025c0ea6cbbd78e97438ffb79e608 Mon Sep 17 00:00:00 2001 From: jr Date: Thu, 9 Jul 2015 15:20:11 +0200 Subject: [PATCH 3/3] Added "NoLeftClickDelay" TaskbarIcon dependency property, to optionally make left clicks work without delay. --- .../NotifyIconWpf/TaskbarIcon.Declarations.cs | 27 +++++++++++++++++++ .../Source/NotifyIconWpf/TaskbarIcon.cs | 14 +++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs index a12c56e..d7157d9 100644 --- a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs +++ b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.Declarations.cs @@ -828,6 +828,33 @@ namespace Hardcodet.Wpf.TaskbarNotification #endregion + #region NoLeftClickDelay dependency property + + /// + /// Set to true to make left clicks work without delay. + /// + public static readonly DependencyProperty NoLeftClickDelayProperty = + DependencyProperty.Register("NoLeftClickDelay", + typeof(bool), + typeof(TaskbarIcon), + new FrameworkPropertyMetadata(false)); + + + /// + /// A property wrapper for the + /// dependency property:
+ /// Set to true to make left clicks work without delay. + ///
+ [Category(CategoryName)] + [Description("Set to true to make left clicks work without delay.")] + public bool NoLeftClickDelay + { + get { return (bool)GetValue(NoLeftClickDelayProperty); } + set { SetValue(NoLeftClickDelayProperty, value); } + } + + #endregion + //EVENTS #region TrayLeftMouseDown diff --git a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs index 884eedc..f24b65c 100644 --- a/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs +++ b/Hardcodet.NotifyIcon.Wpf/Source/NotifyIconWpf/TaskbarIcon.cs @@ -67,6 +67,14 @@ namespace Hardcodet.Wpf.TaskbarNotification ///
private readonly Timer singleClickTimer; + /// + /// The time we should wait for a double click. + /// + private int doubleClickWaitTime + { + get { return NoLeftClickDelay ? 0 : WinApi.GetDoubleClickTime(); } + } + /// /// A timer that is used to close open balloon tooltips. /// @@ -409,7 +417,7 @@ namespace Hardcodet.Wpf.TaskbarNotification LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this); ShowTrayPopup(cursorPosition); }; - singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite); + singleClickTimer.Change(doubleClickWaitTime, Timeout.Infinite); isLeftClickCommandInvoked = true; } else @@ -431,7 +439,7 @@ namespace Hardcodet.Wpf.TaskbarNotification LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this); ShowContextMenu(cursorPosition); }; - singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite); + singleClickTimer.Change(doubleClickWaitTime, Timeout.Infinite); isLeftClickCommandInvoked = true; } else @@ -450,7 +458,7 @@ namespace Hardcodet.Wpf.TaskbarNotification { LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this); }; - singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite); + singleClickTimer.Change(doubleClickWaitTime, Timeout.Infinite); } }