Files
personal-toolbox/PersonalToolBox/Views/MainWindow.xaml.cs
home-PC 92beb46f22 Phase 4: 全局快捷键拦截 (Win32 API)
- 新增 HotKeyManager (Helpers/), 使用 user32.dll RegisterHotKey/UnregisterHotKey 注册系统级快捷键

- 支持 Ctrl/Alt/Shift/Win 修饰键组合, 单字符键和命名键(F1-F12等)解析

- MainWindow.OnSourceInitialized 挂载 HwndSource.AddHook 拦截 WM_HOTKEY 消息

- 启动时自动注册所有含快捷键的工具, 工具增删改后自动重新注册

- 快捷键冲突时记录 Win32 错误码, 无效格式打印警告

- 新增 12 个 HotKeyManager 单元测试 (73 tests total)
2026-05-09 23:07:44 +08:00

94 lines
2.8 KiB
C#

using System;
using System.Collections.Specialized;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Interop;
using PersonalToolBox.Helpers;
using PersonalToolBox.ViewModels;
namespace PersonalToolBox.Views;
public partial class MainWindow : Window
{
private readonly MainViewModel _viewModel;
private readonly HotKeyManager _hotKeyManager;
public MainWindow(MainViewModel viewModel, HotKeyManager hotKeyManager)
{
InitializeComponent();
_viewModel = viewModel;
_hotKeyManager = hotKeyManager;
DataContext = viewModel;
viewModel.Logs.CollectionChanged += OnLogsCollectionChanged;
}
/// <summary>
/// 窗口句柄就绪后注册全局快捷键
/// </summary>
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var hwndSource = (HwndSource)PresentationSource.FromVisual(this)!;
_hotKeyManager.Initialize(hwndSource.Handle);
// 挂载 WndProc 钩子,拦截 WM_HOTKEY 消息
hwndSource.AddHook(WndProcHook);
// 注册所有已配置快捷键的工具
_viewModel.RegisterAllHotKeys();
}
/// <summary>
/// 窗口消息钩子
/// </summary>
private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
handled = _hotKeyManager.TryHandleMessage(msg, wParam, lParam);
return IntPtr.Zero;
}
private void OnLogsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add && LogListBox.Items.Count > 0)
{
Dispatcher.BeginInvoke(new Action(() =>
{
LogListBox.ScrollIntoView(LogListBox.Items[^1]);
}), System.Windows.Threading.DispatcherPriority.Background);
}
}
}
/// <summary>
/// bool → double: true=1.0, false=0.4 (工具卡片失效时置灰)
/// </summary>
public class BoolToOpacityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is bool b && b ? 1.0 : 0.4;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
/// <summary>
/// string → string: 取字符串首字符作为图标占位
/// </summary>
public class FirstCharConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var s = value as string;
return string.IsNullOrEmpty(s) ? "?" : s[0].ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}