using System; using System.Collections.Concurrent; using System.Collections.Specialized; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Data; using System.Windows.Forms; using System.Windows.Interop; using System.Windows.Media.Imaging; using FontAwesome.Sharp; using PersonalToolBox.Helpers; using PersonalToolBox.ViewModels; namespace PersonalToolBox.Views; public partial class MainWindow : Window { private readonly MainViewModel _viewModel; private readonly HotKeyManager _hotKeyManager; private NotifyIcon? _notifyIcon; private ToolStripMenuItem? _hotKeyToggleMenuItem; private bool _canActuallyClose; [DllImport("user32.dll")] private static extern uint RegisterWindowMessage(string lpString); private readonly int _showMainMsg; public MainWindow(MainViewModel viewModel, HotKeyManager hotKeyManager) { InitializeComponent(); _viewModel = viewModel; _hotKeyManager = hotKeyManager; DataContext = viewModel; _showMainMsg = (int)RegisterWindowMessage("PersonalToolBox_ShowMain"); var iconPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "app.ico"); if (File.Exists(iconPath)) { using var stream = File.OpenRead(iconPath); var decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); this.Icon = decoder.Frames[0]; } viewModel.Logs.CollectionChanged += OnLogsCollectionChanged; viewModel.PropertyChanged += OnViewModelPropertyChanged; CreateTrayIcon(); } /// /// 窗口句柄就绪后注册全局快捷键 /// protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); var hwndSource = (HwndSource)PresentationSource.FromVisual(this)!; _hotKeyManager.Initialize(hwndSource.Handle); hwndSource.AddHook(WndProcHook); _viewModel.RegisterAllHotKeys(); } // ──────────────── 系统托盘 ──────────────── /// /// 创建系统托盘图标及右键菜单 /// private void CreateTrayIcon() { var iconPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "app.ico"); var trayIcon = File.Exists(iconPath) ? new System.Drawing.Icon(iconPath) : System.Drawing.SystemIcons.Application; _notifyIcon = new NotifyIcon { Icon = trayIcon, Visible = true, Text = "个人工具箱" }; _notifyIcon.MouseClick += (s, e) => { if (e.Button == MouseButtons.Left) ToggleVisibility(); }; var menu = new ContextMenuStrip(); menu.Items.Add("显示主界面", null, (_, _) => ShowMainWindow()); _hotKeyToggleMenuItem = new ToolStripMenuItem( _viewModel.IsHotKeyEnabled ? "暂停快捷键功能" : "恢复快捷键功能"); _hotKeyToggleMenuItem.Click += (_, _) => _viewModel.ToggleHotKeyCommand.Execute(null); menu.Items.Add(_hotKeyToggleMenuItem); menu.Items.Add(new ToolStripSeparator()); menu.Items.Add("彻底退出", null, (_, _) => ExitApplication()); _notifyIcon.ContextMenuStrip = menu; } /// /// 左键托盘图标:切换窗口显示/隐藏 /// private void ToggleVisibility() { if (IsVisible) Hide(); else ShowMainWindow(); } /// /// 显示主窗口并置前 /// private void ShowMainWindow() { Show(); WindowState = WindowState.Normal; Activate(); } /// /// 彻底退出程序 /// public void ExitApplication() { _canActuallyClose = true; _notifyIcon?.Dispose(); _hotKeyManager.Dispose(); System.Windows.Application.Current.Shutdown(); } /// /// 点击关闭按钮时隐藏窗口而非退出 /// protected override void OnClosing(CancelEventArgs e) { if (!_canActuallyClose) { e.Cancel = true; Hide(); } base.OnClosing(e); } // ──────────────── 其他 ──────────────── private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == _showMainMsg) { Dispatcher.Invoke(() => ShowMainWindow()); handled = true; return IntPtr.Zero; } handled = _hotKeyManager.TryHandleMessage(msg, wParam, lParam); return IntPtr.Zero; } private void OnViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(MainViewModel.IsHotKeyEnabled) && _hotKeyToggleMenuItem != null) { bool enabled = _viewModel.IsHotKeyEnabled; _hotKeyToggleMenuItem.Text = enabled ? "暂停快捷键功能" : "恢复快捷键功能"; } } 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); } } } /// /// bool → double: true=1.0, false=0.4 (工具卡片失效时置灰) /// 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(); } /// /// string → string: 取字符串首字符作为图标占位 /// 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(); } /// /// string → IconChar: 将图标枚举名称字符串转换为 IconChar 枚举值 /// public class StringToIconCharConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is string s && !string.IsNullOrEmpty(s) && System.Enum.TryParse(s, true, out var icon)) return icon; return IconChar.None; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException(); } /// /// string (exe路径) → ImageSource: 提取 .exe 文件自身图标,带缓存 /// public class ExePathToIconConverter : IValueConverter { private static readonly ConcurrentDictionary Cache = new(); public object? Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is not string path || path.Length == 0) return null; if (!path.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) return null; return Cache.GetOrAdd(path, key => { try { if (!File.Exists(key)) return null; using var icon = System.Drawing.Icon.ExtractAssociatedIcon(key); if (icon == null) return null; using var bitmap = icon.ToBitmap(); using var ms = new MemoryStream(); bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png); ms.Seek(0, SeekOrigin.Begin); var image = new BitmapImage(); image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; image.StreamSource = ms; image.EndInit(); image.Freeze(); return image; } catch { return null; } }); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException(); }