add:配置切换
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供多配置文件保存、切换与热重载支持。
|
||||
/// 方案保存在 Configs/Profiles 目录下,当前生效的配置仍为 Configs/Settings.json。
|
||||
/// </summary>
|
||||
public static class ConfigProfileManager
|
||||
{
|
||||
private static readonly string ProfilesDir = Path.Combine(App.RootPath, "Configs", "Profiles");
|
||||
private static readonly string SettingsFilePath = Path.Combine(App.RootPath, "Configs", "Settings.json");
|
||||
private const string ProfileExtension = ".json";
|
||||
|
||||
/// <summary>将配置文件名称转为安全文件名(去掉非法字符)。</summary>
|
||||
private static string ToSafeFileName(string profileName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(profileName)) return "未命名";
|
||||
var invalid = Path.GetInvalidFileNameChars();
|
||||
var name = string.Join("_", profileName.Trim().Split(invalid, StringSplitOptions.RemoveEmptyEntries));
|
||||
return string.IsNullOrEmpty(name) ? "未命名" : name;
|
||||
}
|
||||
|
||||
/// <summary>确保配置文件目录存在。</summary>
|
||||
public static void EnsureProfilesDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(ProfilesDir))
|
||||
{
|
||||
ProcessProtectionManager.WithWriteAccess(ProfilesDir, () => Directory.CreateDirectory(ProfilesDir));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"创建配置文件目录失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>获取所有配置文件名称(不含扩展名),按名称排序。</summary>
|
||||
public static IReadOnlyList<string> ListProfileNames()
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureProfilesDirectory();
|
||||
if (!Directory.Exists(ProfilesDir)) return Array.Empty<string>();
|
||||
var files = Directory.GetFiles(ProfilesDir, "*" + ProfileExtension);
|
||||
return files
|
||||
.Select(f => Path.GetFileNameWithoutExtension(f))
|
||||
.Where(n => !string.IsNullOrEmpty(n))
|
||||
.OrderBy(n => n, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"列举配置文件失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>获取某配置文件对应的文件路径。</summary>
|
||||
public static string GetProfilePath(string profileName)
|
||||
{
|
||||
var safe = ToSafeFileName(profileName);
|
||||
return Path.Combine(ProfilesDir, safe + ProfileExtension);
|
||||
}
|
||||
|
||||
/// <summary>将当前配置的 JSON 内容保存为指定名称的配置文件。</summary>
|
||||
/// <param name="profileName">配置文件显示名称(会转为安全文件名)。</param>
|
||||
/// <param name="settingsJson">已序列化好的 Settings JSON 字符串。</param>
|
||||
/// <returns>成功返回 true。</returns>
|
||||
public static bool SaveAsProfile(string profileName, string settingsJson)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(settingsJson))
|
||||
{
|
||||
LogHelper.WriteLogToFile("配置文件保存失败:内容为空", LogHelper.LogType.Warning);
|
||||
return false;
|
||||
}
|
||||
EnsureProfilesDirectory();
|
||||
var path = GetProfilePath(profileName);
|
||||
ProcessProtectionManager.WithWriteAccess(path, () => File.WriteAllText(path, settingsJson));
|
||||
LogHelper.WriteLogToFile($"配置文件已保存: {ToSafeFileName(profileName)}", LogHelper.LogType.Event);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"保存配置文件失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>将指定配置文件应用到当前配置(覆盖 Configs/Settings.json),供主窗口随后热重载。</summary>
|
||||
/// <param name="profileName">配置文件名称(与 ListProfileNames 中一致,或与保存时使用的显示名一致)。</param>
|
||||
/// <returns>成功返回 true;文件不存在或复制失败返回 false。</returns>
|
||||
public static bool ApplyProfile(string profileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = GetProfilePath(profileName);
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
LogHelper.WriteLogToFile($"配置文件文件不存在: {path}", LogHelper.LogType.Warning);
|
||||
return false;
|
||||
}
|
||||
var json = File.ReadAllText(path);
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
LogHelper.WriteLogToFile("配置文件内容为空", LogHelper.LogType.Warning);
|
||||
return false;
|
||||
}
|
||||
// 可选:校验是否为合法 Settings JSON
|
||||
try
|
||||
{
|
||||
JsonConvert.DeserializeObject<Settings>(json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"配置文件格式无效: {ex.Message}", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
var configsDir = Path.GetDirectoryName(SettingsFilePath);
|
||||
if (!string.IsNullOrEmpty(configsDir) && !Directory.Exists(configsDir))
|
||||
{
|
||||
ProcessProtectionManager.WithWriteAccess(configsDir, () => Directory.CreateDirectory(configsDir));
|
||||
}
|
||||
ProcessProtectionManager.WithWriteAccess(SettingsFilePath, () => File.WriteAllText(SettingsFilePath, json));
|
||||
LogHelper.WriteLogToFile($"已应用配置文件: {profileName}(请热重载以生效)", LogHelper.LogType.Event);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用配置文件失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>删除指定名称的配置文件。</summary>
|
||||
public static bool DeleteProfile(string profileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = GetProfilePath(profileName);
|
||||
if (!File.Exists(path)) return true;
|
||||
ProcessProtectionManager.WithWriteAccess(path, () => File.Delete(path));
|
||||
LogHelper.WriteLogToFile($"已删除配置文件: {profileName}", LogHelper.LogType.Event);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"删除配置文件失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2873,6 +2873,31 @@
|
||||
<Button x:Name="BtnRestoreBackup" Content="还原备份" Click="BtnRestoreBackup_Click"
|
||||
Background="#2563eb" Foreground="White" Padding="12,6" />
|
||||
</ui:SimpleStackPanel>
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0" Stroke="#3f3f46"
|
||||
StrokeThickness="1" Margin="0,8,0,4" />
|
||||
<TextBlock Text="配置文件切换与热重载" FontWeight="Bold" Foreground="#fafafa"
|
||||
FontSize="16" Margin="0,5,0,5" />
|
||||
<TextBlock TextWrapping="Wrap" Foreground="#a1a1aa"
|
||||
Text="# 将当前配置另存为命名方案,或切换方案并立即生效(无需重启)" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,6,0,0">
|
||||
<TextBlock Foreground="#fafafa" Text="方案:" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,8,0" />
|
||||
<ComboBox x:Name="ComboBoxConfigProfile" Width="180" Margin="0,0,8,0"
|
||||
SelectionChanged="ComboBoxConfigProfile_SelectionChanged" />
|
||||
<Button x:Name="BtnApplyConfigProfile" Content="应用配置文件(热重载)" Click="BtnApplyConfigProfile_Click"
|
||||
Background="#16a34a" Foreground="White" Padding="10,6" Margin="0,0,8,0" />
|
||||
<Button x:Name="BtnDeleteConfigProfile" Content="删除配置文件" Click="BtnDeleteConfigProfile_Click"
|
||||
Background="#dc2626" Foreground="White" Padding="10,6" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,8,0,0">
|
||||
<TextBlock Foreground="#fafafa" Text="另存为配置文件:" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,8,0" />
|
||||
<TextBox x:Name="TextBoxNewProfileName" Width="160" Margin="0,0,8,0"
|
||||
VerticalContentAlignment="Center" Padding="6,4"
|
||||
ToolTip="输入新配置文件名称后点击保存" />
|
||||
<Button x:Name="BtnSaveAsConfigProfile" Content="另存为配置文件" Click="BtnSaveAsConfigProfile_Click"
|
||||
Background="#2563eb" Foreground="White" Padding="10,6" />
|
||||
</ui:SimpleStackPanel>
|
||||
</ui:SimpleStackPanel>
|
||||
</GroupBox>
|
||||
<GroupBox>
|
||||
|
||||
@@ -4289,6 +4289,115 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshConfigProfileList()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ComboBoxConfigProfile == null) return;
|
||||
var names = ConfigProfileManager.ListProfileNames();
|
||||
var selected = ComboBoxConfigProfile.SelectedItem as string;
|
||||
ComboBoxConfigProfile.ItemsSource = names;
|
||||
if (selected != null && names.Contains(selected))
|
||||
ComboBoxConfigProfile.SelectedItem = selected;
|
||||
else if (names.Count > 0 && ComboBoxConfigProfile.SelectedIndex < 0)
|
||||
ComboBoxConfigProfile.SelectedIndex = 0;
|
||||
if (BtnDeleteConfigProfile != null)
|
||||
BtnDeleteConfigProfile.IsEnabled = ComboBoxConfigProfile.SelectedItem != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"刷新配置文件列表失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void ComboBoxConfigProfile_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (BtnDeleteConfigProfile != null)
|
||||
BtnDeleteConfigProfile.IsEnabled = ComboBoxConfigProfile?.SelectedItem != null;
|
||||
}
|
||||
|
||||
private void BtnSaveAsConfigProfile_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
var name = TextBoxNewProfileName?.Text?.Trim();
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
MessageBox.Show("请输入配置文件名称后再保存。", "配置文件", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(Settings, Formatting.Indented);
|
||||
if (ConfigProfileManager.SaveAsProfile(name, json))
|
||||
{
|
||||
RefreshConfigProfileList();
|
||||
if (TextBoxNewProfileName != null) TextBoxNewProfileName.Clear();
|
||||
ShowNotification($"已另存为配置文件:{name}");
|
||||
}
|
||||
else
|
||||
MessageBox.Show("保存配置文件失败,请查看日志。", "配置文件", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"另存为配置文件失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"保存配置文件失败: {ex.Message}", "配置文件", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void BtnApplyConfigProfile_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
var name = ComboBoxConfigProfile?.SelectedItem as string;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
MessageBox.Show("请先选择要应用的配置文件。", "配置文件", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (ConfigProfileManager.ApplyProfile(name))
|
||||
{
|
||||
ReloadSettingsFromFile();
|
||||
ShowNotification($"已应用配置文件「{name}」并热重载");
|
||||
}
|
||||
else
|
||||
MessageBox.Show("应用配置文件失败,请查看日志。", "配置文件", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用配置文件失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"应用配置文件失败: {ex.Message}", "配置文件", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void BtnDeleteConfigProfile_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
var name = ComboBoxConfigProfile?.SelectedItem as string;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
MessageBox.Show("请先选择要删除的配置文件。", "配置文件", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (MessageBox.Show($"确定要删除配置文件「{name}」吗?", "确认删除", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||
return;
|
||||
if (ConfigProfileManager.DeleteProfile(name))
|
||||
{
|
||||
RefreshConfigProfileList();
|
||||
ShowNotification($"已删除配置文件:{name}");
|
||||
}
|
||||
else
|
||||
MessageBox.Show("删除配置文件失败,请查看日志。", "配置文件", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"删除配置文件失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
MessageBox.Show($"删除配置文件失败: {ex.Message}", "配置文件", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RandSettings
|
||||
|
||||
@@ -25,6 +25,15 @@ namespace Ink_Canvas
|
||||
/// 从配置文件加载用户设置并将其应用到主窗口和相关控件的状态(包括启动、外观、画布、手势、PPT、自动化等各项配置)。
|
||||
/// </summary>
|
||||
/// <param name="isStartup">指示当前为应用启动阶段;为 true 时按启动流程应用启动相关设置(例如触发启动专用动作和启动时的行为)。</param>
|
||||
/// <summary>
|
||||
/// 从当前配置文件重新加载设置并应用到界面(热重载),不触发启动逻辑与自动更新检查。
|
||||
/// 用于配置文件切换后立即生效。
|
||||
/// </summary>
|
||||
public void ReloadSettingsFromFile()
|
||||
{
|
||||
LoadSettings(false, skipAutoUpdateCheck: true);
|
||||
}
|
||||
|
||||
/// <param name="skipAutoUpdateCheck">指示是否跳过自动更新检查;为 true 时不会在加载设置后执行自动更新检测。</param>
|
||||
private void LoadSettings(bool isStartup = false, bool skipAutoUpdateCheck = false)
|
||||
{
|
||||
@@ -1250,6 +1259,9 @@ namespace Ink_Canvas
|
||||
|
||||
// 加载画笔自动恢复设置
|
||||
LoadBrushAutoRestoreSettings();
|
||||
|
||||
// 刷新配置文件列表
|
||||
try { RefreshConfigProfileList(); } catch (Exception ex) { LogHelper.WriteLogToFile($"刷新配置文件列表失败: {ex.Message}", LogHelper.LogType.Warning); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user