Files
home-PC f33c89d2c4 添加单实例限制、应用图标、移除托盘设置菜单、快捷键暂停/恢复功能
单实例限制:通过命名Mutex和窗口消息广播确保只能运行一个实例,再次启动时唤起已有窗口

应用图标:exe文件嵌入app.ico,托盘和窗口图标统一使用该图标文件

移除托盘右键菜单中与显示主界面重复的设置选项

快捷键暂停/恢复:托盘菜单和主界面侧边栏均添加切换按钮,通过MainViewModel.IsHotKeyEnabled双向同步
2026-05-10 14:10:52 +08:00

357 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
using PersonalToolBox.Helpers;
using PersonalToolBox.Models;
using PersonalToolBox.Services;
using PersonalToolBox.Views;
namespace PersonalToolBox.ViewModels;
/// <summary>
/// 主窗口 ViewModel管理 UI 状态、数据绑定和用户交互逻辑
/// </summary>
public partial class MainViewModel : ObservableObject
{
private readonly ILogService _logService;
private readonly IDataService _dataService;
private readonly IProcessExecutionService _processService;
private readonly IServiceProvider _serviceProvider;
private readonly HotKeyManager _hotKeyManager;
/// <summary>
/// 全部分类的虚拟对象
/// </summary>
private static readonly Category AllCategory = new() { Id = "", Name = "全部" };
public MainViewModel(
ILogService logService,
IDataService dataService,
IProcessExecutionService processService,
IServiceProvider serviceProvider,
HotKeyManager hotKeyManager)
{
_logService = logService;
_dataService = dataService;
_processService = processService;
_serviceProvider = serviceProvider;
_hotKeyManager = hotKeyManager;
LoadData();
}
// ───────────────────────────── 可观察属性 ─────────────────────────────
[ObservableProperty]
private string _searchText = string.Empty;
[ObservableProperty]
private Category? _selectedCategory;
public ObservableCollection<LogEntry> Logs => _logService.Logs;
public ObservableCollection<Category> Categories { get; } = new();
public ObservableCollection<ToolItem> Tools { get; } = new();
[ObservableProperty]
private ObservableCollection<ToolItem> _filteredTools = new();
[ObservableProperty]
private string _currentTheme = "Dark";
[ObservableProperty]
private bool _isAutoStart;
[ObservableProperty]
private bool _isHotKeyEnabled = true;
// ───────────────────────────── 命令 ─────────────────────────────
[RelayCommand]
private void ClearLogs() => _logService.Clear();
[RelayCommand]
private void ToggleTheme()
{
CurrentTheme = CurrentTheme == "Dark" ? "Light" : "Dark";
ThemeHelper.ApplyTheme(CurrentTheme);
_dataService.Config.Theme = CurrentTheme;
_dataService.Save();
_logService.Info($"已切换到{(CurrentTheme == "Dark" ? "" : "")}主题");
}
/// <summary>
/// 切换开机自启动
/// </summary>
[RelayCommand]
private void ToggleAutoStart()
{
IsAutoStart = !IsAutoStart;
AutoStartHelper.SetAutoStart(IsAutoStart);
_dataService.Config.AutoStart = IsAutoStart;
_dataService.Save();
_logService.Info(IsAutoStart ? "已启用开机自启动" : "已关闭开机自启动");
}
[RelayCommand]
private void ToggleHotKey()
{
IsHotKeyEnabled = !IsHotKeyEnabled;
_hotKeyManager.IsEnabled = IsHotKeyEnabled;
var status = IsHotKeyEnabled ? "已恢复" : "已暂停";
_logService.Info($"{status}快捷键功能");
}
/// <summary>
/// 供 MainWindow 外部调用,用于输出提示信息
/// </summary>
public void LogServiceMessage(string message)
{
_logService.Info(message);
}
/// <summary>
/// 打开添加工具弹窗
/// </summary>
[RelayCommand]
private void AddTool()
{
var vm = _serviceProvider.GetRequiredService<ToolEditViewModel>();
var window = new ToolEditWindow(vm);
window.ShowDialog();
if (vm.Saved)
{
RefreshData();
}
}
/// <summary>
/// 打开编辑工具弹窗
/// </summary>
[RelayCommand]
private void EditTool(ToolItem tool)
{
if (tool == null) return;
// 创建编辑 ViewModel需要新建实例DI 容器无法区分参数)
var dataService = _serviceProvider.GetRequiredService<IDataService>();
var logService = _serviceProvider.GetRequiredService<ILogService>();
var editVm = new ToolEditViewModel(dataService, logService, tool);
var window = new ToolEditWindow(editVm);
window.ShowDialog();
if (editVm.Saved)
{
RefreshData();
}
}
/// <summary>
/// 执行工具(双击卡片或右键菜单)
/// </summary>
[RelayCommand]
private async Task ExecuteTool(ToolItem? tool)
{
if (tool == null) return;
await _processService.ExecuteAsync(tool);
}
// ───────────────────────────── 分类管理命令 ─────────────────────────────
/// <summary>
/// 添加分类
/// </summary>
[RelayCommand]
private void AddCategory()
{
var vm = _serviceProvider.GetRequiredService<CategoryEditViewModel>();
var window = new CategoryEditWindow(vm);
window.ShowDialog();
if (vm.Saved) RefreshData();
}
/// <summary>
/// 编辑分类(右键菜单触发)
/// </summary>
[RelayCommand]
private void EditCategory(Category? category)
{
if (category == null || string.IsNullOrEmpty(category.Id)) return;
var dataService = _serviceProvider.GetRequiredService<IDataService>();
var logService = _serviceProvider.GetRequiredService<ILogService>();
var editVm = new CategoryEditViewModel(dataService, logService, category);
var window = new CategoryEditWindow(editVm);
window.ShowDialog();
if (editVm.Saved) RefreshData();
}
/// <summary>
/// 删除分类,其下所有工具移入"全部"
/// </summary>
[RelayCommand]
private void DeleteCategory(Category? category)
{
if (category == null || string.IsNullOrEmpty(category.Id)) return;
if (MessageBox.Show(
$"确定删除分类 \"{category.Name}\" 吗?\n该分类下的所有工具将移入「全部」。",
"确认删除", MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes)
return;
// 将属于该分类的工具设为"全部"
foreach (var tool in _dataService.Config.Tools.Where(t => t.CategoryId == category.Id))
tool.CategoryId = string.Empty;
// 从配置中移除分类
_dataService.Config.Categories.Remove(category);
_dataService.Save();
_logService.Info($"已删除分类: {category.Name}");
RefreshData();
}
// ───────────────────────────── 组合管理命令 ─────────────────────────────
/// <summary>
/// 添加组合
/// </summary>
[RelayCommand]
private void AddGroup()
{
var vm = _serviceProvider.GetRequiredService<GroupEditViewModel>();
var window = new GroupEditWindow(vm);
window.ShowDialog();
if (vm.Saved) RefreshData();
}
/// <summary>
/// 编辑组合
/// </summary>
[RelayCommand]
private void EditGroup(ToolItem tool)
{
if (tool == null || !tool.IsGroup) return;
var dataService = _serviceProvider.GetRequiredService<IDataService>();
var logService = _serviceProvider.GetRequiredService<ILogService>();
var editVm = new GroupEditViewModel(dataService, logService, tool);
var window = new GroupEditWindow(editVm);
window.ShowDialog();
if (editVm.Saved) RefreshData();
}
/// <summary>
/// 删除工具或组合
/// </summary>
[RelayCommand]
private void DeleteTool(ToolItem? tool)
{
if (tool == null) return;
var type = tool.IsGroup ? "组合" : "工具";
if (MessageBox.Show($"确定删除{type} \"{tool.Name}\" 吗?",
"确认删除", MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes)
return;
_dataService.Config.Tools.Remove(tool);
_dataService.Save();
_logService.Info($"已删除{type}: {tool.Name}");
RefreshData();
}
// ───────────────────────────── 自动运行 ─────────────────────────────
/// <summary>
/// 启动时自动运行标记为 AutoRunOnStart 的工具和组合
/// </summary>
public async Task ExecuteAutoRunToolsAsync()
{
var autoTools = _dataService.Config.Tools.Where(t => t.AutoRunOnStart).ToList();
if (autoTools.Count == 0) return;
_logService.Info($"正在启动 {autoTools.Count} 个自启动工具...");
foreach (var tool in autoTools)
{
await _processService.ExecuteAsync(tool);
await Task.Delay(500);
}
}
// ───────────────────────────── 数据刷新 ─────────────────────────────
/// <summary>
/// 从配置重新加载数据并刷新 UI
/// </summary>
public void RefreshData()
{
var previousCategoryId = SelectedCategory?.Id;
Categories.Clear();
Categories.Add(AllCategory);
foreach (var cat in _dataService.Config.Categories)
Categories.Add(cat);
Tools.Clear();
foreach (var tool in _dataService.Config.Tools)
Tools.Add(tool);
// 恢复之前选中的分类(若仍存在),否则选中"全部"
SelectedCategory = Categories.FirstOrDefault(c => c.Id == previousCategoryId) ?? AllCategory;
ApplyFilter();
// 工具列表变更后重新注册快捷键
RegisterAllHotKeys();
}
// ───────────────────────────── 快捷键 ─────────────────────────────
/// <summary>
/// 注册所有工具的全局快捷键(由 MainWindow.OnSourceInitialized 及数据变更后调用)
/// </summary>
public void RegisterAllHotKeys()
{
_hotKeyManager.RegisterAll(Tools);
}
private void LoadData()
{
RefreshData();
SelectedCategory = AllCategory;
CurrentTheme = _dataService.Config.Theme;
IsAutoStart = _dataService.Config.AutoStart;
ThemeHelper.ApplyTheme(CurrentTheme);
}
// ───────────────────────────── 过滤逻辑 ─────────────────────────────
partial void OnSearchTextChanged(string value) => ApplyFilter();
partial void OnSelectedCategoryChanged(Category? value) => ApplyFilter();
private void ApplyFilter()
{
var filtered = Tools.AsEnumerable();
if (SelectedCategory != null && !string.IsNullOrEmpty(SelectedCategory.Id))
filtered = filtered.Where(t => t.CategoryId == SelectedCategory.Id);
if (!string.IsNullOrWhiteSpace(SearchText))
filtered = filtered.Where(t =>
t.Name.Contains(SearchText, System.StringComparison.OrdinalIgnoreCase));
FilteredTools = new ObservableCollection<ToolItem>(filtered);
}
}