improved notifyicon
added mouseenter and mouseleave events
This commit is contained in:
1 parent
9aa6df87cb
commit
3aa0f09d39
6 files changed
+388
-16
No files matched your search
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
@@ -14,6 +16,8 @@ namespace HandyControl.Controls
|
||||
{
|
||||
public class NotifyIcon : FrameworkElement, IDisposable
|
||||
{
|
||||
private bool _isMouseOver;
|
||||
|
||||
private bool _added;
|
||||
|
||||
private readonly object _syncObj = new object();
|
||||
@@ -44,7 +48,9 @@ namespace HandyControl.Controls
|
||||
|
||||
private bool _doubleClick;
|
||||
|
||||
private DispatcherTimer _dispatcherTimer;
|
||||
private DispatcherTimer _dispatcherTimerBlink;
|
||||
|
||||
private DispatcherTimer _dispatcherTimerPos;
|
||||
|
||||
private bool _isTransparent;
|
||||
|
||||
@@ -87,6 +93,12 @@ namespace HandyControl.Controls
|
||||
OnIconChanged();
|
||||
UpdateIcon(true);
|
||||
}
|
||||
|
||||
_dispatcherTimerPos = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(200)
|
||||
};
|
||||
_dispatcherTimerPos.Tick += DispatcherTimerPos_Tick;
|
||||
};
|
||||
|
||||
if (Application.Current != null) Application.Current.Exit += (s, e) => Dispose();
|
||||
@@ -137,9 +149,9 @@ namespace HandyControl.Controls
|
||||
private static void OnBlinkIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var ctl = (NotifyIcon)d;
|
||||
if (ctl._dispatcherTimer != null)
|
||||
if (ctl._dispatcherTimerBlink != null)
|
||||
{
|
||||
ctl._dispatcherTimer.Interval = (TimeSpan) e.NewValue;
|
||||
ctl._dispatcherTimerBlink.Interval = (TimeSpan) e.NewValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,30 +170,175 @@ namespace HandyControl.Controls
|
||||
if (ctl.Visibility != Visibility.Visible) return;
|
||||
if ((bool) e.NewValue)
|
||||
{
|
||||
if (ctl._dispatcherTimer == null)
|
||||
if (ctl._dispatcherTimerBlink == null)
|
||||
{
|
||||
ctl._dispatcherTimer = new DispatcherTimer
|
||||
ctl._dispatcherTimerBlink = new DispatcherTimer
|
||||
{
|
||||
Interval = ctl.BlinkInterval
|
||||
};
|
||||
ctl._dispatcherTimer.Tick += ctl.DispatcherTimer_Tick;
|
||||
ctl._dispatcherTimerBlink.Tick += ctl.DispatcherTimerBlinkTick;
|
||||
}
|
||||
ctl._dispatcherTimer.Start();
|
||||
ctl._dispatcherTimerBlink.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
ctl._dispatcherTimer?.Stop();
|
||||
ctl._dispatcherTimer = null;
|
||||
ctl._dispatcherTimerBlink?.Stop();
|
||||
ctl._dispatcherTimerBlink = null;
|
||||
ctl.UpdateIcon(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void DispatcherTimer_Tick(object sender, EventArgs e)
|
||||
private void DispatcherTimerBlinkTick(object sender, EventArgs e)
|
||||
{
|
||||
if (Visibility != Visibility.Visible || _iconCurrentHandle == IntPtr.Zero) return;
|
||||
UpdateIcon(true, !_isTransparent);
|
||||
}
|
||||
|
||||
private bool CheckMouseIsEnter()
|
||||
{
|
||||
var isTrue = FindNotifyIcon(out var rectNotify);
|
||||
if (!isTrue) return false;
|
||||
NativeMethods.GetCursorPos(out var point);
|
||||
if (point.X >= rectNotify.Left && point.X <= rectNotify.Right &&
|
||||
point.Y >= rectNotify.Top && point.Y <= rectNotify.Bottom)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DispatcherTimerPos_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if (CheckMouseIsEnter())
|
||||
{
|
||||
if (!_isMouseOver)
|
||||
{
|
||||
_isMouseOver = true;
|
||||
RaiseEvent(new MouseEventArgs(Mouse.PrimaryDevice, Environment.TickCount)
|
||||
{
|
||||
RoutedEvent = MouseEnterEvent
|
||||
});
|
||||
_dispatcherTimerPos.Interval = TimeSpan.FromMilliseconds(500);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_dispatcherTimerPos.Stop();
|
||||
_isMouseOver = false;
|
||||
RaiseEvent(new MouseEventArgs(Mouse.PrimaryDevice, Environment.TickCount)
|
||||
{
|
||||
RoutedEvent = MouseLeaveEvent
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//referenced from http://www.cnblogs.com/sczmzx/p/5158127.html
|
||||
private IntPtr FindTrayToolbarWindow()
|
||||
{
|
||||
var hWnd = NativeMethods.FindWindow("Shell_TrayWnd", null);
|
||||
if (hWnd != IntPtr.Zero)
|
||||
{
|
||||
hWnd = NativeMethods.FindWindowEx(hWnd, IntPtr.Zero, "TrayNotifyWnd", null);
|
||||
if (hWnd != IntPtr.Zero)
|
||||
{
|
||||
|
||||
hWnd = NativeMethods.FindWindowEx(hWnd, IntPtr.Zero, "SysPager", null);
|
||||
if (hWnd != IntPtr.Zero)
|
||||
{
|
||||
hWnd = NativeMethods.FindWindowEx(hWnd, IntPtr.Zero, "ToolbarWindow32", null);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return hWnd;
|
||||
}
|
||||
|
||||
//referenced from http://www.cnblogs.com/sczmzx/p/5158127.html
|
||||
private IntPtr FindTrayToolbarOverFlowWindow()
|
||||
{
|
||||
var hWnd = NativeMethods.FindWindow("NotifyIconOverflowWindow", null);
|
||||
if (hWnd != IntPtr.Zero)
|
||||
{
|
||||
hWnd = NativeMethods.FindWindowEx(hWnd, IntPtr.Zero, "ToolbarWindow32", null);
|
||||
}
|
||||
return hWnd;
|
||||
}
|
||||
|
||||
private bool FindNotifyIcon(out NativeMethods.RECT rect)
|
||||
{
|
||||
var rectNotify = new NativeMethods.RECT();
|
||||
var hTrayWnd = FindTrayToolbarWindow();
|
||||
var isTrue = FindNotifyIcon(hTrayWnd, ref rectNotify);
|
||||
if (!isTrue)
|
||||
{
|
||||
hTrayWnd = FindTrayToolbarOverFlowWindow();
|
||||
isTrue = FindNotifyIcon(hTrayWnd, ref rectNotify);
|
||||
}
|
||||
rect = rectNotify;
|
||||
return isTrue;
|
||||
}
|
||||
|
||||
//referenced from http://www.cnblogs.com/sczmzx/p/5158127.html
|
||||
private bool FindNotifyIcon(IntPtr hTrayWnd, ref NativeMethods.RECT rectNotify)
|
||||
{
|
||||
NativeMethods.GetWindowRect(hTrayWnd, out var rectTray);
|
||||
var count = (int)NativeMethods.SendMessage(hTrayWnd, NativeMethods.TB_BUTTONCOUNT, 0, IntPtr.Zero);
|
||||
|
||||
var isFind = false;
|
||||
if (count > 0)
|
||||
{
|
||||
NativeMethods.GetWindowThreadProcessId(hTrayWnd, out var trayPid);
|
||||
var hProcess = NativeMethods.OpenProcess(NativeMethods.ProcessAccess.VMOperation | NativeMethods.ProcessAccess.VMRead | NativeMethods.ProcessAccess.VMWrite, false, trayPid);
|
||||
var address = NativeMethods.VirtualAllocEx(hProcess, IntPtr.Zero, 1024, NativeMethods.AllocationType.Commit, NativeMethods.MemoryProtection.ReadWrite);
|
||||
|
||||
var btnData = new NativeMethods.TBBUTTON();
|
||||
var trayData = new NativeMethods.TRAYDATA();
|
||||
var handel = Process.GetCurrentProcess().Id;
|
||||
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
NativeMethods.SendMessage(hTrayWnd, NativeMethods.TB_GETBUTTON, i, address);
|
||||
var isTrue = NativeMethods.ReadProcessMemory(hProcess, address, out btnData, Marshal.SizeOf(btnData), out _);
|
||||
if (!isTrue) continue;
|
||||
if (btnData.dwData == IntPtr.Zero)
|
||||
{
|
||||
btnData.dwData = btnData.iString;
|
||||
}
|
||||
NativeMethods.ReadProcessMemory(hProcess, btnData.dwData, out trayData, Marshal.SizeOf(trayData), out _);
|
||||
NativeMethods.GetWindowThreadProcessId(trayData.hwnd, out var dwProcessId);
|
||||
if (dwProcessId == (uint)handel)
|
||||
{
|
||||
var rect = new NativeMethods.RECT();
|
||||
var lngRect = NativeMethods.VirtualAllocEx(hProcess, IntPtr.Zero, Marshal.SizeOf(typeof(Rect)), NativeMethods.AllocationType.Commit, NativeMethods.MemoryProtection.ReadWrite);
|
||||
NativeMethods.SendMessage(hTrayWnd, NativeMethods.TB_GETITEMRECT, i, lngRect);
|
||||
NativeMethods.ReadProcessMemory(hProcess, lngRect, out rect, Marshal.SizeOf(rect), out _);
|
||||
|
||||
NativeMethods.VirtualFreeEx(hProcess, lngRect, Marshal.SizeOf(rect), NativeMethods.FreeType.Decommit);
|
||||
NativeMethods.VirtualFreeEx(hProcess, lngRect, 0, NativeMethods.FreeType.Release);
|
||||
|
||||
var left = rectTray.Left + rect.Left;
|
||||
var top = rectTray.Top + rect.Top;
|
||||
var botton = rectTray.Top + rect.Bottom;
|
||||
var right = rectTray.Left + rect.Right;
|
||||
rectNotify = new NativeMethods.RECT
|
||||
{
|
||||
Left = left,
|
||||
Right = right,
|
||||
Top = top,
|
||||
Bottom = botton
|
||||
};
|
||||
isFind = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
NativeMethods.VirtualFreeEx(hProcess, address, 0x4096, NativeMethods.FreeType.Decommit);
|
||||
NativeMethods.VirtualFreeEx(hProcess, address, 0, NativeMethods.FreeType.Release);
|
||||
NativeMethods.CloseHandle(hProcess);
|
||||
}
|
||||
return isFind;
|
||||
}
|
||||
|
||||
public bool IsBlink
|
||||
{
|
||||
get => (bool) GetValue(IsBlinkProperty);
|
||||
@@ -289,6 +446,13 @@ namespace HandyControl.Controls
|
||||
ShowContextMenu();
|
||||
WmMouseUp(MouseButton.Right);
|
||||
break;
|
||||
case NativeMethods.WM_MOUSEMOVE:
|
||||
if (!_dispatcherTimerPos.IsEnabled)
|
||||
{
|
||||
_dispatcherTimerPos.Interval = TimeSpan.FromMilliseconds(200);
|
||||
_dispatcherTimerPos.Start();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -390,9 +554,9 @@ namespace HandyControl.Controls
|
||||
if (_isDisposed) return;
|
||||
if (disposing)
|
||||
{
|
||||
if (_dispatcherTimer != null && IsBlink)
|
||||
if (_dispatcherTimerBlink != null && IsBlink)
|
||||
{
|
||||
_dispatcherTimer.Stop();
|
||||
_dispatcherTimerBlink.Stop();
|
||||
}
|
||||
UpdateIcon(false);
|
||||
}
|
||||
|
||||
@@ -41,10 +41,165 @@ namespace HandyControl.Tools.Interop
|
||||
WM_MBUTTONDOWN = 0x0207,
|
||||
WM_MBUTTONUP = 0x0208,
|
||||
WM_MBUTTONDBLCLK = 0x0209,
|
||||
WM_USER = 0x0400;
|
||||
WM_USER = 0x0400,
|
||||
TB_GETBUTTON = WM_USER + 23,
|
||||
TB_BUTTONCOUNT = WM_USER + 24,
|
||||
TB_GETITEMRECT = WM_USER + 29,
|
||||
STANDARD_RIGHTS_REQUIRED = 0x000F0000,
|
||||
SYNCHRONIZE = 0x00100000,
|
||||
PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF,
|
||||
MEM_COMMIT = 0x1000,
|
||||
MEM_RELEASE = 0x8000,
|
||||
PAGE_READWRITE = 0x04,
|
||||
TBSTATE_HIDDEN = 0x08;
|
||||
|
||||
[Flags]
|
||||
public enum ProcessAccess
|
||||
{
|
||||
AllAccess = CreateThread | DuplicateHandle | QueryInformation | SetInformation | Terminate | VMOperation | VMRead | VMWrite | Synchronize,
|
||||
CreateThread = 0x2,
|
||||
DuplicateHandle = 0x40,
|
||||
QueryInformation = 0x400,
|
||||
SetInformation = 0x200,
|
||||
Terminate = 0x1,
|
||||
VMOperation = 0x8,
|
||||
VMRead = 0x10,
|
||||
VMWrite = 0x20,
|
||||
Synchronize = 0x100000
|
||||
}
|
||||
|
||||
[Serializable, StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct RECT
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
|
||||
public RECT(int left, int top, int right, int bottom)
|
||||
{
|
||||
Left = left;
|
||||
Top = top;
|
||||
Right = right;
|
||||
Bottom = bottom;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct TBBUTTON
|
||||
{
|
||||
public int iBitmap;
|
||||
public int idCommand;
|
||||
public IntPtr fsStateStylePadding;
|
||||
public IntPtr dwData;
|
||||
public IntPtr iString;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum AllocationType
|
||||
{
|
||||
Commit = 0x1000,
|
||||
Reserve = 0x2000,
|
||||
Decommit = 0x4000,
|
||||
Release = 0x8000,
|
||||
Reset = 0x80000,
|
||||
Physical = 0x400000,
|
||||
TopDown = 0x100000,
|
||||
WriteWatch = 0x200000,
|
||||
LargePages = 0x20000000
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum MemoryProtection
|
||||
{
|
||||
Execute = 0x10,
|
||||
ExecuteRead = 0x20,
|
||||
ExecuteReadWrite = 0x40,
|
||||
ExecuteWriteCopy = 0x80,
|
||||
NoAccess = 0x01,
|
||||
ReadOnly = 0x02,
|
||||
ReadWrite = 0x04,
|
||||
WriteCopy = 0x08,
|
||||
GuardModifierflag = 0x100,
|
||||
NoCacheModifierflag = 0x200,
|
||||
WriteCombineModifierflag = 0x400
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct TRAYDATA
|
||||
{
|
||||
public IntPtr hwnd;
|
||||
public uint uID;
|
||||
public uint uCallbackMessage;
|
||||
public uint bReserved0;
|
||||
public uint bReserved1;
|
||||
public IntPtr hIcon;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum FreeType
|
||||
{
|
||||
Decommit = 0x4000,
|
||||
Release = 0x8000,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct POINT
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public POINT(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
public static extern int RegisterWindowMessage(string msg);
|
||||
|
||||
[DllImport(ExternDll.User32, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, ref RECT lpPoints, uint cPoints);
|
||||
|
||||
[DllImport(ExternDll.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, out TBBUTTON lpBuffer, int dwSize, out int lpNumberOfBytesRead);
|
||||
|
||||
[DllImport(ExternDll.Kernel32, SetLastError = true)]
|
||||
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, out RECT lpBuffer, int dwSize, out int lpNumberOfBytesRead);
|
||||
|
||||
[DllImport(ExternDll.Kernel32, SetLastError = true)]
|
||||
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, out TRAYDATA lpBuffer, int dwSize, out int lpNumberOfBytesRead);
|
||||
|
||||
[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
|
||||
public static extern uint SendMessage(IntPtr hWnd, uint Msg, uint wParam, IntPtr lParam);
|
||||
|
||||
[DllImport(ExternDll.User32, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
[DllImport(ExternDll.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr OpenProcess(ProcessAccess dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);
|
||||
|
||||
[DllImport(ExternDll.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
|
||||
|
||||
[DllImport(ExternDll.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern int CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport(ExternDll.Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, FreeType dwFreeType);
|
||||
|
||||
[DllImport(ExternDll.User32, SetLastError = true)]
|
||||
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
|
||||
[DllImport(ExternDll.User32, SetLastError = true)]
|
||||
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
|
||||
|
||||
[DllImport(ExternDll.User32)]
|
||||
public static extern int GetWindowRect(IntPtr hwnd, out RECT lpRect);
|
||||
|
||||
[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
|
||||
public static extern bool GetCursorPos(out POINT pt);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user