From a8aa1a45253671f51cd8361550b2f46386ef61fc Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Tue, 14 May 2019 23:03:14 +0800 Subject: [PATCH] add ColorDropper --- .../Controls/ColorPicker/ColorDropper.cs | 66 ++++++++++++++++++- .../Controls/ColorPicker/ColorPicker.cs | 23 ++----- .../Data/Args/MouseHookEventArgs.cs | 12 ++++ .../Data/Enum/MouseHookMessageType.cs | 12 ++++ .../HandyControl_Shared.projitems | 3 + .../Tools/Hook/MouseHook.cs | 49 ++++++++++++-- .../Tools/Interop/UnsafeNativeMethods.cs | 40 +++++++++++ 7 files changed, 180 insertions(+), 25 deletions(-) create mode 100644 src/Shared/HandyControl_Shared/Data/Args/MouseHookEventArgs.cs create mode 100644 src/Shared/HandyControl_Shared/Data/Enum/MouseHookMessageType.cs diff --git a/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorDropper.cs b/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorDropper.cs index 9c8d5459..4b6f48f4 100644 --- a/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorDropper.cs +++ b/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorDropper.cs @@ -1,9 +1,69 @@ -using System.Windows.Controls; +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using HandyControl.Data; +using HandyControl.Tools; +using HandyControl.Tools.Interop; namespace HandyControl.Controls { - public class ColorDropper : Control + internal class ColorDropper : Control { - + private Cursor _dropperCursor; + + private readonly ColorPicker _colorPicker; + + public ColorDropper(ColorPicker colorPicker) + { + _colorPicker = colorPicker; + } + + public void Update(bool isShow) + { + if (isShow) + { + if (_dropperCursor == null) + { + var info = Application.GetResourceStream( + new Uri("pack://application:,,,/HandyControl;Component/Resources/dropper.cur")); + if (info != null) + { + _dropperCursor = new Cursor(info.Stream); + } + } + + if (_dropperCursor == null) return; + + Mouse.OverrideCursor = _dropperCursor; + MouseHook.Start(); + MouseHook.StatusChanged += MouseHook_StatusChanged; + } + else + { + Mouse.OverrideCursor = Cursors.Arrow; + } + } + + private void MouseHook_StatusChanged(object sender, MouseHookEventArgs e) + { + if (!_colorPicker.IsMouseOver && e.Message == MouseHookMessageType.LeftButtonDown) + { + var brush = new SolidColorBrush(GetColorAt(e.Point.X, e.Point.Y)); + _colorPicker.SelectedBrush = brush; + //MouseHook.Stop(); + //MouseHook.StatusChanged -= MouseHook_StatusChanged; + } + } + + public static Color GetColorAt(int x, int y) + { + var desk = UnsafeNativeMethods.GetDesktopWindow(); + var dc = UnsafeNativeMethods.GetWindowDC(desk); + var a = (int)UnsafeNativeMethods.GetPixel(dc, x, y); + UnsafeNativeMethods.ReleaseDC(desk, dc); + return Color.FromArgb(255, (byte) ((a >> 0) & 0xff), (byte) ((a >> 8) & 0xff), (byte) ((a >> 16) & 0xff)); + } } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorPicker.cs b/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorPicker.cs index ef1a39f1..695e0566 100644 --- a/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorPicker.cs +++ b/src/Shared/HandyControl_Shared/Controls/ColorPicker/ColorPicker.cs @@ -41,7 +41,7 @@ namespace HandyControl.Controls #region Data - private Cursor _dropperCursor; + private ColorDropper _colorDropper; private ToggleButton _toggleButtonDropper; @@ -696,25 +696,12 @@ namespace HandyControl.Controls private void ToggleButtonDropper_Click(object sender, RoutedEventArgs e) { - if (_toggleButtonDropper.IsChecked == true) + if (_colorDropper == null) { - if (_dropperCursor == null) - { - var info = Application.GetResourceStream(new Uri("pack://application:,,,/HandyControl;Component/Resources/dropper.cur")); - if (info != null) - { - _dropperCursor = new Cursor(info.Stream); - } - } - - if (_dropperCursor == null) return; - - Mouse.OverrideCursor = _dropperCursor; - } - else - { - Mouse.OverrideCursor = Cursors.Arrow; + _colorDropper = new ColorDropper(this); } + // ReSharper disable once PossibleInvalidOperationException + _colorDropper.Update(_toggleButtonDropper.IsChecked.Value); } public void Dispose() => System.Windows.Window.GetWindow(this)?.Close(); diff --git a/src/Shared/HandyControl_Shared/Data/Args/MouseHookEventArgs.cs b/src/Shared/HandyControl_Shared/Data/Args/MouseHookEventArgs.cs new file mode 100644 index 00000000..9c6b03fd --- /dev/null +++ b/src/Shared/HandyControl_Shared/Data/Args/MouseHookEventArgs.cs @@ -0,0 +1,12 @@ +using System; +using HandyControl.Tools.Interop; + +namespace HandyControl.Data +{ + internal class MouseHookEventArgs : EventArgs + { + public MouseHookMessageType Message { get; set; } + + public NativeMethods.POINT Point { get; set; } + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Data/Enum/MouseHookMessageType.cs b/src/Shared/HandyControl_Shared/Data/Enum/MouseHookMessageType.cs new file mode 100644 index 00000000..4eeb302d --- /dev/null +++ b/src/Shared/HandyControl_Shared/Data/Enum/MouseHookMessageType.cs @@ -0,0 +1,12 @@ +namespace HandyControl.Data +{ + public enum MouseHookMessageType + { + LeftButtonDown = 0x0201, + LeftButtonUp = 0x0202, + MouseMove = 0x0200, + MouseWheel = 0x020A, + RightButtonDown = 0x0204, + RightButtonUp = 0x0205 + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index 57aa662b..39535ac9 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -113,6 +113,7 @@ + @@ -136,6 +137,7 @@ + @@ -244,6 +246,7 @@ + diff --git a/src/Shared/HandyControl_Shared/Tools/Hook/MouseHook.cs b/src/Shared/HandyControl_Shared/Tools/Hook/MouseHook.cs index deab86c1..e7e2d0d1 100644 --- a/src/Shared/HandyControl_Shared/Tools/Hook/MouseHook.cs +++ b/src/Shared/HandyControl_Shared/Tools/Hook/MouseHook.cs @@ -1,7 +1,48 @@ -namespace HandyControl.Tools -{ - public class MouseHook - { +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using HandyControl.Data; +using HandyControl.Tools.Interop; +namespace HandyControl.Tools +{ + internal class MouseHook + { + public static event EventHandler StatusChanged; + + private static IntPtr HookId = IntPtr.Zero; + + private static readonly UnsafeNativeMethods.HookProc Proc = HookCallback; + + public static void Start() => HookId = SetHook(Proc); + + public static void Stop() => UnsafeNativeMethods.UnhookWindowsHookEx(HookId); + + private static IntPtr SetHook(UnsafeNativeMethods.HookProc proc) + { + using (var curProcess = Process.GetCurrentProcess()) + using (var curModule = curProcess.MainModule) + { + if (curModule != null) + { + return UnsafeNativeMethods.SetWindowsHookEx((int) UnsafeNativeMethods.HookType.WH_MOUSE_LL, proc, + UnsafeNativeMethods.GetModuleHandle(curModule.ModuleName), 0); + } + return IntPtr.Zero; + } + } + + private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) + { + if (nCode < 0) return UnsafeNativeMethods.CallNextHookEx(HookId, nCode, wParam, lParam); + var hookStruct = (UnsafeNativeMethods.MOUSEHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(UnsafeNativeMethods.MOUSEHOOKSTRUCT)); + StatusChanged?.Invoke(null, new MouseHookEventArgs + { + Message = (MouseHookMessageType)wParam, + Point = new NativeMethods.POINT(hookStruct.pt.X, hookStruct.pt.Y) + }); + + return UnsafeNativeMethods.CallNextHookEx(HookId, nCode, wParam, lParam); + } } } diff --git a/src/Shared/HandyControl_Shared/Tools/Interop/UnsafeNativeMethods.cs b/src/Shared/HandyControl_Shared/Tools/Interop/UnsafeNativeMethods.cs index a88f2d1b..1c893525 100644 --- a/src/Shared/HandyControl_Shared/Tools/Interop/UnsafeNativeMethods.cs +++ b/src/Shared/HandyControl_Shared/Tools/Interop/UnsafeNativeMethods.cs @@ -174,5 +174,45 @@ namespace HandyControl.Tools.Interop [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)] public static extern bool SetForegroundWindow(IntPtr hWnd); + + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); + + [DllImport(ExternDll.Kernel32, CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr GetModuleHandle(string lpModuleName); + + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool UnhookWindowsHookEx(IntPtr hhk); + + public enum HookType + { + WH_MOUSE_LL = 14, + } + + [StructLayout(LayoutKind.Sequential)] + public struct MOUSEHOOKSTRUCT + { + public NativeMethods.POINT pt; + public IntPtr hwnd; + public uint wHitTestCode; + public IntPtr dwExtraInfo; + } + + public delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam); + + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId); + + [DllImport(ExternDll.User32, SetLastError = true)] + public static extern IntPtr GetDesktopWindow(); + + [DllImport(ExternDll.User32, SetLastError = true)] + public static extern IntPtr GetWindowDC(IntPtr window); + + [DllImport(ExternDll.Gdi32, SetLastError = true)] + public static extern uint GetPixel(IntPtr dc, int x, int y); + + [DllImport(ExternDll.User32, SetLastError = true)] + public static extern int ReleaseDC(IntPtr window, IntPtr dc); } } \ No newline at end of file