- 新增 HotKeyManager (Helpers/), 使用 user32.dll RegisterHotKey/UnregisterHotKey 注册系统级快捷键 - 支持 Ctrl/Alt/Shift/Win 修饰键组合, 单字符键和命名键(F1-F12等)解析 - MainWindow.OnSourceInitialized 挂载 HwndSource.AddHook 拦截 WM_HOTKEY 消息 - 启动时自动注册所有含快捷键的工具, 工具增删改后自动重新注册 - 快捷键冲突时记录 Win32 错误码, 无效格式打印警告 - 新增 12 个 HotKeyManager 单元测试 (73 tests total)
94 lines
2.8 KiB
C#
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();
|
|
}
|