delete:插件系统

This commit is contained in:
2026-04-05 14:12:35 +08:00
parent 1fca17d557
commit 7a30c93ff5
38 changed files with 1 additions and 9204 deletions
@@ -1,179 +0,0 @@
<Window x:Class="Ink_Canvas.Windows.PluginSettingsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
xmlns:local="clr-namespace:Ink_Canvas.Windows"
mc:Ignorable="d"
Title="插件管理" Height="550" Width="800"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResize"
Topmost="True"
ui:ThemeManager.IsThemeAware="True"
ui:TitleBar.ExtendViewIntoTitleBar="True"
ui:WindowHelper.SystemBackdropType="Mica"
ui:WindowHelper.UseModernWindowStyle="True"
ui:TitleBar.Height="48">
<Window.Resources>
<!-- 定义必要的资源 -->
<SolidColorBrush x:Key="BorderBrush" Color="#DDDDDD"/>
<SolidColorBrush x:Key="SystemAccentColorLight1" Color="#3B82F6"/>
<SolidColorBrush x:Key="SystemAccentColor" Color="#2563EB"/>
<SolidColorBrush x:Key="SystemControlBackgroundChromeMediumBrush" Color="#F0F0F0"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 自定义标题栏 -->
<Border x:Name="Border_TitleBarRoot"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.Height)}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=Title}"
VerticalAlignment="Center" Margin="12,0,0,0" FontSize="12" FontWeight="SemiBold"/>
<!--Right Inset-->
<Rectangle Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=(ui:TitleBar.SystemOverlayRightInset)}"
Grid.Column="2"/>
<!--Right Buttons-->
<ikw:SimpleStackPanel x:Name="StackPanel_RightButtons"
Orientation="Horizontal" Grid.Column="1" Spacing="5">
</ikw:SimpleStackPanel>
</Grid>
</Border>
<!-- 内容区域背景 -->
<Border Grid.Row="1" Grid.RowSpan="2"
Background="{DynamicResource SystemControlBackgroundAltHighBrush}"
BorderThickness="0,1,0,0"
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"/>
<!-- 主内容区 -->
<Grid Grid.Row="1" Margin="20,20,20,20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 左侧插件列表 -->
<Border Grid.Column="0" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Margin="0,0,10,0" CornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="插件列表" Margin="10,10,0,5" FontSize="16" FontWeight="SemiBold" Foreground="Black"/>
<ListView Grid.Row="1" x:Name="PluginListView" BorderThickness="0" Margin="0,5,0,0"
SelectionChanged="PluginListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Grid.Column="0" VerticalAlignment="Center"
Foreground="Black" FontWeight="Normal" FontSize="14"/>
<ui:ToggleSwitch Grid.Column="1" IsOn="{Binding IsEnabled}"
Toggled="PluginToggleSwitch_Toggled"
Tag="{Binding}" MinWidth="40"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Border>
<!-- 右侧插件详情和设置 -->
<ScrollViewer Grid.Column="1" Margin="10,0,0,0" VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 插件详情 -->
<Border Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Padding="15" CornerRadius="5">
<Grid x:Name="PluginDetailGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="名称:" FontWeight="SemiBold" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="版本:" FontWeight="SemiBold" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Version}" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="作者:" FontWeight="SemiBold" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Author}" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="描述:" FontWeight="SemiBold" Margin="0,0,0,5" Foreground="Black"/>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Description}" TextWrapping="Wrap" Margin="0,0,0,5" Foreground="Black"/>
<Button Grid.Row="3" Grid.Column="2" x:Name="BtnDeletePlugin" Content="删除插件"
Visibility="Collapsed" Click="BtnDeletePlugin_Click"
Margin="10,0,0,0" Padding="8,3" HorizontalAlignment="Right"
Background="#FFEE5555" Foreground="White"/>
</Grid>
</Border>
<!-- 插件设置区域 -->
<Border Grid.Row="1" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Margin="0,10,0,0" Padding="15" CornerRadius="5">
<StackPanel>
<TextBlock Text="插件设置" FontSize="16" FontWeight="SemiBold" Margin="0,0,0,10" Foreground="Black"/>
<ContentControl x:Name="PluginSettingsContainer" MinHeight="50"/>
</StackPanel>
</Border>
<!-- 插件配置内容区 -->
<Border Grid.Row="2" BorderThickness="1" BorderBrush="{DynamicResource BorderBrush}" Margin="0,10,0,0" Padding="0" CornerRadius="5">
<ContentControl x:Name="PluginContentContainer" Margin="0"/>
</Border>
</Grid>
</ScrollViewer>
</Grid>
<!-- 底部操作栏 -->
<Border Grid.Row="2" Background="{DynamicResource SystemControlBackgroundAltHighBrush}" Height="60">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="20,0,0,0">
<Button x:Name="BtnLoadPlugin" Content="加载本地插件" Click="BtnLoadPlugin_Click"
Padding="15,5" Background="{DynamicResource SystemAccentColor}" Foreground="White"/>
<Button x:Name="BtnSaveConfig" Content="保存状态" Click="BtnSaveConfig_Click"
Padding="15,5" Margin="10,0,0,0" Background="{DynamicResource SystemAccentColor}" Foreground="White"
ToolTip="手动保存当前所有插件的启用/禁用状态到配置文件"/>
<Button x:Name="BtnExportPlugin" Content="导出插件" Click="BtnExportPlugin_Click"
Padding="15,5" Margin="10,0,0,0" Background="{DynamicResource SystemAccentColor}" Foreground="White"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</Window>
@@ -1,727 +0,0 @@
using Ink_Canvas.Helpers;
using Ink_Canvas.Helpers.Plugins;
using Ink_Canvas.Helpers.Plugins.BuiltIn;
using Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
using iNKORE.UI.WPF.Modern.Controls;
using Microsoft.Win32;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
namespace Ink_Canvas.Windows
{
/// <summary>
/// PluginSettingsWindow.xaml 的交互逻辑
/// </summary>
public partial class PluginSettingsWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 刷新插件列表
/// </summary>
public void RefreshPluginList()
{
LoadPlugins();
// 如果当前选中的插件仍然存在,保持其选中状态
if (SelectedPlugin != null)
{
var matchingPlugin = Plugins.FirstOrDefault(p => p.Plugin.GetType().FullName == SelectedPlugin.GetType().FullName);
if (matchingPlugin != null)
{
PluginListView.SelectedItem = matchingPlugin;
}
}
OnPropertyChanged(nameof(SelectedPlugin));
LogHelper.WriteLogToFile("插件列表已刷新");
}
private IPlugin _selectedPlugin;
/// <summary>
/// 当前选中的插件
/// </summary>
public IPlugin SelectedPlugin
{
get => _selectedPlugin;
set
{
if (_selectedPlugin != value)
{
_selectedPlugin = value;
OnPropertyChanged(nameof(SelectedPlugin));
OnPropertyChanged(nameof(Name));
OnPropertyChanged(nameof(Version));
OnPropertyChanged(nameof(Author));
OnPropertyChanged(nameof(Description));
OnPropertyChanged(nameof(IsEnabled));
OnPropertyChanged(nameof(IsBuiltIn));
}
}
}
public new string Name => SelectedPlugin?.Name ?? string.Empty;
public string Version => SelectedPlugin?.Version?.ToString() ?? string.Empty;
public string Author => SelectedPlugin?.Author ?? string.Empty;
public string Description => SelectedPlugin?.Description ?? string.Empty;
public new bool IsEnabled => SelectedPlugin is PluginBase plugin && plugin.IsEnabled;
public bool IsBuiltIn => SelectedPlugin?.IsBuiltIn ?? false;
/// <summary>
/// 插件列表
/// </summary>
public ObservableCollection<PluginViewModel> Plugins { get; } = new ObservableCollection<PluginViewModel>();
public PluginSettingsWindow()
{
InitializeComponent();
// 设置数据上下文
PluginDetailGrid.DataContext = this;
// 设置导出按钮初始状态
BtnExportPlugin.IsEnabled = false;
BtnExportPlugin.ToolTip = "请先选择要导出的插件";
// 加载插件列表
LoadPlugins();
// 注册窗口关闭事件
Closing += PluginSettingsWindow_Closing;
}
/// <summary>
/// 窗口关闭事件处理
/// </summary>
private void PluginSettingsWindow_Closing(object sender, CancelEventArgs e)
{
try
{
// 保存插件配置
LogHelper.WriteLogToFile("插件管理窗口关闭,保存插件配置...");
PluginManager.Instance.SaveConfig();
LogHelper.WriteLogToFile("插件配置已保存");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"关闭窗口时保存插件配置出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 加载插件列表
/// </summary>
private void LoadPlugins()
{
Plugins.Clear();
LogHelper.WriteLogToFile($"开始加载插件列表到UI,插件总数: {PluginManager.Instance.Plugins.Count}");
// 添加所有已加载的插件
foreach (var plugin in PluginManager.Instance.Plugins)
{
bool isEnabled = false;
// 从插件实例获取启用状态
if (plugin is PluginBase pluginBase)
{
isEnabled = pluginBase.IsEnabled;
}
// 记录插件详细信息
LogHelper.WriteLogToFile($"正在加载插件到UI: 类型={plugin.GetType().FullName}, 名称={plugin.Name ?? ""}, 状态={isEnabled}");
// 创建视图模型并添加到集合
var viewModel = new PluginViewModel(plugin)
{
IsEnabled = isEnabled
};
Plugins.Add(viewModel);
LogHelper.WriteLogToFile($"已加载插件到UI列表: {plugin.Name},状态: {(isEnabled ? "" : "")}");
}
// 绑定到ListView
LogHelper.WriteLogToFile($"绑定 {Plugins.Count} 个插件到ListView");
PluginListView.ItemsSource = Plugins;
// 如果有插件,选择第一个
if (Plugins.Count > 0)
{
LogHelper.WriteLogToFile($"选择第一个插件: {Plugins[0].Name}");
PluginListView.SelectedIndex = 0;
}
else
{
LogHelper.WriteLogToFile("没有找到任何插件", LogHelper.LogType.Warning);
}
}
/// <summary>
/// 更新属性变更通知
/// </summary>
/// <param name="propertyName">属性名称</param>
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// 插件列表选择变更事件
/// </summary>
private void PluginListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (PluginListView.SelectedItem is PluginViewModel viewModel)
{
// 设置当前选中的插件
SelectedPlugin = viewModel.Plugin;
// 加载插件设置界面
PluginSettingsContainer.Content = SelectedPlugin.GetSettingsView();
// 设置删除按钮的可见性
BtnDeletePlugin.Visibility = !SelectedPlugin.IsBuiltIn ? Visibility.Visible : Visibility.Collapsed;
// 设置导出按钮的可用状态
BtnExportPlugin.IsEnabled = !SelectedPlugin.IsBuiltIn;
if (SelectedPlugin.IsBuiltIn)
{
BtnExportPlugin.ToolTip = "内置插件无法导出";
}
else
{
BtnExportPlugin.ToolTip = "将插件导出为.iccpp文件";
}
}
else
{
SelectedPlugin = null;
PluginSettingsContainer.Content = null;
BtnDeletePlugin.Visibility = Visibility.Collapsed;
BtnExportPlugin.IsEnabled = false;
BtnExportPlugin.ToolTip = "请先选择要导出的插件";
}
}
/// <summary>
/// 加载本地插件按钮点击事件
/// </summary>
private void BtnLoadPlugin_Click(object sender, RoutedEventArgs e)
{
try
{
// 创建文件对话框
OpenFileDialog dialog = new OpenFileDialog
{
Filter = "ICC插件文件(*.iccpp)|*.iccpp",
Title = "选择要加载的插件文件"
};
// 显示对话框
if (dialog.ShowDialog() == true)
{
// 获取插件文件路径
string pluginPath = dialog.FileName;
// 检查是否在Plugins目录下
string pluginsDirectory = Path.Combine(App.RootPath, "Plugins");
string targetPath = Path.Combine(pluginsDirectory, Path.GetFileName(pluginPath));
// 确保Plugins目录存在
if (!Directory.Exists(pluginsDirectory))
{
Directory.CreateDirectory(pluginsDirectory);
}
// 如果插件不在Plugins目录下,复制过去
if (!string.Equals(pluginPath, targetPath, StringComparison.OrdinalIgnoreCase))
{
File.Copy(pluginPath, targetPath, true);
pluginPath = targetPath;
}
// 加载插件
IPlugin plugin = PluginManager.Instance.LoadExternalPlugin(pluginPath);
if (plugin != null)
{
// 刷新插件列表
LoadPlugins();
// 选择新加载的插件
foreach (var item in Plugins)
{
if (item.Plugin == plugin)
{
PluginListView.SelectedItem = item;
break;
}
}
MessageBox.Show($"插件 {plugin.Name} v{plugin.Version} 已成功加载!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
MessageBox.Show("插件加载失败,请检查文件是否有效。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
catch (Exception ex)
{
MessageBox.Show($"加载插件时发生错误:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 删除插件按钮点击事件
/// </summary>
private void BtnDeletePlugin_Click(object sender, RoutedEventArgs e)
{
if (SelectedPlugin == null) return;
// 不能删除内置插件
if (SelectedPlugin.IsBuiltIn)
{
MessageBox.Show("内置插件无法删除。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 保存插件名称,以便在删除后使用
string pluginName = SelectedPlugin.Name;
// 确认删除
MessageBoxResult result = MessageBox.Show(
$"确定要删除插件 {pluginName} 吗?\n此操作将永久删除插件文件,无法恢复。",
"删除确认",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
// 删除插件
bool success = PluginManager.Instance.DeletePlugin(SelectedPlugin);
if (success)
{
// 刷新插件列表
LoadPlugins();
// 如果还有插件,选择第一个
if (Plugins.Count > 0)
{
PluginListView.SelectedIndex = 0;
}
MessageBox.Show($"插件 {pluginName} 已成功删除。", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
MessageBox.Show($"删除插件 {pluginName} 失败,请稍后重试。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/// <summary>
/// 导出插件按钮点击事件
/// </summary>
private void BtnExportPlugin_Click(object sender, RoutedEventArgs e)
{
try
{
// 检查是否有选中的插件
if (SelectedPlugin == null)
{
MessageBox.Show("请先选择要导出的插件", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 检查是否为内置插件
if (SelectedPlugin.IsBuiltIn)
{
MessageBox.Show("内置插件无法导出", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 检查插件文件是否存在
string pluginPath = null;
if (SelectedPlugin is PluginBase pluginBase)
{
pluginPath = pluginBase.PluginPath;
}
if (string.IsNullOrEmpty(pluginPath) || !File.Exists(pluginPath))
{
MessageBox.Show("插件文件不存在或无法访问", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
// 创建保存文件对话框
SaveFileDialog dialog = new SaveFileDialog
{
Filter = "ICC插件文件(*.iccpp)|*.iccpp",
Title = "导出插件",
FileName = Path.GetFileName(pluginPath)
};
// 显示对话框
if (dialog.ShowDialog() == true)
{
// 获取目标路径
string targetPath = dialog.FileName;
// 如果目标文件已存在,询问是否覆盖
if (File.Exists(targetPath) && !string.Equals(pluginPath, targetPath, StringComparison.OrdinalIgnoreCase))
{
MessageBoxResult result = MessageBox.Show("目标文件已存在,是否覆盖?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
{
return;
}
}
// 复制插件文件到目标路径
if (!string.Equals(pluginPath, targetPath, StringComparison.OrdinalIgnoreCase))
{
File.Copy(pluginPath, targetPath, true);
}
LogHelper.WriteLogToFile($"插件 {SelectedPlugin.Name} 已成功导出到: {targetPath}");
MessageBox.Show($"插件 {SelectedPlugin.Name} 已成功导出!", "成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"导出插件时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"导出插件时发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 插件开关切换事件
/// </summary>
private void PluginToggleSwitch_Toggled(object sender, RoutedEventArgs e)
{
try
{
if (sender is ToggleSwitch toggleSwitch &&
toggleSwitch.Tag is IPlugin plugin)
{
// 记录当前开关状态
bool targetState = toggleSwitch.IsOn;
// 记录插件类型名称和名称,用于稍后查找重载后的插件
string pluginTypeName = plugin.GetType().FullName;
string pluginName = plugin.Name;
bool wasBuiltIn = plugin.IsBuiltIn;
LogHelper.WriteLogToFile($"UI开关切换: {pluginName}, 目标状态: {(targetState ? "" : "")}");
// 切换插件状态
PluginManager.Instance.TogglePlugin(plugin, targetState);
// 立即同步保存配置到文件(确保状态被立即持久化)
PluginManager.Instance.SaveConfig();
LogHelper.WriteLogToFile("插件状态已立即保存到配置文件");
// 延迟一下再检查状态,确保变更已应用
Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 查找最新的插件实例
IPlugin currentPlugin = null;
foreach (var p in PluginManager.Instance.Plugins)
{
if (p.GetType().FullName == pluginTypeName || p.Name == pluginName)
{
currentPlugin = p;
break;
}
}
if (currentPlugin == null)
{
LogHelper.WriteLogToFile($"无法找到插件: {pluginName}UI状态可能不准确", LogHelper.LogType.Warning);
return;
}
// 检查实际状态
bool actualState = currentPlugin is PluginBase pb && pb.IsEnabled;
LogHelper.WriteLogToFile($"插件 {pluginName} 实际状态: {(actualState ? "" : "")}");
// 更新视图模型
PluginViewModel viewModel = null;
if (toggleSwitch.DataContext is PluginViewModel vm)
{
viewModel = vm;
}
else
{
viewModel = Plugins.FirstOrDefault(p => p.Plugin == currentPlugin);
}
if (viewModel != null)
{
// 确保视图模型状态与实际状态一致
if (viewModel.IsEnabled != actualState)
{
LogHelper.WriteLogToFile($"同步视图模型状态: {(actualState ? "" : "")}");
viewModel.IsEnabled = actualState;
}
// 确保UI开关状态与实际状态一致
if (toggleSwitch.IsOn != actualState)
{
LogHelper.WriteLogToFile($"同步UI开关状态: {(actualState ? "" : "")}");
toggleSwitch.IsOn = actualState;
}
}
// 如果是当前选中的插件,更新属性
if (currentPlugin == SelectedPlugin)
{
OnPropertyChanged(nameof(IsEnabled));
}
// 对于内置插件,特别处理
if (wasBuiltIn)
{
// 特殊插件刷新逻辑,如果是超级启动台插件,立即刷新UI
if (currentPlugin is SuperLauncherPlugin &&
PluginSettingsContainer.Content is LauncherSettingsControl)
{
// 重新获取设置界面
PluginSettingsContainer.Content = currentPlugin.GetSettingsView();
}
}
LogHelper.WriteLogToFile($"插件 {pluginName} UI状态同步完成");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"同步UI状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
}), DispatcherPriority.Background);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换插件状态时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"切换插件状态时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 刷新插件列表按钮点击事件
/// </summary>
private void Button_Refresh_Click(object sender, RoutedEventArgs e)
{
try
{
// 刷新插件列表
RefreshPluginList();
LogHelper.WriteLogToFile("用户点击刷新按钮,刷新插件列表");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"刷新插件列表时出错: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show($"刷新插件列表时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 保存插件状态按钮点击事件
/// </summary>
private void BtnSaveConfig_Click(object sender, RoutedEventArgs e)
{
try
{
int syncCount = 0;
// 遍历界面上所有插件视图模型,获取开关状态
foreach (var viewModel in Plugins)
{
try
{
if (viewModel.Plugin != null)
{
// 获取UI中开关的当前状态(从界面控件读取)
bool uiState = viewModel.IsEnabled;
// 获取插件类型名,用于查找配置
string pluginTypeName = viewModel.Plugin.GetType().FullName;
// 查找实际的插件实例(可能与viewModel.Plugin不同,因为可能已经重新加载)
IPlugin actualPlugin = null;
foreach (var p in PluginManager.Instance.Plugins)
{
if (p.GetType().FullName == pluginTypeName)
{
actualPlugin = p;
break;
}
}
// 如果找不到对应的实际插件实例,跳过
if (actualPlugin == null)
{
LogHelper.WriteLogToFile($"手动保存:无法找到与UI对应的插件实例:{viewModel.Name}", LogHelper.LogType.Warning);
continue;
}
// 获取插件实际状态
bool pluginState = false;
if (actualPlugin is PluginBase pluginBase)
{
pluginState = pluginBase.IsEnabled;
}
// 如果界面状态与插件实际状态不一致,应用界面状态
if (uiState != pluginState)
{
// 应用界面的状态到插件
PluginManager.Instance.TogglePlugin(actualPlugin, uiState);
LogHelper.WriteLogToFile($"手动保存:同步插件 {actualPlugin.Name} 状态 {pluginState} -> {uiState}");
syncCount++;
}
// 确保配置中的状态也与界面一致
if (PluginManager.Instance.PluginStates.TryGetValue(pluginTypeName, out bool configState) && configState != uiState)
{
PluginManager.Instance.PluginStates[pluginTypeName] = uiState;
LogHelper.WriteLogToFile($"手动保存:更新配置中插件 {actualPlugin.Name} 状态 {configState} -> {uiState}");
syncCount++;
}
}
}
catch (Exception pluginEx)
{
// 单个插件处理失败不应该影响其他插件
LogHelper.WriteLogToFile($"手动保存:处理插件 {viewModel.Name} 时出错: {pluginEx.Message}", LogHelper.LogType.Error);
}
}
// 保存插件状态配置
PluginManager.Instance.SaveConfig();
// 记录日志
LogHelper.WriteLogToFile($"用户手动保存插件状态配置,同步了 {syncCount} 个状态变更");
// 刷新插件列表,确保UI与最新状态同步
RefreshPluginList();
// 显示保存成功提示
string message = syncCount > 0
? $"插件状态已成功保存,同步了 {syncCount} 个状态变更"
: "插件状态已成功保存,所有插件状态已是最新";
MessageBox.Show(message, "保存成功", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
// 记录错误日志
LogHelper.WriteLogToFile($"手动保存插件状态时出错: {ex.Message}", LogHelper.LogType.Error);
// 显示错误信息
MessageBox.Show($"保存插件状态时发生错误: {ex.Message}", "保存失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
/// <summary>
/// 插件视图模型
/// </summary>
public class PluginViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 插件实例
/// </summary>
public IPlugin Plugin { get; }
/// <summary>
/// 插件名称
/// </summary>
public string Name
{
get
{
string name = Plugin?.Name ?? "未命名插件";
LogHelper.WriteLogToFile($"获取插件名称: {name},类型: {Plugin?.GetType().FullName ?? ""}");
return name;
}
}
/// <summary>
/// 插件是否启用
/// </summary>
private bool _isEnabled;
public bool IsEnabled
{
get => _isEnabled;
set
{
if (_isEnabled != value)
{
_isEnabled = value;
OnPropertyChanged(nameof(IsEnabled));
}
}
}
public PluginViewModel(IPlugin plugin)
{
Plugin = plugin;
// 获取实际状态
_isEnabled = plugin is PluginBase pluginBase && pluginBase.IsEnabled;
// 记录日志
LogHelper.WriteLogToFile($"创建插件视图模型: {plugin?.GetType().FullName ?? ""}, 名称: {plugin?.Name ?? ""}");
// 注册插件状态变更事件
if (plugin is PluginBase pb)
{
pb.EnabledStateChanged += Plugin_EnabledStateChanged;
}
}
/// <summary>
/// 处理插件状态变更事件
/// </summary>
private void Plugin_EnabledStateChanged(object sender, bool isEnabled)
{
// 在UI线程上更新状态
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
IsEnabled = isEnabled;
// 确保配置立即保存
if (sender is IPlugin plugin)
{
LogHelper.WriteLogToFile($"视图模型捕获到插件 {plugin.Name} 状态变更: {(isEnabled ? "" : "")}");
PluginManager.Instance.SaveConfig();
LogHelper.WriteLogToFile("视图模型已触发配置保存");
}
}));
}
/// <summary>
/// 属性变更通知
/// </summary>
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
@@ -142,15 +142,6 @@
</StackPanel>
</Border>
<!-- 插件管理 -->
<Border Margin="0,25,0,0" BorderBrush="#e6e6e6" BorderThickness="1.25,1.25,1.25,4" CornerRadius="8">
<StackPanel Orientation="Vertical" Margin="18,18,18,18">
<TextBlock Text="通过插件扩展InkCanvas的功能。您可以启用或禁用插件,或加载自定义插件。"
TextWrapping="Wrap" Foreground="#9a9996" FontSize="11" Margin="0,0,0,12"/>
<Button x:Name="BtnOpenPluginManager" Content="打开插件管理器"
HorizontalAlignment="Left" Padding="15,5"/>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</UserControl>