Files
personal-toolbox/PersonalToolBox/ViewModels/MainViewModel.cs
home-PC b715904439 新增工具/组合随软件启动自动运行功能
- ToolItem 模型新增 AutoRunOnStart 属性,持久化到配置文件
- App.OnStartup 启动后自动执行标记为自启动的工具/组合(间隔500ms)
- 工具编辑窗口和组合编辑窗口新增「随软件启动自动运行」CheckBox
- 主界面工具卡片左上角 Rocket 角标标识已启用自启的项目
2026-05-10 02:51:29 +08:00

345 lines
11 KiB
C#
Raw 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;
// ───────────────────────────── 命令 ─────────────────────────────
[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 ? "已启用开机自启动" : "已关闭开机自启动");
}
/// <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);
}
}