diff --git a/src/Shared/HandyControlDemo_Shared/App.xaml.cs b/src/Shared/HandyControlDemo_Shared/App.xaml.cs index b597e60c..3f67d7c5 100644 --- a/src/Shared/HandyControlDemo_Shared/App.xaml.cs +++ b/src/Shared/HandyControlDemo_Shared/App.xaml.cs @@ -1,8 +1,5 @@ using System; -using System.Globalization; -using System.Threading; using System.Windows; -using HandyControl.Controls; using HandyControl.Data; using HandyControl.Tools; using HandyControlDemo.Data; diff --git a/src/Shared/HandyControlDemo_Shared/MainWindow.xaml b/src/Shared/HandyControlDemo_Shared/MainWindow.xaml index fabf5ef1..cde47eb6 100644 --- a/src/Shared/HandyControlDemo_Shared/MainWindow.xaml +++ b/src/Shared/HandyControlDemo_Shared/MainWindow.xaml @@ -23,6 +23,10 @@ hc:Dialog.Token="{x:Static data:MessageToken.MainWindow}" d:DesignHeight="800" d:DesignWidth="1400"> + + + + diff --git a/src/Shared/HandyControlDemo_Shared/MainWindow.xaml.cs b/src/Shared/HandyControlDemo_Shared/MainWindow.xaml.cs index ec24646f..5b5db38e 100644 --- a/src/Shared/HandyControlDemo_Shared/MainWindow.xaml.cs +++ b/src/Shared/HandyControlDemo_Shared/MainWindow.xaml.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using GalaSoft.MvvmLight.Messaging; using HandyControl.Controls; +using HandyControl.Tools; using HandyControlDemo.Data; using HandyControlDemo.Tools; @@ -21,6 +22,8 @@ namespace HandyControlDemo Messenger.Default.Send(null, MessageToken.ClearLeftSelected); Messenger.Default.Send(true, MessageToken.FullSwitch); Messenger.Default.Send(AssemblyHelper.CreateInternalInstance($"UserControl.{MessageToken.OverView}"), MessageToken.LoadShowContent); + + GlobalShortcut.Init(this); } protected override void OnClosing(CancelEventArgs e) diff --git a/src/Shared/HandyControlDemo_Shared/ViewModel/Main/MainViewModel.cs b/src/Shared/HandyControlDemo_Shared/ViewModel/Main/MainViewModel.cs index c2d992cb..5b3c1691 100644 --- a/src/Shared/HandyControlDemo_Shared/ViewModel/Main/MainViewModel.cs +++ b/src/Shared/HandyControlDemo_Shared/ViewModel/Main/MainViewModel.cs @@ -6,6 +6,7 @@ using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight.CommandWpf; #endif using GalaSoft.MvvmLight.Messaging; +using HandyControl.Controls; using HandyControlDemo.Data; using HandyControlDemo.Service; using HandyControlDemo.Tools; @@ -98,9 +99,15 @@ namespace HandyControlDemo.ViewModel public RelayCommand OpenOverviewCmd => new Lazy(() => new RelayCommand(OpenOverview)).Value; -#endregion + public RelayCommand GlobalShortcutInfoCmd => new Lazy(() => + new RelayCommand(() => Growl.Info("Global Shortcut Info"))).Value; -#region 方法 + public RelayCommand GlobalShortcutWarningCmd => new Lazy(() => + new RelayCommand(() => Growl.Warning("Global Shortcut Warning"))).Value; + + #endregion + + #region 方法 /// /// 切换例子 diff --git a/src/Shared/HandyControl_Shared/Data/Args/KeyboardHookEventArgs.cs b/src/Shared/HandyControl_Shared/Data/Args/KeyboardHookEventArgs.cs index 3d3eb0b3..717c6384 100644 --- a/src/Shared/HandyControl_Shared/Data/Args/KeyboardHookEventArgs.cs +++ b/src/Shared/HandyControl_Shared/Data/Args/KeyboardHookEventArgs.cs @@ -5,10 +5,13 @@ namespace HandyControl.Data { internal class KeyboardHookEventArgs : EventArgs { + public bool IsSystemKey { get; } + public Key Key { get; } - public KeyboardHookEventArgs(int virtualKey) + public KeyboardHookEventArgs(int virtualKey, bool isSystemKey) { + IsSystemKey = isSystemKey; Key = KeyInterop.KeyFromVirtualKey(virtualKey); } } diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index 0b15d2bc..1c6a298b 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -19,6 +19,7 @@ + diff --git a/src/Shared/HandyControl_Shared/Tools/GlobalShortcut.cs b/src/Shared/HandyControl_Shared/Tools/GlobalShortcut.cs new file mode 100644 index 00000000..ce248ba5 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Tools/GlobalShortcut.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.Windows; +using System.Windows.Input; +using HandyControl.Data; + +namespace HandyControl.Tools +{ + public class GlobalShortcut + { + private static readonly Dictionary CommandDic = new Dictionary(); + + static GlobalShortcut() + { + KeyboardHook.KeyDown += KeyboardHook_KeyDown; + } + + private static void KeyboardHook_KeyDown(object sender, KeyboardHookEventArgs e) => HitTest(e.Key); + + private static void HitTest(Key key) + { + if (IsModifierKey(key)) return; + + var modifierKeys = Keyboard.Modifiers; + var keyStr = modifierKeys != ModifierKeys.None ? $"{modifierKeys.ToString()}, {key.ToString()}" : key.ToString(); + ExecuteCommand(keyStr); + } + + private static void ExecuteCommand(string key) + { + if (string.IsNullOrEmpty(key)) return; + if (CommandDic.ContainsKey(key)) + { + var keyBinding = CommandDic[key]; + var command = keyBinding.Command; + if (command != null && command.CanExecute(keyBinding.CommandParameter)) + { + command.Execute(keyBinding.CommandParameter); + } + } + } + + private static bool IsModifierKey(Key key) + { + return key == Key.LeftCtrl || key == Key.LeftAlt || key == Key.LeftShift || key == Key.LWin || + key == Key.RightCtrl || key == Key.RightAlt || key == Key.RightShift || key == Key.RWin; + } + + public static void Init(DependencyObject host) + { + CommandDic.Clear(); + + if (host == null) return; + + var keyBindings = GetKeyBindings(host); + if (keyBindings == null || keyBindings.Count == 0) return; + + foreach (KeyBinding item in keyBindings) + { + if (item.Key == Key.None) + { + continue; + } + + if (item.Modifiers == ModifierKeys.None) + { + CommandDic[item.Key.ToString()] = item; + } + else + { + var keyStr = $"{item.Modifiers.ToString()}, {item.Key.ToString()}"; + CommandDic[keyStr] = item; + } + } + + KeyboardHook.Start(); + } + + public static readonly DependencyProperty KeyBindingsProperty = DependencyProperty.RegisterAttached( + "KeyBindings", typeof(InputBindingCollection), typeof(GlobalShortcut), new PropertyMetadata(new InputBindingCollection())); + + public static void SetKeyBindings(DependencyObject element, InputBindingCollection value) + => element.SetValue(KeyBindingsProperty, value); + + public static InputBindingCollection GetKeyBindings(DependencyObject element) + => (InputBindingCollection) element.GetValue(KeyBindingsProperty); + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Tools/Hook/KeyboardHook.cs b/src/Shared/HandyControl_Shared/Tools/Hook/KeyboardHook.cs index 0bd562fd..32bb8dcf 100644 --- a/src/Shared/HandyControl_Shared/Tools/Hook/KeyboardHook.cs +++ b/src/Shared/HandyControl_Shared/Tools/Hook/KeyboardHook.cs @@ -16,7 +16,24 @@ namespace HandyControl.Tools private static readonly UnsafeNativeMethods.HookProc Proc = HookCallback; - public static void Start() => HookId = SetHook(Proc); + private static int VirtualKey; + + private static readonly IntPtr KeyDownIntPtr = (IntPtr)NativeMethods.WM_KEYDOWN; + + private static readonly IntPtr KeyUpIntPtr = (IntPtr)NativeMethods.WM_KEYUP; + + private static readonly IntPtr SyskeyDownIntPtr = (IntPtr)NativeMethods.WM_SYSKEYDOWN; + + private static readonly IntPtr SyskeyUpIntPtr = (IntPtr)NativeMethods.WM_SYSKEYUP; + + public static void Start() + { + if (HookId != IntPtr.Zero) + { + Stop(); + } + HookId = SetHook(Proc); + } public static void Stop() => UnsafeNativeMethods.UnhookWindowsHookEx(HookId); @@ -38,17 +55,35 @@ namespace HandyControl.Tools { if (nCode >= 0) { - if (wParam == (IntPtr)NativeMethods.WM_KEYDOWN) + if (wParam == KeyDownIntPtr) { var virtualKey = Marshal.ReadInt32(lParam); - - KeyDown?.Invoke(null, new KeyboardHookEventArgs(virtualKey)); + if (VirtualKey != virtualKey) + { + VirtualKey = virtualKey; + KeyDown?.Invoke(null, new KeyboardHookEventArgs(virtualKey, false)); + } } - else if (wParam == (IntPtr)NativeMethods.WM_KEYUP) + else if (wParam == SyskeyDownIntPtr) { var virtualKey = Marshal.ReadInt32(lParam); - - KeyUp?.Invoke(null, new KeyboardHookEventArgs(virtualKey)); + if (VirtualKey != virtualKey) + { + VirtualKey = virtualKey; + KeyDown?.Invoke(null, new KeyboardHookEventArgs(virtualKey, true)); + } + } + else if (wParam == KeyUpIntPtr) + { + var virtualKey = Marshal.ReadInt32(lParam); + VirtualKey = -1; + KeyUp?.Invoke(null, new KeyboardHookEventArgs(virtualKey, false)); + } + else if (wParam == SyskeyUpIntPtr) + { + var virtualKey = Marshal.ReadInt32(lParam); + VirtualKey = -1; + KeyUp?.Invoke(null, new KeyboardHookEventArgs(virtualKey, true)); } } return UnsafeNativeMethods.CallNextHookEx(HookId, nCode, wParam, lParam); diff --git a/src/Shared/HandyControl_Shared/Tools/Hook/MouseHook.cs b/src/Shared/HandyControl_Shared/Tools/Hook/MouseHook.cs index e7e2d0d1..5405c6cf 100644 --- a/src/Shared/HandyControl_Shared/Tools/Hook/MouseHook.cs +++ b/src/Shared/HandyControl_Shared/Tools/Hook/MouseHook.cs @@ -14,7 +14,14 @@ namespace HandyControl.Tools private static readonly UnsafeNativeMethods.HookProc Proc = HookCallback; - public static void Start() => HookId = SetHook(Proc); + public static void Start() + { + if (HookId != IntPtr.Zero) + { + Stop(); + } + HookId = SetHook(Proc); + } public static void Stop() => UnsafeNativeMethods.UnhookWindowsHookEx(HookId); diff --git a/src/Shared/HandyControl_Shared/Tools/Interop/NativeMethods.cs b/src/Shared/HandyControl_Shared/Tools/Interop/NativeMethods.cs index 94ae1c3b..66c17772 100644 --- a/src/Shared/HandyControl_Shared/Tools/Interop/NativeMethods.cs +++ b/src/Shared/HandyControl_Shared/Tools/Interop/NativeMethods.cs @@ -33,6 +33,8 @@ namespace HandyControl.Tools.Interop NIIF_ERROR = 0x00000003, WM_KEYDOWN = 0x0100, WM_KEYUP = 0x0101, + WM_SYSKEYDOWN = 0x0104, + WM_SYSKEYUP = 0x0105, WM_MOUSEMOVE = 0x0200, WM_LBUTTONDOWN = 0x0201, WM_LBUTTONUP = 0x0202,