diff --git a/HandyControl/Controls/NotifyIcon/NotifyIcon.cs b/HandyControl/Controls/NotifyIcon/NotifyIcon.cs index a2717693..c2a6713d 100644 --- a/HandyControl/Controls/NotifyIcon/NotifyIcon.cs +++ b/HandyControl/Controls/NotifyIcon/NotifyIcon.cs @@ -1,6 +1,9 @@ -using System.Windows; +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; using System.Windows.Media; -using HandyControl.Data; using HandyControl.Tools; using HandyControl.Tools.Interop; @@ -12,8 +15,6 @@ namespace HandyControl.Controls private readonly object _syncObj = new object(); - private WindowClass _windowClass; - private readonly int _id; private static int NextId; @@ -30,10 +31,24 @@ namespace HandyControl.Controls private const int WmTrayMouseMessage = NativeMethods.WM_USER + 1024; + private string _windowClassName; + + private static readonly int WmTaskbarcreated = NativeMethods.RegisterWindowMessage("TaskbarCreated"); + + private IntPtr _messageWindowHandle; + + private readonly WndProc _callback; + + private ToolTip _toolTip; + public NotifyIcon() { _id = ++NextId; - _windowClass = new WindowClass(); + _callback = Callback; + CreateToolTip(); + RegisterClass(); + OnIconChanged(); + UpdateIcon(true); } public static readonly DependencyProperty IconProperty = DependencyProperty.Register( @@ -43,7 +58,7 @@ namespace HandyControl.Controls { var ctl = (NotifyIcon) d; ctl._icon = (ImageSource) e.NewValue; - ctl.UpdateIcon(); + ctl.OnIconChanged(); } public ImageSource Icon @@ -52,7 +67,7 @@ namespace HandyControl.Controls set => SetValue(IconProperty, value); } - private void UpdateIcon() + private void OnIconChanged() { IconHandle largeIconHandle; IconHandle smallIconHandle; @@ -90,63 +105,85 @@ namespace HandyControl.Controls _currentSmallIconHandle = smallIconHandle; } - public static readonly DependencyProperty BalloonTipIconProperty = DependencyProperty.Register( - "BalloonTipIcon", typeof(ToolTipIcon), typeof(NotifyIcon), new PropertyMetadata(default(ToolTipIcon))); - - public ToolTipIcon BalloonTipIcon + private void UpdateIcon(bool showIconInTray) { - get => (ToolTipIcon) GetValue(BalloonTipIconProperty); - set => SetValue(BalloonTipIconProperty, value); - } - - public static readonly DependencyProperty BalloonTipTitleProperty = DependencyProperty.Register( - "BalloonTipTitle", typeof(string), typeof(NotifyIcon), new PropertyMetadata(default(string))); - - public string BalloonTipTitle - { - get => (string) GetValue(BalloonTipTitleProperty); - set => SetValue(BalloonTipTitleProperty, value); - } - - public static readonly DependencyProperty TextProperty = DependencyProperty.Register( - "Text", typeof(string), typeof(NotifyIcon), new PropertyMetadata(default(string))); - - public string Text - { - get => (string) GetValue(TextProperty); - set => SetValue(TextProperty, value); - } - - public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon) - { - if (timeout < 0) return; - if (string.IsNullOrEmpty(tipTitle)) return; - - if (_added) + lock (_syncObj) { if (DesignerHelper.IsInDesignMode) return; - IntSecurity.UnrestrictedWindows.Demand(); - var data = new NOTIFYICONDATA { - hWnd = _windowClass.MessageWindowHandle, + uCallbackMessage = WmTrayMouseMessage, + uFlags = NativeMethods.NIF_MESSAGE | NativeMethods.NIF_ICON | NativeMethods.NIF_TIP, + hWnd = _messageWindowHandle, uID = _id, - uFlags = NativeMethods.NIF_INFO, - uTimeoutOrVersion = timeout, - szInfoTitle = tipTitle, - szInfo = tipText + dwInfoFlags = NativeMethods.NIIF_INFO, + hIcon = _currentSmallIconHandle.CriticalGetHandle() }; - switch (tipIcon) + if (showIconInTray) { - case ToolTipIcon.Info: data.dwInfoFlags = NativeMethods.NIIF_INFO; break; - case ToolTipIcon.Warning: data.dwInfoFlags = NativeMethods.NIIF_WARNING; break; - case ToolTipIcon.Error: data.dwInfoFlags = NativeMethods.NIIF_ERROR; break; - case ToolTipIcon.None: data.dwInfoFlags = NativeMethods.NIIF_NONE; break; + if (!_added) + { + UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_ADD, data); + _added = true; + } + else + { + UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_MODIFY, data); + } + } + else if (_added) + { + UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_DELETE, data); + _added = false; } - UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_MODIFY, data); } } + + private void RegisterClass() + { + _windowClassName = $"HandyControl.Controls.NotifyIcon{Guid.NewGuid()}"; + var wndclass = new WNDCLASS + { + style = 0, + lpfnWndProc = _callback, + cbClsExtra = 0, + cbWndExtra = 0, + hInstance = IntPtr.Zero, + hIcon = IntPtr.Zero, + hCursor = IntPtr.Zero, + hbrBackground = IntPtr.Zero, + lpszMenuName = string.Empty, + lpszClassName = _windowClassName + }; + + UnsafeNativeMethods.RegisterClass(wndclass); + _messageWindowHandle = UnsafeNativeMethods.CreateWindowEx(0, _windowClassName, "", 0, 0, 0, 1, 1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + + private IntPtr Callback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) + { + if (msg == WmTaskbarcreated) + { + UpdateIcon(true); + } + + return UnsafeNativeMethods.DefWindowProc(hWnd, msg, wparam, lparam); + } + + private void CreateToolTip() + { + _toolTip = new ToolTip + { + Placement = PlacementMode.Mouse, + HasDropShadow = false, + BorderThickness = new Thickness(0), + Background = Brushes.Transparent + }; + + _toolTip.SetBinding(ContentControl.ContentProperty, new Binding(ToolTipProperty.Name) {Source = this}); + _toolTip.SetBinding(DataContextProperty, new Binding(DataContextProperty.Name) { Source = this }); + } } } \ No newline at end of file diff --git a/HandyControl/Controls/NotifyIcon/NotifyIconNativeWindow.cs b/HandyControl/Controls/NotifyIcon/NotifyIconNativeWindow.cs deleted file mode 100644 index 1bcf1a11..00000000 --- a/HandyControl/Controls/NotifyIcon/NotifyIconNativeWindow.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Runtime.InteropServices; - -namespace HandyControl.Controls -{ - internal class NotifyIconNativeWindow - { - internal NotifyIcon Reference; - - private GCHandle _rootRef; - - public void LockReference(bool locked) - { - if (locked) - { - if (!_rootRef.IsAllocated) - { - _rootRef = GCHandle.Alloc(Reference, GCHandleType.Normal); - } - } - else - { - if (_rootRef.IsAllocated) - { - _rootRef.Free(); - } - } - } - } -} \ No newline at end of file diff --git a/HandyControl/HandyControl.csproj b/HandyControl/HandyControl.csproj index b4fcfd7b..31a32fbf 100644 --- a/HandyControl/HandyControl.csproj +++ b/HandyControl/HandyControl.csproj @@ -117,7 +117,6 @@ - @@ -231,7 +230,6 @@ - @@ -324,7 +322,6 @@ - diff --git a/HandyControl/Tools/Helper/ExternDllHelper.cs b/HandyControl/Tools/Helper/ExternDllHelper.cs index 858885dc..58264b24 100644 --- a/HandyControl/Tools/Helper/ExternDllHelper.cs +++ b/HandyControl/Tools/Helper/ExternDllHelper.cs @@ -17,8 +17,6 @@ namespace HandyControl.Tools private const string User32 = "user32.dll"; - public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); - public const int E_FAIL = unchecked((int) 0x80004005); [StructLayout(LayoutKind.Sequential)] diff --git a/HandyControl/Tools/Helper/IconHelper.cs b/HandyControl/Tools/Helper/IconHelper.cs index a45b146c..afecc130 100644 --- a/HandyControl/Tools/Helper/IconHelper.cs +++ b/HandyControl/Tools/Helper/IconHelper.cs @@ -11,16 +11,18 @@ namespace HandyControl.Tools { internal static class IconHelper { - private static Size _smallIconSize; - private static Size _iconSize; - private static int _systemBitDepth; + private static Size SmallIconSize; + + private static Size IconSize; + + private static int SystemBitDepth; [SecurityCritical, SecuritySafeCritical] public static void GetIconHandlesFromImageSource(ImageSource image, out IconHandle largeIconHandle, out IconHandle smallIconHandle) { EnsureSystemMetrics(); - largeIconHandle = CreateIconHandleFromImageSource(image, _iconSize); - smallIconHandle = CreateIconHandleFromImageSource(image, _smallIconSize); + largeIconHandle = CreateIconHandleFromImageSource(image, IconSize); + smallIconHandle = CreateIconHandleFromImageSource(image, SmallIconSize); } [SecurityCritical] @@ -226,7 +228,7 @@ namespace HandyControl.Tools private static int MatchImage(BitmapFrame frame, Size size, int bpp) { - var score = 2 * MyAbs(bpp, _systemBitDepth, false) + + var score = 2 * MyAbs(bpp, SystemBitDepth, false) + MyAbs(frame.PixelWidth, (int)size.Width, true) + MyAbs(frame.PixelHeight, (int)size.Height, true); @@ -248,7 +250,7 @@ namespace HandyControl.Tools [SecurityCritical, SecuritySafeCritical] private static void EnsureSystemMetrics() { - if (_systemBitDepth == 0) + if (SystemBitDepth == 0) { var hdcDesktop = new HandleRef(null, UnsafeNativeMethods.GetDC(new HandleRef())); try @@ -266,9 +268,9 @@ namespace HandyControl.Tools var cxIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CXICON); var cyIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CYICON); - _smallIconSize = new Size(cxSmallIcon, cySmallIcon); - _iconSize = new Size(cxIcon, cyIcon); - _systemBitDepth = sysBitDepth; + SmallIconSize = new Size(cxSmallIcon, cySmallIcon); + IconSize = new Size(cxIcon, cyIcon); + SystemBitDepth = sysBitDepth; } finally { diff --git a/HandyControl/Tools/Interop/BITMAPINFO.cs b/HandyControl/Tools/Interop/BITMAPINFO.cs index 4a803564..4938fbdd 100644 --- a/HandyControl/Tools/Interop/BITMAPINFO.cs +++ b/HandyControl/Tools/Interop/BITMAPINFO.cs @@ -1,9 +1,11 @@ -using System.Runtime.InteropServices; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; using System.Security; namespace HandyControl.Tools.Interop { [StructLayout(LayoutKind.Sequential, Pack = 2)] + [SuppressMessage("ReSharper", "InconsistentNaming")] internal struct BITMAPINFO { public int bmiHeader_biSize; diff --git a/HandyControl/Tools/Interop/CommonHandles.cs b/HandyControl/Tools/Interop/CommonHandles.cs index deb2a7f5..9a61a221 100644 --- a/HandyControl/Tools/Interop/CommonHandles.cs +++ b/HandyControl/Tools/Interop/CommonHandles.cs @@ -1,5 +1,8 @@ -namespace HandyControl.Tools.Interop +using System.Diagnostics.CodeAnalysis; + +namespace HandyControl.Tools.Interop { + [SuppressMessage("ReSharper", "InconsistentNaming")] internal static class CommonHandles { public static readonly int Icon = HandleCollector.RegisterType(nameof(Icon), 20, 500); diff --git a/HandyControl/Tools/Interop/IntSecurity.cs b/HandyControl/Tools/Interop/IntSecurity.cs deleted file mode 100644 index 679ddaa4..00000000 --- a/HandyControl/Tools/Interop/IntSecurity.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Security; -using System.Security.Permissions; - -namespace HandyControl.Tools.Interop -{ - internal class IntSecurity - { - public static CodeAccessPermission UnrestrictedWindows => - new Lazy(() => AllWindows).Value; - - public static CodeAccessPermission AllWindows => - new Lazy(() => new UIPermission(UIPermissionWindow.AllWindows)).Value; - - public static CodeAccessPermission CreateAnyWindow => - new Lazy(() => SafeSubWindows).Value; - - public static CodeAccessPermission SafeSubWindows => - new Lazy(() => new UIPermission(UIPermissionWindow.SafeSubWindows)).Value; - } -} \ No newline at end of file diff --git a/HandyControl/Tools/Interop/NativeValues.cs b/HandyControl/Tools/Interop/NativeValues.cs index 628123f7..67796c7a 100644 --- a/HandyControl/Tools/Interop/NativeValues.cs +++ b/HandyControl/Tools/Interop/NativeValues.cs @@ -1,4 +1,8 @@ -namespace HandyControl.Tools.Interop +// ReSharper disable InconsistentNaming + +using System.Runtime.InteropServices; + +namespace HandyControl.Tools.Interop { internal enum SM { @@ -95,4 +99,21 @@ internal const int TRUE = 1; internal const int FALSE = 0; } + + [StructLayout(LayoutKind.Sequential)] + public class POINT + { + public int x; + public int y; + + public POINT() + { + } + + public POINT(int x, int y) + { + this.x = x; + this.y = y; + } + } } \ No newline at end of file diff --git a/HandyControl/Tools/Interop/UnsafeNativeMethods.cs b/HandyControl/Tools/Interop/UnsafeNativeMethods.cs index ed6eb2a4..6d472e5a 100644 --- a/HandyControl/Tools/Interop/UnsafeNativeMethods.cs +++ b/HandyControl/Tools/Interop/UnsafeNativeMethods.cs @@ -169,5 +169,13 @@ namespace HandyControl.Tools.Interop [DllImport(ExternDll.User32, CharSet = CharSet.Auto, SetLastError = true)] [ResourceExposure(ResourceScope.None)] public static extern short RegisterClass(WNDCLASS wc); + + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] + [ResourceExposure(ResourceScope.None)] + public static extern bool GetCursorPos([In, Out] POINT pt); } } \ No newline at end of file diff --git a/HandyControl/Tools/Interop/WNDCLASS.cs b/HandyControl/Tools/Interop/WNDCLASS.cs index 363d083f..1fef93ed 100644 --- a/HandyControl/Tools/Interop/WNDCLASS.cs +++ b/HandyControl/Tools/Interop/WNDCLASS.cs @@ -10,7 +10,7 @@ namespace HandyControl.Tools.Interop internal class WNDCLASS { public int style; - public ExternDllHelper.WndProc lpfnWndProc; + public WndProc lpfnWndProc; public int cbClsExtra = 0; public int cbWndExtra = 0; public IntPtr hInstance = IntPtr.Zero; diff --git a/HandyControl/Tools/Interop/WindowClass.cs b/HandyControl/Tools/Interop/WindowClass.cs deleted file mode 100644 index db49dc77..00000000 --- a/HandyControl/Tools/Interop/WindowClass.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Security.Permissions; - -namespace HandyControl.Tools.Interop -{ - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - internal class WindowClass - { - internal string WindowClassName; - - private static readonly int WmTaskbarcreated = NativeMethods.RegisterWindowMessage("TaskbarCreated"); - - internal IntPtr MessageWindowHandle { get; private set; } - - private readonly object _syncObj = new object(); - - private bool _added; - - public WindowClass() - { - RegisterClass(); - } - - private void RegisterClass() - { - WindowClassName = $"HandyControl.Controls.NotifyIcon{Guid.NewGuid()}"; - var wndclass = new WNDCLASS - { - style = 0, - lpfnWndProc = Callback, - cbClsExtra = 0, - cbWndExtra = 0, - hInstance = IntPtr.Zero, - hIcon = IntPtr.Zero, - hCursor = IntPtr.Zero, - hbrBackground = IntPtr.Zero, - lpszMenuName = string.Empty, - lpszClassName = WindowClassName - }; - - UnsafeNativeMethods.RegisterClass(wndclass); - MessageWindowHandle = UnsafeNativeMethods.CreateWindowEx(0, WindowClassName, "", 0, 0, 0, 1, 1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); - } - - public IntPtr Callback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) - { - if (msg == WmTaskbarcreated) - { - _added = false; - AddIcon(); - } - return IntPtr.Zero; - } - - private void AddIcon() - { - lock (_syncObj) - { - if (DesignerHelper.IsInDesignMode) return; - - //NativeMethods.NOTIFYICONDATA data = new NativeMethods.NOTIFYICONDATA(); - //data.uCallbackMessage = WM_TRAYMOUSEMESSAGE; - //data.uFlags = NativeMethods.NIF_MESSAGE; - //if (showIconInTray) - //{ - // if (window.Handle == IntPtr.Zero) - // { - // window.CreateHandle(new CreateParams()); - // } - //} - //data.hWnd = window.Handle; - //data.uID = id; - //data.hIcon = IntPtr.Zero; - //data.szTip = null; - //if (icon != null) - //{ - // data.uFlags |= NativeMethods.NIF_ICON; - // data.hIcon = icon.Handle; - //} - //data.uFlags |= NativeMethods.NIF_TIP; - //data.szTip = text; - - //if (showIconInTray && icon != null) - //{ - // if (!added) - // { - // UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_ADD, data); - // added = true; - // } - // else - // { - // UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_MODIFY, data); - // } - //} - //else if (added) - //{ - // UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_DELETE, data); - // added = false; - //} - } - } - } -} \ No newline at end of file diff --git a/HandyControlDemo/UserControl/Controls/NotifyIconDemoCtl.xaml b/HandyControlDemo/UserControl/Controls/NotifyIconDemoCtl.xaml index dc1085aa..6f055fe8 100644 --- a/HandyControlDemo/UserControl/Controls/NotifyIconDemoCtl.xaml +++ b/HandyControlDemo/UserControl/Controls/NotifyIconDemoCtl.xaml @@ -1,8 +1,9 @@  - +