mirror of
https://github.com/ckaczor/wpf-notifyicon.git
synced 2026-02-16 11:08:30 -05:00
Updating branch with imported PR from master.
This commit is contained in:
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
|
||||||
|
namespace Hardcodet.Wpf.TaskbarNotification.Interop
|
||||||
|
{
|
||||||
|
public static class SystemInfo
|
||||||
|
{
|
||||||
|
private static Tuple<double, double> dpiFactors;
|
||||||
|
|
||||||
|
private static Tuple<double, double> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
|
||||||
namespace Hardcodet.Wpf.TaskbarNotification.Interop
|
namespace Hardcodet.Wpf.TaskbarNotification.Interop
|
||||||
@@ -18,6 +19,7 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop
|
|||||||
/// <returns>Tray coordinates.</returns>
|
/// <returns>Tray coordinates.</returns>
|
||||||
public static Point GetTrayLocation()
|
public static Point GetTrayLocation()
|
||||||
{
|
{
|
||||||
|
int space = 2;
|
||||||
var info = new AppBarInfo();
|
var info = new AppBarInfo();
|
||||||
info.GetSystemTaskBarPosition();
|
info.GetSystemTaskBarPosition();
|
||||||
|
|
||||||
@@ -26,31 +28,42 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop
|
|||||||
int x = 0, y = 0;
|
int x = 0, y = 0;
|
||||||
if (info.Edge == AppBarInfo.ScreenEdge.Left)
|
if (info.Edge == AppBarInfo.ScreenEdge.Left)
|
||||||
{
|
{
|
||||||
x = rcWorkArea.Left + 2;
|
x = rcWorkArea.Right + space;
|
||||||
y = rcWorkArea.Bottom;
|
y = rcWorkArea.Bottom;
|
||||||
}
|
}
|
||||||
else if (info.Edge == AppBarInfo.ScreenEdge.Bottom)
|
else if (info.Edge == AppBarInfo.ScreenEdge.Bottom)
|
||||||
{
|
{
|
||||||
x = rcWorkArea.Right;
|
x = rcWorkArea.Right;
|
||||||
y = rcWorkArea.Bottom;
|
y = rcWorkArea.Bottom - rcWorkArea.Height - space;
|
||||||
}
|
}
|
||||||
else if (info.Edge == AppBarInfo.ScreenEdge.Top)
|
else if (info.Edge == AppBarInfo.ScreenEdge.Top)
|
||||||
{
|
{
|
||||||
x = rcWorkArea.Right;
|
x = rcWorkArea.Right;
|
||||||
y = rcWorkArea.Top;
|
y = rcWorkArea.Top + rcWorkArea.Height + space;
|
||||||
}
|
}
|
||||||
else if (info.Edge == AppBarInfo.ScreenEdge.Right)
|
else if (info.Edge == AppBarInfo.ScreenEdge.Right)
|
||||||
{
|
{
|
||||||
x = rcWorkArea.Right;
|
x = rcWorkArea.Right - rcWorkArea.Width - space;
|
||||||
y = rcWorkArea.Bottom;
|
y = rcWorkArea.Bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Point {X = x, Y = y};
|
return GetDeviceCoordinates(new Point {X = x, Y = y});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recalculates OS coordinates in order to support WPFs coordinate
|
||||||
|
/// system if OS scaling (DPIs) is not 100%.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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")]
|
[DllImport("user32.dll")]
|
||||||
private static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
|
private static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
|
||||||
@@ -80,27 +93,15 @@ namespace Hardcodet.Wpf.TaskbarNotification.Interop
|
|||||||
get { return (ScreenEdge) m_data.uEdge; }
|
get { return (ScreenEdge) m_data.uEdge; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Rectangle WorkArea
|
public Rectangle WorkArea
|
||||||
{
|
{
|
||||||
get
|
get { return GetRectangle(m_data.rc); }
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
public void GetPosition(string strClassName, string strWindowName)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="BalloonIcon.cs" />
|
<Compile Include="BalloonIcon.cs" />
|
||||||
|
<Compile Include="Interop\SystemInfo.cs" />
|
||||||
<Compile Include="Interop\TrayInfo.cs">
|
<Compile Include="Interop\TrayInfo.cs">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
@@ -861,6 +861,33 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region NoLeftClickDelay dependency property
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to true to make left clicks work without delay.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DependencyProperty NoLeftClickDelayProperty =
|
||||||
|
DependencyProperty.Register("NoLeftClickDelay",
|
||||||
|
typeof(bool),
|
||||||
|
typeof(TaskbarIcon),
|
||||||
|
new FrameworkPropertyMetadata(false));
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A property wrapper for the <see cref="NoLeftClickDelayProperty"/>
|
||||||
|
/// dependency property:<br/>
|
||||||
|
/// Set to true to make left clicks work without delay.
|
||||||
|
/// </summary>
|
||||||
|
[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
|
//EVENTS
|
||||||
|
|
||||||
#region TrayLeftMouseDown
|
#region TrayLeftMouseDown
|
||||||
|
|||||||
@@ -70,9 +70,17 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maintains opened tooltip popups.
|
/// Maintains opened tooltip popups.
|
||||||
|
/// The time we should wait for a double click.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ToolTipObserver toolTipObserver;
|
private ToolTipObserver toolTipObserver;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// </summary>
|
||||||
|
private int doubleClickWaitTime
|
||||||
|
{
|
||||||
|
get { return NoLeftClickDelay ? 0 : WinApi.GetDoubleClickTime(); }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A timer that is used to close open balloon tooltips.
|
/// A timer that is used to close open balloon tooltips.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -151,6 +159,14 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Custom Balloons
|
#region Custom Balloons
|
||||||
|
public delegate Point GetCustomPopupPosition();
|
||||||
|
|
||||||
|
public GetCustomPopupPosition CustomPopupPosition;
|
||||||
|
|
||||||
|
public Point GetPopupTrayPosition()
|
||||||
|
{
|
||||||
|
return TrayInfo.GetTrayLocation();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shows a custom control as a tooltip in the tray location.
|
/// Shows a custom control as a tooltip in the tray location.
|
||||||
@@ -223,8 +239,8 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
popup.Placement = PlacementMode.AbsolutePoint;
|
popup.Placement = PlacementMode.AbsolutePoint;
|
||||||
popup.StaysOpen = true;
|
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.HorizontalOffset = position.X - 1;
|
||||||
popup.VerticalOffset = position.Y - 1;
|
popup.VerticalOffset = position.Y - 1;
|
||||||
|
|
||||||
@@ -400,7 +416,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
WinApi.GetCursorPos(ref cursorPosition);
|
WinApi.GetCursorPos(ref cursorPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
cursorPosition = GetDeviceCoordinates(cursorPosition);
|
cursorPosition = TrayInfo.GetDeviceCoordinates(cursorPosition);
|
||||||
|
|
||||||
bool isLeftClickCommandInvoked = false;
|
bool isLeftClickCommandInvoked = false;
|
||||||
|
|
||||||
@@ -415,7 +431,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
|
LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
|
||||||
ShowTrayPopup(cursorPosition);
|
ShowTrayPopup(cursorPosition);
|
||||||
};
|
};
|
||||||
singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
|
singleClickTimer.Change(doubleClickWaitTime, Timeout.Infinite);
|
||||||
isLeftClickCommandInvoked = true;
|
isLeftClickCommandInvoked = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -437,7 +453,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
|
LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
|
||||||
ShowContextMenu(cursorPosition);
|
ShowContextMenu(cursorPosition);
|
||||||
};
|
};
|
||||||
singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
|
singleClickTimer.Change(doubleClickWaitTime, Timeout.Infinite);
|
||||||
isLeftClickCommandInvoked = true;
|
isLeftClickCommandInvoked = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -456,7 +472,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
{
|
{
|
||||||
LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
|
LeftClickCommand.ExecuteIfEnabled(LeftClickCommandParameter, LeftClickCommandTarget ?? this);
|
||||||
};
|
};
|
||||||
singleClickTimer.Change(WinApi.GetDoubleClickTime(), Timeout.Infinite);
|
singleClickTimer.Change(doubleClickWaitTime, Timeout.Infinite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,7 +836,6 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Displays a balloon tip with the specified title,
|
/// Displays a balloon tip with the specified title,
|
||||||
/// text, and a custom icon in the taskbar for the specified time period.
|
/// text, and a custom icon in the taskbar for the specified time period.
|
||||||
@@ -828,15 +843,21 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
/// <param name="title">The title to display on the balloon tip.</param>
|
/// <param name="title">The title to display on the balloon tip.</param>
|
||||||
/// <param name="message">The text to display on the balloon tip.</param>
|
/// <param name="message">The text to display on the balloon tip.</param>
|
||||||
/// <param name="customIcon">A custom icon.</param>
|
/// <param name="customIcon">A custom icon.</param>
|
||||||
|
/// <param name="largeIcon">True to allow large icons (Windows Vista and later).</param>
|
||||||
/// <exception cref="ArgumentNullException">If <paramref name="customIcon"/>
|
/// <exception cref="ArgumentNullException">If <paramref name="customIcon"/>
|
||||||
/// is a null reference.</exception>
|
/// is a null reference.</exception>
|
||||||
public void ShowBalloonTip(string title, string message, Icon customIcon)
|
public void ShowBalloonTip(string title, string message, Icon customIcon, bool largeIcon = false)
|
||||||
{
|
{
|
||||||
if (customIcon == null) throw new ArgumentNullException("customIcon");
|
if (customIcon == null) throw new ArgumentNullException("customIcon");
|
||||||
|
|
||||||
lock (this)
|
lock (this)
|
||||||
{
|
{
|
||||||
ShowBalloonTip(title, message, BalloonFlags.User, customIcon.Handle);
|
var flags = BalloonFlags.User;
|
||||||
|
|
||||||
|
if (largeIcon)
|
||||||
|
flags |= BalloonFlags.LargeIcon;
|
||||||
|
|
||||||
|
ShowBalloonTip(title, message, flags, customIcon.Handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -998,34 +1019,7 @@ namespace Hardcodet.Wpf.TaskbarNotification
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Recalculates OS coordinates in order to support WPFs coordinate
|
|
||||||
/// system if OS scaling (DPIs) is not 100%.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="point"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
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
|
#region Dispose / Exit
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user