Compare commits

...

19 Commits

Author SHA1 Message Date
CJKmkp a46f8b36a0 improve:PPT联动模块 2026-01-17 17:44:46 +08:00
CJKmkp c0453ea1fc improve:PPT联动 2026-01-17 17:31:22 +08:00
CJKmkp 41b8f9c962 add:新设置 2026-01-17 17:24:27 +08:00
CJKmkp e69175e5c4 improve:主题适配 2026-01-17 17:22:46 +08:00
CJKmkp 76babf4dd3 improve:PPT联动 2026-01-17 17:11:11 +08:00
CJKmkp 5d6f53dc58 更新版本号 2026-01-17 17:10:56 +08:00
CJKmkp 72a49b7bf2 improve:自动更新 2026-01-17 17:05:55 +08:00
CJKmkp c4230a15c9 improve:信息保存 2026-01-17 16:51:28 +08:00
CJKmkp 84167bbcfc improve:自动收纳 2026-01-17 16:50:37 +08:00
CJKmkp 7f154ba1db improve:自动收纳 2026-01-17 16:46:37 +08:00
CJKmkp 7abb7e2ef1 improve:卡死检测 2026-01-17 16:32:57 +08:00
PrefacedCorg 04d21ac890 fix:颜色错误 2026-01-16 21:17:14 +08:00
CJKmkp a6992874f8 add:新设置 2026-01-11 08:23:09 +08:00
CJKmkp 1b2db81dde add:新设置 2026-01-11 00:16:22 +08:00
CJKmkp 17e6d23650 improve:PPT时间胶囊 2026-01-11 00:14:44 +08:00
CJKmkp 2aa1ba537f Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2026-01-10 23:32:06 +08:00
CJKmkp 981ca7629e add:新设置 2026-01-10 23:20:31 +08:00
PrefacedCorg 1aaef3d554 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2026-01-10 23:16:16 +08:00
PrefacedCorg 2fd83a4a80 更新注释 2026-01-10 23:15:13 +08:00
23 changed files with 1013 additions and 399 deletions
+72 -172
View File
@@ -445,41 +445,17 @@ namespace Ink_Canvas
try
{
LogHelper.WriteLogToFile("开始创建启动画面...");
UpdateHeartbeatOnUIThread();
_splashScreen = new SplashScreen();
LogHelper.WriteLogToFile("启动画面对象创建成功,准备显示...");
UpdateHeartbeatOnUIThread();
if (Current?.Dispatcher != null && !Current.Dispatcher.HasShutdownStarted)
{
Current.Dispatcher.Invoke(() =>
{
_splashScreen.Show();
_isSplashScreenShown = true;
Current.Dispatcher.Invoke(() => { }, DispatcherPriority.Render);
UpdateHeartbeatOnUIThread();
LogHelper.WriteLogToFile("启动画面已显示");
}, DispatcherPriority.Normal);
}
else
{
_splashScreen.Show();
_isSplashScreenShown = true;
UpdateHeartbeatOnUIThread();
LogHelper.WriteLogToFile("启动画面已显示");
}
_splashScreen.Show();
_isSplashScreenShown = true;
splashScreenStartTime = DateTime.Now;
LogHelper.WriteLogToFile("启动画面已显示");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"显示启动画面失败: {ex.Message}", LogHelper.LogType.Error);
LogHelper.WriteLogToFile($"异常堆栈: {ex.StackTrace}", LogHelper.LogType.Error);
UpdateHeartbeatOnUIThread();
}
}
@@ -666,35 +642,26 @@ namespace Ink_Canvas
async void App_Startup(object sender, StartupEventArgs e)
{
// 初始化应用启动时间
appStartTime = DateTime.Now;
UpdateHeartbeatOnUIThread();
// 根据设置决定是否显示启动画面
if (ShouldShowSplashScreen())
{
ShowSplashScreen();
SetSplashMessage("正在启动 Ink Canvas...");
SetSplashProgress(20);
UpdateHeartbeatOnUIThread();
await Task.Delay(500);
Application.Current.Dispatcher.Invoke(() =>
{
UpdateHeartbeatOnUIThread();
}, DispatcherPriority.Render);
// 强制刷新UI,确保启动画面显示
Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.Render);
}
UpdateHeartbeatOnUIThread();
System.Threading.Thread.Sleep(500);
RootPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
LogHelper.NewLog(string.Format("Ink Canvas Starting (Version: {0})", Assembly.GetExecutingAssembly().GetName().Version));
UpdateHeartbeatOnUIThread();
// 检查是否为最终应用启动(更新后的应用)
bool isFinalApp = e.Args.Contains("--final-app");
bool skipMutexCheck = e.Args.Contains("--skip-mutex-check");
@@ -721,61 +688,49 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile("App | 检测到最终应用启动(更新后的应用)");
}
// 释放IACore相关DLL
if (_isSplashScreenShown)
{
SetSplashMessage("正在初始化组件...");
SetSplashProgress(40);
await Task.Delay(500);
}
UpdateHeartbeatOnUIThread();
try
{
IACoreDllExtractor.ExtractIACoreDlls();
UpdateHeartbeatOnUIThread();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"释放IACore DLL时出错: {ex.Message}", LogHelper.LogType.Error);
UpdateHeartbeatOnUIThread();
}
// 释放UIAccess DLL
if (_isSplashScreenShown)
{
SetSplashMessage("正在初始化组件...");
SetSplashProgress(50);
await Task.Delay(300);
}
UpdateHeartbeatOnUIThread();
try
{
UIAccessDllExtractor.ExtractUIAccessDlls();
UpdateHeartbeatOnUIThread();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"释放UIAccess DLL时出错: {ex.Message}", LogHelper.LogType.Error);
UpdateHeartbeatOnUIThread();
}
// 记录应用启动(设备标识符)
if (_isSplashScreenShown)
{
SetSplashMessage("正在加载配置...");
SetSplashProgress(60);
await Task.Delay(500);
}
UpdateHeartbeatOnUIThread();
DeviceIdentifier.RecordAppLaunch();
LogHelper.WriteLogToFile($"App | 设备ID: {DeviceIdentifier.GetDeviceId()}");
LogHelper.WriteLogToFile($"App | 使用频率: {DeviceIdentifier.GetUsageFrequency()}");
LogHelper.WriteLogToFile($"App | 更新优先级: {DeviceIdentifier.GetUpdatePriority()}");
UpdateHeartbeatOnUIThread();
// 处理更新模式启动
bool isUpdateMode = AutoUpdateHelper.HandleUpdateModeStartup(e.Args);
@@ -1027,26 +982,22 @@ namespace Ink_Canvas
StartArgs = e.Args;
// 在非更新模式下创建主窗口
if (_isSplashScreenShown)
{
SetSplashMessage("正在初始化主界面...");
SetSplashProgress(80);
await Task.Delay(500);
}
UpdateHeartbeatOnUIThread();
var mainWindow = new MainWindow();
MainWindow = mainWindow;
UpdateHeartbeatOnUIThread();
// 主窗口加载完成后关闭启动画面
mainWindow.Loaded += (s, args) =>
{
UpdateHeartbeatOnUIThread();
isStartupPhase = false;
LogHelper.WriteLogToFile("心跳监控 | 主窗口加载完成,启动阶段结束");
isStartupComplete = true;
startupCompleteHeartbeat = DateTime.Now;
LogHelper.WriteLogToFile($"启动完成心跳已记录,启动画面显示时长: {(startupCompleteHeartbeat - splashScreenStartTime).TotalSeconds:F2}秒");
if (_isSplashScreenShown)
{
@@ -1058,15 +1009,10 @@ namespace Ink_Canvas
{
SetSplashMessage("启动完成!");
SetSplashProgress(100);
UpdateHeartbeatOnUIThread();
// 延迟关闭启动画面,让用户看到完成消息
Task.Delay(500).ContinueWith(__ =>
{
Dispatcher.Invoke(() =>
{
CloseSplashScreen();
UpdateHeartbeatOnUIThread();
});
Dispatcher.Invoke(() => CloseSplashScreen());
});
});
});
@@ -1074,37 +1020,30 @@ namespace Ink_Canvas
};
mainWindow.Show();
UpdateHeartbeatOnUIThread();
// 注册.icstk文件关联
try
{
LogHelper.WriteLogToFile("开始注册.icstk文件关联");
FileAssociationManager.RegisterFileAssociation();
FileAssociationManager.ShowFileAssociationStatus();
UpdateHeartbeatOnUIThread();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注册文件关联时出错: {ex.Message}", LogHelper.LogType.Error);
UpdateHeartbeatOnUIThread();
}
// 启动IPC监听器
try
{
LogHelper.WriteLogToFile("启动IPC监听器");
FileAssociationManager.StartIpcListener();
UpdateHeartbeatOnUIThread();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动IPC监听器时出错: {ex.Message}", LogHelper.LogType.Error);
UpdateHeartbeatOnUIThread();
}
UpdateHeartbeatOnUIThread();
LogHelper.WriteLogToFile("心跳监控 | 应用启动流程完成");
}
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
@@ -1132,106 +1071,71 @@ namespace Ink_Canvas
NoAction
}
// 心跳相关
private static Timer heartbeatTimer;
private static DateTime lastHeartbeat = DateTime.Now;
private static Timer watchdogTimer;
private static DispatcherTimer uiHeartbeatTimer;
private static bool isStartupPhase = true;
private static DateTime startupStartTime = DateTime.Now;
public static void UpdateHeartbeatOnUIThread()
{
try
{
if (Current?.Dispatcher != null && !Current.Dispatcher.HasShutdownStarted)
{
Current.Dispatcher.Invoke(() =>
{
lastHeartbeat = DateTime.Now;
Current.Dispatcher.Invoke(() => { }, DispatcherPriority.Background);
}, DispatcherPriority.Normal);
}
else
{
lastHeartbeat = DateTime.Now;
}
}
catch
{
lastHeartbeat = DateTime.Now;
}
}
private static bool isStartupComplete = false;
private static DateTime startupCompleteHeartbeat = DateTime.MinValue;
private static DateTime splashScreenStartTime = DateTime.MinValue;
private void StartHeartbeatMonitor()
{
startupStartTime = DateTime.Now;
heartbeatTimer = new Timer(_ =>
{
if ((DateTime.Now - lastHeartbeat).TotalSeconds > 2)
{
lastHeartbeat = DateTime.Now;
}
}, null, 0, 1000);
if (Current?.Dispatcher != null)
{
uiHeartbeatTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(500)
};
uiHeartbeatTimer.Tick += (s, e) =>
{
lastHeartbeat = DateTime.Now;
Current.Dispatcher.Invoke(() => { }, DispatcherPriority.Background);
};
uiHeartbeatTimer.Start();
}
heartbeatTimer = new Timer(_ => lastHeartbeat = DateTime.Now, null, 0, 1000);
watchdogTimer = new Timer(_ =>
{
try
if (_isSplashScreenShown && splashScreenStartTime != DateTime.MinValue)
{
double timeSinceLastHeartbeat = (DateTime.Now - lastHeartbeat).TotalSeconds;
double timeSinceStartup = (DateTime.Now - startupStartTime).TotalSeconds;
double timeoutThreshold = isStartupPhase ? 30.0 : 10.0;
if (isStartupPhase && timeSinceStartup > 60)
if (!isStartupComplete)
{
isStartupPhase = false;
LogHelper.WriteLogToFile("心跳监控 | 启动阶段结束,切换到正常运行模式");
}
if (timeSinceLastHeartbeat > timeoutThreshold)
{
string phase = isStartupPhase ? "启动阶段" : "运行阶段";
LogHelper.WriteLogToFile($"心跳监控 | 检测到主线程无响应({phase},超时{timeoutThreshold}秒),准备自动重启。");
LogHelper.WriteLogToFile($"心跳监控 | 距离上次心跳: {timeSinceLastHeartbeat:F1}秒,距离启动: {timeSinceStartup:F1}秒");
SyncCrashActionFromSettings();
if (CrashAction == CrashActionType.SilentRestart)
TimeSpan elapsedSinceSplashStart = DateTime.Now - splashScreenStartTime;
if (elapsedSinceSplashStart.TotalMinutes >= 2)
{
StartupCount.Increment();
if (StartupCount.GetCount() >= 5)
LogHelper.WriteLogToFile($"检测到启动假死:启动画面已显示{elapsedSinceSplashStart.TotalMinutes:F2}分钟,但未收到启动完成心跳,自动重启。", LogHelper.LogType.Error);
SyncCrashActionFromSettings();
if (CrashAction == CrashActionType.SilentRestart)
{
MessageBox.Show("检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。", "重启次数过多", MessageBoxButton.OK, MessageBoxImage.Error);
StartupCount.Reset();
StartupCount.Increment();
if (StartupCount.GetCount() >= 5)
{
MessageBox.Show("检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。", "重启次数过多", MessageBoxButton.OK, MessageBoxImage.Error);
StartupCount.Reset();
Environment.Exit(1);
}
try
{
string exePath = Process.GetCurrentProcess().MainModule.FileName;
Process.Start(exePath);
}
catch { }
Environment.Exit(1);
}
try
{
string exePath = Process.GetCurrentProcess().MainModule.FileName;
Process.Start(exePath);
}
catch { }
Environment.Exit(1);
return;
}
}
}
catch (Exception ex)
if (isStartupComplete && (DateTime.Now - lastHeartbeat).TotalSeconds > 10)
{
LogHelper.WriteLogToFile($"心跳监控 | 检测过程出错: {ex.Message}", LogHelper.LogType.Warning);
LogHelper.NewLog("检测到主线程无响应,自动重启。");
SyncCrashActionFromSettings();
if (CrashAction == CrashActionType.SilentRestart)
{
StartupCount.Increment();
if (StartupCount.GetCount() >= 5)
{
MessageBox.Show("检测到程序已连续重启5次,已停止自动重启。请联系开发者或检查系统环境。", "重启次数过多", MessageBoxButton.OK, MessageBoxImage.Error);
StartupCount.Reset();
Environment.Exit(1);
}
try
{
string exePath = Process.GetCurrentProcess().MainModule.FileName;
Process.Start(exePath);
}
catch { }
Environment.Exit(1);
}
}
}, null, 0, 3000);
}
@@ -1303,20 +1207,14 @@ namespace Ink_Canvas
private void App_Exit(object sender, ExitEventArgs e)
{
// 仅在软件内主动退出时关闭看门狗,并写入退出信号
try
{
heartbeatTimer?.Dispose();
watchdogTimer?.Dispose();
uiHeartbeatTimer?.Stop();
uiHeartbeatTimer = null;
}
catch { }
try
{
// 记录应用退出状态
string exitType = IsAppExitByUser ? "用户主动退出" : "应用程序退出";
WriteCrashLog($"{exitType},退出代码: {e.ApplicationExitCode}");
// 记录应用退出(设备标识符)
try
{
DeviceIdentifier.RecordAppExit();
@@ -1329,6 +1227,7 @@ namespace Ink_Canvas
if (IsAppExitByUser)
{
// 写入退出信号文件,通知看门狗正常退出
StartupCount.Reset();
File.WriteAllText(watchdogExitSignalFile, "exit");
if (watchdogProcess != null && !watchdogProcess.HasExited)
@@ -1339,6 +1238,7 @@ namespace Ink_Canvas
}
catch (Exception ex)
{
// 尝试记录最后的错误
try
{
LogHelper.WriteLogToFile($"退出处理时发生错误: {ex.Message}", LogHelper.LogType.Error);
+2 -2
View File
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.18.4")]
[assembly: AssemblyFileVersion("1.7.18.4")]
[assembly: AssemblyVersion("1.7.18.5")]
[assembly: AssemblyFileVersion("1.7.18.5")]
+115 -20
View File
@@ -105,6 +105,71 @@ namespace Ink_Canvas.Helpers
}
}
},
{ UpdateChannel.Preview, new List<UpdateLineGroup>
{
new UpdateLineGroup
{
GroupName = "GitHub主线",
VersionUrl = "https://github.com/InkCanvasForClass/community-beta/raw/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://github.com/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://github.com/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
GroupName = "bgithub备用",
VersionUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://bgithub.xyz/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
GroupName = "kkgithub线路",
VersionUrl = "https://kkgithub.com/InkCanvasForClass/community-beta/raw/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://kkgithub.com/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://kkgithub.com/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
GroupName = "智教联盟",
DownloadUrlFormat = "https://get.smart-teach.cn/d/Ningbo-S3/shared/jiangling/community-beta/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
GroupName = "inkeys",
DownloadUrlFormat = "https://iccce.inkeys.top/Beta/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://bgithub.xyz/InkCanvasForClass/community-beta/raw/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
GroupName = "gh-proxy",
VersionUrl = "https://gh-proxy.org/https://raw.githubusercontent.com/InkCanvasForClass/community-beta/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://gh-proxy.org/https://github.com/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://gh-proxy.org/https://raw.githubusercontent.com/InkCanvasForClass/community-beta/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
GroupName = "hk.gh-proxy",
VersionUrl = "https://hk.gh-proxy.org/https://raw.githubusercontent.com/InkCanvasForClass/community-beta/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://hk.gh-proxy.org/https://github.com/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://hk.gh-proxy.org/https://raw.githubusercontent.com/InkCanvasForClass/community-beta/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
GroupName = "cdn.gh-proxy",
VersionUrl = "https://cdn.gh-proxy.org/https://raw.githubusercontent.com/InkCanvasForClass/community-beta/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://cdn.gh-proxy.org/https://github.com/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://cdn.gh-proxy.org/https://raw.githubusercontent.com/InkCanvasForClass/community-beta/refs/heads/main/UpdateLog.md"
},
new UpdateLineGroup
{
GroupName = "edgeone.gh-proxy",
VersionUrl = "https://edgeone.gh-proxy.org/https://raw.githubusercontent.com/InkCanvasForClass/community-beta/refs/heads/main/AutomaticUpdateVersionControl.txt",
DownloadUrlFormat = "https://edgeone.gh-proxy.org/https://github.com/InkCanvasForClass/community-beta/releases/download/{0}/InkCanvasForClass.CE.{0}.zip",
LogUrl = "https://edgeone.gh-proxy.org/https://raw.githubusercontent.com/InkCanvasForClass/community-beta/refs/heads/main/UpdateLog.md"
}
}
},
{ UpdateChannel.Beta, new List<UpdateLineGroup>
{
new UpdateLineGroup
@@ -527,7 +592,7 @@ namespace Ink_Canvas.Helpers
{
try
{
string apiUrl = channel == UpdateChannel.Beta
string apiUrl = (channel == UpdateChannel.Beta || channel == UpdateChannel.Preview)
? "https://api.github.com/repos/InkCanvasForClass/community-beta/releases"
: "https://api.github.com/repos/InkCanvasForClass/community/releases";
using (var client = new HttpClient())
@@ -569,28 +634,58 @@ namespace Ink_Canvas.Helpers
{
try
{
string apiUrl = channel == UpdateChannel.Beta
? "https://api.github.com/repos/InkCanvasForClass/community-beta/releases/latest"
: "https://api.github.com/repos/InkCanvasForClass/community/releases/latest";
using (var client = new HttpClient())
if (channel == UpdateChannel.Beta)
{
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub API调用");
var response = await client.GetStringAsync(apiUrl);
var json = JObject.Parse(response);
string version = json["tag_name"]?.ToString();
string releaseNotes = json["body"]?.ToString();
string downloadUrl = json["assets"]?.First?["browser_download_url"]?.ToString();
// 解析发布时间
DateTime? releaseTime = null;
if (json["published_at"] != null && DateTime.TryParse(json["published_at"].ToString(), out DateTime parsedTime))
string apiUrl = "https://api.github.com/repos/InkCanvasForClass/community-beta/releases";
using (var client = new HttpClient())
{
releaseTime = parsedTime;
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub API调用");
var response = await client.GetStringAsync(apiUrl);
var releases = JArray.Parse(response);
if (releases.Count > 0)
{
var latestRelease = releases[0];
string version = latestRelease["tag_name"]?.ToString();
string releaseNotes = latestRelease["body"]?.ToString();
string downloadUrl = latestRelease["assets"]?.First?["browser_download_url"]?.ToString();
DateTime? releaseTime = null;
if (latestRelease["published_at"] != null && DateTime.TryParse(latestRelease["published_at"].ToString(), out DateTime parsedTime))
{
releaseTime = parsedTime;
}
if (!string.IsNullOrEmpty(version) && !string.IsNullOrEmpty(downloadUrl))
return (version, downloadUrl, releaseNotes, releaseTime);
}
}
}
else
{
string apiUrl = channel == UpdateChannel.Preview
? "https://api.github.com/repos/InkCanvasForClass/community-beta/releases/latest"
: "https://api.github.com/repos/InkCanvasForClass/community/releases/latest";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub API调用");
var response = await client.GetStringAsync(apiUrl);
var json = JObject.Parse(response);
string version = json["tag_name"]?.ToString();
string releaseNotes = json["body"]?.ToString();
string downloadUrl = json["assets"]?.First?["browser_download_url"]?.ToString();
if (!string.IsNullOrEmpty(version) && !string.IsNullOrEmpty(downloadUrl))
return (version, downloadUrl, releaseNotes, releaseTime);
DateTime? releaseTime = null;
if (json["published_at"] != null && DateTime.TryParse(json["published_at"].ToString(), out DateTime parsedTime))
{
releaseTime = parsedTime;
}
if (!string.IsNullOrEmpty(version) && !string.IsNullOrEmpty(downloadUrl))
return (version, downloadUrl, releaseNotes, releaseTime);
}
}
}
catch (Exception ex)
@@ -2023,7 +2118,7 @@ namespace Ink_Canvas.Helpers
var result = new List<(string, string, string)>();
try
{
string apiUrl = channel == UpdateChannel.Beta
string apiUrl = (channel == UpdateChannel.Beta || channel == UpdateChannel.Preview)
? "https://api.github.com/repos/InkCanvasForClass/community-beta/releases"
: "https://api.github.com/repos/InkCanvasForClass/community/releases";
using (var client = new HttpClient())
+17
View File
@@ -960,6 +960,23 @@ namespace Ink_Canvas.Helpers
}
}
/// <summary>
/// 获取上次更新检查时间
/// </summary>
public static DateTime GetLastUpdateCheck()
{
try
{
var stats = LoadUsageStats();
return stats.LastUpdateCheck;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 获取上次更新检查时间失败: {ex.Message}", LogHelper.LogType.Error);
return DateTime.MinValue;
}
}
/// <summary>
/// 从备份文件恢复使用统计数据
+21 -123
View File
@@ -291,18 +291,13 @@ namespace Ink_Canvas.Helpers
}
return null;
}
catch (COMException ex)
catch (COMException)
{
var hr = (uint)ex.HResult;
if (hr == 0x800401E3 || hr == 0x800401F3 || hr == 0x800401E4)
{
return TryConnectToPowerPointViaROT();
}
return null;
}
catch (InvalidCastException)
{
return TryConnectToPowerPointViaROT();
return null;
}
catch (Exception)
{
@@ -310,20 +305,6 @@ namespace Ink_Canvas.Helpers
}
}
private Microsoft.Office.Interop.PowerPoint.Application TryConnectToPowerPointViaROT()
{
try
{
var pptApp = PPTROTConnectionHelper.TryConnectViaROT(IsSupportWPS);
return pptApp;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"ROT 备用方法连接异常: {ex}", LogHelper.LogType.Error);
return null;
}
}
private Microsoft.Office.Interop.PowerPoint.Application TryConnectToWPS()
{
try
@@ -337,24 +318,13 @@ namespace Ink_Canvas.Helpers
}
return null;
}
catch (COMException ex)
catch (COMException)
{
var hr = (uint)ex.HResult;
if (hr == 0x800401E3 || hr == 0x800401F3 || hr == 0x800401E4)
{
// WPS COM注册损坏,尝试使用ROT备用方法
return TryConnectToWPSViaROT();
}
if (hr != 0x80004005 && hr != 0x800706B5 && hr != 0x8001010E)
{
LogHelper.WriteLogToFile($"连接WPS失败: {ex}", LogHelper.LogType.Warning);
}
return null;
}
catch (InvalidCastException)
{
// WPS COM对象类型转换失败,尝试使用ROT备用方法
return TryConnectToWPSViaROT();
return null;
}
catch (Exception)
{
@@ -362,20 +332,6 @@ namespace Ink_Canvas.Helpers
}
}
private Microsoft.Office.Interop.PowerPoint.Application TryConnectToWPSViaROT()
{
try
{
var wpsApp = PPTROTConnectionHelper.TryConnectViaROT(true);
return wpsApp;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"ROT 备用方法连接 WPS 异常: {ex}", LogHelper.LogType.Error);
return null;
}
}
private void ConnectToPPT(Microsoft.Office.Interop.PowerPoint.Application pptApp)
{
try
@@ -415,29 +371,7 @@ namespace Ink_Canvas.Helpers
if (IsInSlideShow)
{
object slideShowWindows = null;
object slideShowWindow = null;
try
{
slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
if (ssw.Count > 0)
{
slideShowWindow = ssw[1];
if (slideShowWindow != null)
{
OnSlideShowBegin(slideShowWindow as SlideShowWindow);
}
}
}
}
finally
{
SafeReleaseComObject(slideShowWindow);
SafeReleaseComObject(slideShowWindows);
}
OnSlideShowBegin(PPTApplication.SlideShowWindows[1]);
}
else if (CurrentPresentation != null)
{
@@ -1124,55 +1058,35 @@ namespace Ink_Canvas.Helpers
/// </summary>
public Presentation GetCurrentActivePresentation()
{
object slideShowWindows = null;
object slideShowWindow = null;
object view = null;
object slide = null;
object activeWindow = null;
object presentation = null;
try
{
if (!IsConnected || PPTApplication == null) return null;
if (!Marshal.IsComObject(PPTApplication)) return null;
if (IsInSlideShow)
if (IsInSlideShow && PPTApplication.SlideShowWindows.Count > 0)
{
slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
try
{
dynamic ssw = slideShowWindows;
if (ssw.Count > 0)
var slideShowWindow = PPTApplication.SlideShowWindows[1];
if (slideShowWindow?.View != null)
{
slideShowWindow = ssw[1];
if (slideShowWindow != null)
{
dynamic sswObj = slideShowWindow;
view = sswObj.View;
if (view != null)
{
dynamic viewObj = view;
slide = viewObj.Slide;
if (slide != null)
{
dynamic slideObj = slide;
presentation = slideObj.Parent;
return presentation as Presentation;
}
}
}
return (Presentation)slideShowWindow.View.Slide.Parent;
}
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x80048240)
{
return null;
}
throw;
}
}
activeWindow = PPTApplication.ActiveWindow;
if (activeWindow != null)
if (PPTApplication.ActiveWindow?.Presentation != null)
{
dynamic aw = activeWindow;
presentation = aw.Presentation;
if (presentation != null)
{
return presentation as Presentation;
}
return PPTApplication.ActiveWindow.Presentation;
}
return CurrentPresentation;
@@ -1184,10 +1098,6 @@ namespace Ink_Canvas.Helpers
{
DisconnectFromPPT();
}
if (hr == 0x80048240)
{
return null;
}
LogHelper.WriteLogToFile($"获取当前活跃演示文稿失败: {comEx.Message}", LogHelper.LogType.Warning);
return CurrentPresentation;
}
@@ -1196,18 +1106,6 @@ namespace Ink_Canvas.Helpers
LogHelper.WriteLogToFile($"获取当前活跃演示文稿失败: {ex}", LogHelper.LogType.Error);
return CurrentPresentation;
}
finally
{
if (presentation != null && !ReferenceEquals(presentation, CurrentPresentation))
{
SafeReleaseComObject(presentation);
}
SafeReleaseComObject(slide);
SafeReleaseComObject(view);
SafeReleaseComObject(slideShowWindow);
SafeReleaseComObject(slideShowWindows);
SafeReleaseComObject(activeWindow);
}
}
/// <summary>
+5 -3
View File
@@ -642,10 +642,12 @@
<ui:RadioButtons x:Name="UpdateChannelSelector" Margin="0,4,0,0">
<RadioButton Content="稳定版 (Release)" GroupName="UpdateChannel"
Tag="Release" Checked="UpdateChannelSelector_Checked"/>
<RadioButton Content="预览版 (Preview)" GroupName="UpdateChannel"
Tag="Preview" Checked="UpdateChannelSelector_Checked"/>
<RadioButton Content="测试版 (Beta)" GroupName="UpdateChannel"
Tag="Beta" Checked="UpdateChannelSelector_Checked"/>
</ui:RadioButtons>
<TextBlock Text="# 稳定版提供可靠更新,测试版提供新功能抢先体验" TextWrapping="Wrap" Foreground="#a1a1aa" />
<TextBlock Text="# 稳定版提供可靠更新,预览版提供新功能体验同时拥有相较Beta版更强的稳定性,测试版提供新功能抢先体验" TextWrapping="Wrap" Foreground="#a1a1aa" />
</ui:SimpleStackPanel>
<!-- 手动更新按钮 -->
@@ -2570,7 +2572,7 @@
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold" />
</ui:SimpleStackPanel>
<TextBlock TextWrapping="Wrap" Foreground="#a1a1aa"
Text="# 避免画布全屏,可解决开启dock栏软件浮动工具栏偏高的问题(但是进入批注也仍然会偏高),重启icc后生效。" />
Text="# 避免画布全屏,会导致左侧或顶部有AppBarDock栏软件)时导致浮动工具栏偏,重启icc后生效。" />
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Foreground="#fafafa" Text="启用EdgeGestureUtil"
@@ -4904,7 +4906,7 @@
<Border ClipToBounds="True" Name="BoardTwoFingerGestureBorder"
Margin="-115,-161,-55,50"
CornerRadius="8"
Background="{DynamicResource FloatBarBackground}" Opacity="1" BorderBrush="{DynamicResource FloatBarBorderBrush}"
Background="{DynamicResource FloatBarBackground}" Opacity="1" BorderBrush="#2563eb"
BorderThickness="1">
<ui:SimpleStackPanel Margin="0">
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
+1 -1
View File
@@ -342,7 +342,7 @@ namespace Ink_Canvas
/// <summary>
/// 根据DPI缩放因子调整TimerContainer的尺寸
/// </summary>
private void AdjustTimerContainerSize()
public void AdjustTimerContainerSize()
{
try
{
+2
View File
@@ -63,6 +63,8 @@ namespace Ink_Canvas
foldFloatingBarByUser = true;
unfoldFloatingBarByUser = false;
if (isFloatingBarFolded) return;
if (isFloatingBarChangingHideMode) return;
await Dispatcher.InvokeAsync(() =>
@@ -431,8 +431,9 @@ namespace Ink_Canvas
LassoSelectIconGeometry.Brush = new SolidColorBrush(FloatBarForegroundColor);
LassoSelectIconGeometry.Geometry = Geometry.Parse(GetCorrectIcon("lassoSelect", false));
// 根据主题设置颜色
if (Settings.Appearance.Theme == 1) // 深色主题
bool isDarkThemeForButtons = Settings.Appearance.Theme == 1 ||
(Settings.Appearance.Theme == 2 && !IsSystemThemeLight());
if (isDarkThemeForButtons)
{
BoardPen.Background = new SolidColorBrush(Color.FromRgb(42, 42, 42));
BoardSelect.Background = new SolidColorBrush(Color.FromRgb(42, 42, 42));
@@ -447,7 +448,7 @@ namespace Ink_Canvas
BoardEraser.BorderBrush = new SolidColorBrush(Color.FromRgb(85, 85, 85));
BoardPen.BorderBrush = new SolidColorBrush(Color.FromRgb(85, 85, 85));
}
else // 浅色主题或跟随系统
else
{
BoardPen.Background = new SolidColorBrush(Color.FromRgb(244, 244, 245));
BoardSelect.Background = new SolidColorBrush(Color.FromRgb(244, 244, 245));
@@ -539,15 +540,16 @@ namespace Ink_Canvas
CursorIconGeometry.Brush = new SolidColorBrush(highlightColor);
CursorIconGeometry.Geometry =
Geometry.Parse(GetCorrectIcon("cursor", true));
// 根据主题设置颜色
if (Settings.Appearance.Theme == 1) // 深色主题
bool isDarkThemeForCursor = Settings.Appearance.Theme == 1 ||
(Settings.Appearance.Theme == 2 && !IsSystemThemeLight());
if (isDarkThemeForCursor)
{
BoardPen.Background = new SolidColorBrush(Color.FromRgb(42, 42, 42));
BoardPen.BorderBrush = new SolidColorBrush(Color.FromRgb(85, 85, 85));
BoardPenGeometry.Brush = new SolidColorBrush(Color.FromRgb(255, 255, 255));
BoardPenLabel.Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
}
else // 浅色主题或跟随系统
else
{
BoardPen.Background = new SolidColorBrush(Color.FromRgb(244, 244, 245));
BoardPen.BorderBrush = new SolidColorBrush(Color.FromRgb(161, 161, 170));
+33 -15
View File
@@ -921,16 +921,22 @@ namespace Ink_Canvas
await Task.Delay(100);
await Application.Current.Dispatcher.InvokeAsync(() =>
{
PureViewboxFloatingBarMarginAnimationInDesktopMode();
if (Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode)
if (!isFloatingBarFolded)
{
Task.Delay(350).ContinueWith(_ =>
PureViewboxFloatingBarMarginAnimationInDesktopMode();
if (Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode)
{
Application.Current.Dispatcher.Invoke(() =>
Task.Delay(350).ContinueWith(_ =>
{
ViewboxFloatingBarMarginAnimation(-60);
Application.Current.Dispatcher.Invoke(() =>
{
if (!isFloatingBarFolded)
{
ViewboxFloatingBarMarginAnimation(-60);
}
});
});
});
}
}
});
}
@@ -1647,11 +1653,17 @@ namespace Ink_Canvas
SetCurrentToolMode(InkCanvasEditingMode.None);
await Task.Delay(150);
PureViewboxFloatingBarMarginAnimationInDesktopMode();
if (Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode)
{
await Task.Delay(350);
ViewboxFloatingBarMarginAnimation(-60);
if (!isFloatingBarFolded)
{
PureViewboxFloatingBarMarginAnimationInDesktopMode();
if (Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode)
{
await Task.Delay(350);
if (!isFloatingBarFolded)
{
ViewboxFloatingBarMarginAnimation(-60);
}
}
}
}
catch (Exception ex)
@@ -1669,11 +1681,17 @@ namespace Ink_Canvas
await HandleManualSlideShowEnd();
await Task.Delay(150);
PureViewboxFloatingBarMarginAnimationInDesktopMode();
if (Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode)
if (!isFloatingBarFolded)
{
await Task.Delay(350);
ViewboxFloatingBarMarginAnimation(-60);
PureViewboxFloatingBarMarginAnimationInDesktopMode();
if (Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode)
{
await Task.Delay(350);
if (!isFloatingBarFolded)
{
ViewboxFloatingBarMarginAnimation(-60);
}
}
}
}
}
+3 -1
View File
@@ -3827,7 +3827,9 @@ namespace Ink_Canvas
if (radioButton != null)
{
string channel = radioButton.Tag.ToString();
UpdateChannel newChannel = channel == "Beta" ? UpdateChannel.Beta : UpdateChannel.Release;
UpdateChannel newChannel = channel == "Beta" ? UpdateChannel.Beta
: channel == "Preview" ? UpdateChannel.Preview
: UpdateChannel.Release;
// 如果通道没有变化,不需要执行更新检查
if (Settings.Startup.UpdateChannel == newChannel)
+153
View File
@@ -507,6 +507,140 @@ namespace Ink_Canvas
return windowTitle.Length == 0 && windowRect.Height < 500;
}
/// <summary>
/// 检查是否存在应当被收纳应用的全屏窗口
/// </summary>
/// <returns>如果存在应当被收纳应用的全屏窗口返回true,否则返回false</returns>
private bool HasFullScreenWindowOfAutoFoldApps()
{
if (_windowOverviewModel == null) return false;
try
{
var fullScreenWindows = _windowOverviewModel.GetFullScreenWindows();
if (fullScreenWindows == null || fullScreenWindows.Count == 0) return false;
foreach (var window in fullScreenWindows)
{
var windowProcessName = window.ProcessName;
var windowRect = window.Rect;
if (windowProcessName == "EasiNote")
{
if (window.ProcessPath != "Unknown")
{
try
{
var versionInfo = FileVersionInfo.GetVersionInfo(window.ProcessPath);
string version = versionInfo.FileVersion;
string prodName = versionInfo.ProductName;
if (version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote)
{
return true;
}
else if (version.StartsWith("3.") && Settings.Automation.IsAutoFoldInEasiNote3)
{
return true;
}
else if (prodName.Contains("3C") && Settings.Automation.IsAutoFoldInEasiNote3C)
{
return true;
}
}
catch { }
}
}
else if (Settings.Automation.IsAutoFoldInEasiCamera && windowProcessName == "EasiCamera")
{
return true;
}
else if (Settings.Automation.IsAutoFoldInEasiNote5C && windowProcessName == "EasiNote5C")
{
return true;
}
else if (Settings.Automation.IsAutoFoldInSeewoPincoTeacher &&
(windowProcessName == "BoardService" || windowProcessName == "seewoPincoTeacher"))
{
return true;
}
else if (Settings.Automation.IsAutoFoldInHiteCamera && windowProcessName == "HiteCamera")
{
return true;
}
else if (Settings.Automation.IsAutoFoldInHiteTouchPro && windowProcessName == "HiteTouchPro")
{
return true;
}
else if (Settings.Automation.IsAutoFoldInWxBoardMain && windowProcessName == "WxBoardMain")
{
return true;
}
else if (Settings.Automation.IsAutoFoldInMSWhiteboard &&
(windowProcessName == "MicrosoftWhiteboard" || windowProcessName == "msedgewebview2"))
{
return true;
}
else if (Settings.Automation.IsAutoFoldInHiteLightBoard && windowProcessName == "HiteLightBoard")
{
return true;
}
else if (Settings.Automation.IsAutoFoldInAdmoxWhiteboard && windowProcessName == "Amdox.WhiteBoard")
{
return true;
}
else if (Settings.Automation.IsAutoFoldInAdmoxBooth && windowProcessName == "Amdox.Booth")
{
return true;
}
else if (Settings.Automation.IsAutoFoldInQPoint && windowProcessName == "QPoint")
{
return true;
}
else if (Settings.Automation.IsAutoFoldInYiYunVisualPresenter && windowProcessName == "YiYunVisualPresenter")
{
return true;
}
else if (Settings.Automation.IsAutoFoldInMaxHubWhiteboard && windowProcessName == "WhiteBoard")
{
if (window.ProcessPath != "Unknown")
{
try
{
var versionInfo = FileVersionInfo.GetVersionInfo(window.ProcessPath);
var version = versionInfo.FileVersion;
var prodName = versionInfo.ProductName;
if (version.StartsWith("6.") && prodName == "WhiteBoard")
{
return true;
}
}
catch { }
}
}
}
if (Settings.Automation.IsAutoFoldInOldZyBoard &&
(WinTabWindowsChecker.IsWindowExisted("WhiteBoard - DrawingWindow") ||
WinTabWindowsChecker.IsWindowExisted("InstantAnnotationWindow")))
{
var oldZyWindows = _windowOverviewModel.Windows.Where(w =>
(w.Title.Contains("WhiteBoard - DrawingWindow") || w.Title.Contains("InstantAnnotationWindow")) &&
w.IsFullScreen).ToList();
if (oldZyWindows.Count > 0)
{
return true;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检查全屏窗口失败: {ex.Message}", LogHelper.LogType.Error);
}
return false;
}
/// <summary>
/// 使用窗口预览模型检测前台窗口是否符合自动收纳要求(仅用于检测,不执行任何操作)
/// </summary>
@@ -690,6 +824,12 @@ namespace Ink_Canvas
if (isFloatingBarChangingHideMode) return;
try
{
if (HasFullScreenWindowOfAutoFoldApps())
{
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
return;
}
bool shouldAutoFold = CheckShouldAutoFoldByWindowPreview();
var windowProcessName = ForegroundWindowInfo.ProcessName();
var windowTitle = ForegroundWindowInfo.WindowTitle();
@@ -948,11 +1088,24 @@ namespace Ink_Canvas
else if (WinTabWindowsChecker.IsWindowExisted("幻灯片放映", false))
{
// 处于幻灯片放映状态
if (HasFullScreenWindowOfAutoFoldApps())
{
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
return;
}
if (!Settings.Automation.IsAutoFoldInPPTSlideShow && isFloatingBarFolded && !foldFloatingBarByUser)
UnFoldFloatingBar_MouseUp(new object(), null);
}
else
{
if (HasFullScreenWindowOfAutoFoldApps())
{
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
unfoldFloatingBarByUser = false;
return;
}
// 检查是否启用了软件退出后保持收纳模式
if (Settings.Automation.KeepFoldAfterSoftwareExit)
{
+2 -2
View File
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.7.18.4")]
[assembly: AssemblyFileVersion("1.7.18.4")]
[assembly: AssemblyVersion("1.7.18.5")]
[assembly: AssemblyFileVersion("1.7.18.5")]
+1
View File
@@ -140,6 +140,7 @@ namespace Ink_Canvas
public enum UpdateChannel
{
Release,
Preview,
Beta
}
@@ -681,6 +681,8 @@ namespace Ink_Canvas.Windows
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null)
{
mainWindow.AdjustTimerContainerSize();
// 显示主计时器窗口
var timerContainer = mainWindow.FindName("TimerContainer") as FrameworkElement;
if (timerContainer != null)
@@ -71,7 +71,7 @@ namespace Ink_Canvas.Windows.SettingsViews
{
var copyright = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyCopyrightAttribute>();
if (copyright != null && !string.IsNullOrEmpty(copyright.Copyright))
{
{
var copyrightText = copyright.Copyright;
AboutCopyright.Text = copyrightText;
AboutBottomCopyright.Text = copyrightText.Replace("Copyright ©", "© Copyright") + " 所有";
@@ -97,13 +97,13 @@ namespace Ink_Canvas.Windows.SettingsViews
if (File.Exists(filePath))
{
var bt = File.GetCreationTime(filePath);
var m = bt.Month.ToString().PadLeft(2, '0');
var d = bt.Day.ToString().PadLeft(2, '0');
var h = bt.Hour.ToString().PadLeft(2, '0');
var min = bt.Minute.ToString().PadLeft(2, '0');
var s = bt.Second.ToString().PadLeft(2, '0');
var m = bt.Month.ToString().PadLeft(2, '0');
var d = bt.Day.ToString().PadLeft(2, '0');
var h = bt.Hour.ToString().PadLeft(2, '0');
var min = bt.Minute.ToString().PadLeft(2, '0');
var s = bt.Second.ToString().PadLeft(2, '0');
AboutBuildTime.Text = $"{bt.Year}-{m}-{d} {h}:{min}:{s}";
}
}
else
{
AboutBuildTime.Text = "获取失败";
@@ -122,7 +122,7 @@ namespace Ink_Canvas.Windows.SettingsViews
try
{
var support = TouchTabletDetectHelper.IsTouchEnabled();
var touchcount = TouchTabletDetectHelper.GetTouchTabletDevices().Count;
var touchcount = TouchTabletDetectHelper.GetTouchTabletDevices().Count;
Dispatcher.BeginInvoke(() =>
{
@@ -145,7 +145,7 @@ namespace Ink_Canvas.Windows.SettingsViews
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(() =>
Dispatcher.BeginInvoke(() =>
AboutTouchTabletText.Text = "检测失败");
System.Diagnostics.Debug.WriteLine($"检测触摸设备失败: {ex.Message}");
}
@@ -208,12 +208,12 @@ namespace Ink_Canvas.Windows.SettingsViews
try
{
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(@"Select * From Win32_PnPEntity"))
collection = searcher.Get();
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(@"Select * From Win32_PnPEntity"))
collection = searcher.Get();
foreach (var device in collection)
{
foreach (var device in collection)
{
try
{
var name = device.GetPropertyValue("Name")?.ToString() ?? "";
@@ -231,7 +231,7 @@ namespace Ink_Canvas.Windows.SettingsViews
descLower.Contains("touch") ||
descLower.Contains("digitizer"))
{
devices.Add(new USBDeviceInfo(
devices.Add(new USBDeviceInfo(
device.GetPropertyValue("DeviceID")?.ToString() ?? "",
device.GetPropertyValue("PNPDeviceID")?.ToString() ?? "",
description
@@ -242,9 +242,9 @@ namespace Ink_Canvas.Windows.SettingsViews
{
continue;
}
}
}
collection.Dispose();
collection.Dispose();
}
catch (Exception ex)
{
@@ -271,10 +271,10 @@ namespace Ink_Canvas.Windows.SettingsViews
public static DateTimeOffset? GetBuildDateTime(Assembly assembly)
{
try
{
var path = assembly.Location;
{
var path = assembly.Location;
if (string.IsNullOrEmpty(path) || !File.Exists(path))
{
{
return null;
}
@@ -298,18 +298,18 @@ namespace Ink_Canvas.Windows.SettingsViews
fileStream.Read(fileHeader, 0, fileHeader.Length);
var pinnedBuffer = GCHandle.Alloc(fileHeader, GCHandleType.Pinned);
try
{
var coffHeader = (_IMAGE_FILE_HEADER)Marshal.PtrToStructure(pinnedBuffer.AddrOfPinnedObject(), typeof(_IMAGE_FILE_HEADER));
try
{
var coffHeader = (_IMAGE_FILE_HEADER)Marshal.PtrToStructure(pinnedBuffer.AddrOfPinnedObject(), typeof(_IMAGE_FILE_HEADER));
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var buildTime = epoch.AddSeconds(coffHeader.TimeDateStamp);
return new DateTimeOffset(buildTime.ToLocalTime());
}
finally
{
pinnedBuffer.Free();
}
}
finally
{
pinnedBuffer.Free();
}
}
}
catch
{
@@ -461,7 +461,7 @@ namespace Ink_Canvas.Windows.SettingsViews
System.Diagnostics.Debug.WriteLine($"打开贡献者名单失败: {ex.Message}");
}
}
/// <summary>
/// 应用主题
/// </summary>
@@ -77,6 +77,9 @@ namespace Ink_Canvas.Windows.SettingsViews
// 截图和屏幕捕捉
new SettingItem { Title = "截图和屏幕捕捉", Category = "截图和屏幕捕捉", ItemName = "SnapshotItem", Type = SettingItemType.Category },
// 更新中心
new SettingItem { Title = "更新中心", Category = "更新中心", ItemName = "UpdateCenterItem", Type = SettingItemType.Category },
// 关于
new SettingItem { Title = "关于 InkCanvasForClass", Category = "关于", ItemName = "AboutItem", Type = SettingItemType.Category },
};
@@ -120,14 +120,21 @@
</Grid>
<Border Height="1" Background="#ebebeb"/>
<Grid Height="54">
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Margin="18,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" Grid.Column="0" MaxWidth="350">
<TextBlock Foreground="#2e3436" FontSize="14.5" Text="更新通道" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="选择更新通道:稳定版提供可靠更新,测试版提供新功能抢先体验" HorizontalAlignment="Left"/>
<TextBlock Foreground="#9a9996" FontSize="11" Margin="0,3.5,0,0" Text="选择更新通道:稳定版提供可靠更新,测试版提供新功能抢先体验" HorizontalAlignment="Left" TextWrapping="Wrap"/>
</StackPanel>
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0">
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0" Grid.Column="1">
<Border x:Name="UpdateChannelReleaseBorder" Padding="13,7" CornerRadius="8" Background="#e1e1e1" Cursor="Hand" Tag="UpdateChannel_Release" Margin="0,0,8,8" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" FontWeight="Bold" Text="稳定版"/>
</Border>
<Border x:Name="UpdateChannelPreviewBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="UpdateChannel_Preview" Margin="0,0,8,8" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="预览版"/>
</Border>
<Border x:Name="UpdateChannelBetaBorder" Padding="13,7" CornerRadius="8" Cursor="Hand" Tag="UpdateChannel_Beta" Margin="0,0,8,8" MouseLeftButtonDown="OptionButton_Click">
<TextBlock Foreground="#2e3436" FontSize="14" Text="测试版"/>
</Border>
@@ -182,11 +182,15 @@ namespace Ink_Canvas.Windows.SettingsViews
// 更新通道
if (MainWindow.Settings.Startup.UpdateChannel == UpdateChannel.Release)
{
UpdateUpdateChannelButtons(true);
UpdateUpdateChannelButtons(UpdateChannel.Release);
}
else if (MainWindow.Settings.Startup.UpdateChannel == UpdateChannel.Preview)
{
UpdateUpdateChannelButtons(UpdateChannel.Preview);
}
else
{
UpdateUpdateChannelButtons(false);
UpdateUpdateChannelButtons(UpdateChannel.Beta);
}
// 仅PPT模式
@@ -356,7 +360,20 @@ namespace Ink_Canvas.Windows.SettingsViews
MainWindowSettingsHelper.InvokeMainWindowMethod("UpdateChannelSelector_Checked",
new System.Windows.Controls.RadioButton { Tag = "Release" }, e);
// 更新UI状态
UpdateUpdateChannelButtons(true);
UpdateUpdateChannelButtons(UpdateChannel.Release);
break;
case "UpdateChannel_Preview":
// 选择预览版
MainWindowSettingsHelper.UpdateSettingDirectly(() =>
{
MainWindow.Settings.Startup.UpdateChannel = UpdateChannel.Preview;
}, "UpdateChannelSelector");
// 调用 MainWindow 中的方法
MainWindowSettingsHelper.InvokeMainWindowMethod("UpdateChannelSelector_Checked",
new System.Windows.Controls.RadioButton { Tag = "Preview" }, e);
// 更新UI状态
UpdateUpdateChannelButtons(UpdateChannel.Preview);
break;
case "UpdateChannel_Beta":
@@ -369,7 +386,7 @@ namespace Ink_Canvas.Windows.SettingsViews
MainWindowSettingsHelper.InvokeMainWindowMethod("UpdateChannelSelector_Checked",
new System.Windows.Controls.RadioButton { Tag = "Beta" }, e);
// 更新UI状态
UpdateUpdateChannelButtons(false);
UpdateUpdateChannelButtons(UpdateChannel.Beta);
break;
}
}
@@ -377,34 +394,46 @@ namespace Ink_Canvas.Windows.SettingsViews
/// <summary>
/// 更新更新通道按钮状态
/// </summary>
private void UpdateUpdateChannelButtons(bool isReleaseSelected)
private void UpdateUpdateChannelButtons(UpdateChannel selectedChannel)
{
try
{
bool isDarkTheme = ThemeHelper.IsDarkTheme;
var selectedBrush = isDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225));
var unselectedBrush = isDarkTheme ? new SolidColorBrush(Color.FromRgb(35, 35, 35)) : new SolidColorBrush(Colors.Transparent);
if (UpdateChannelReleaseBorder != null)
{
UpdateChannelReleaseBorder.Background = isReleaseSelected
? (isDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)))
: (isDarkTheme ? new SolidColorBrush(Color.FromRgb(35, 35, 35)) : new SolidColorBrush(Colors.Transparent));
bool isSelected = selectedChannel == UpdateChannel.Release;
UpdateChannelReleaseBorder.Background = isSelected ? selectedBrush : unselectedBrush;
var textBlock = UpdateChannelReleaseBorder.Child as TextBlock;
if (textBlock != null)
{
textBlock.FontWeight = isReleaseSelected ? FontWeights.Bold : FontWeights.Normal;
textBlock.FontWeight = isSelected ? FontWeights.Bold : FontWeights.Normal;
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
}
}
if (UpdateChannelPreviewBorder != null)
{
bool isSelected = selectedChannel == UpdateChannel.Preview;
UpdateChannelPreviewBorder.Background = isSelected ? selectedBrush : unselectedBrush;
var textBlock = UpdateChannelPreviewBorder.Child as TextBlock;
if (textBlock != null)
{
textBlock.FontWeight = isSelected ? FontWeights.Bold : FontWeights.Normal;
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
}
}
if (UpdateChannelBetaBorder != null)
{
UpdateChannelBetaBorder.Background = !isReleaseSelected
? (isDarkTheme ? ThemeHelper.GetButtonBackgroundBrush() : new SolidColorBrush(Color.FromRgb(225, 225, 225)))
: (isDarkTheme ? new SolidColorBrush(Color.FromRgb(35, 35, 35)) : new SolidColorBrush(Colors.Transparent));
bool isSelected = selectedChannel == UpdateChannel.Beta;
UpdateChannelBetaBorder.Background = isSelected ? selectedBrush : unselectedBrush;
var textBlock = UpdateChannelBetaBorder.Child as TextBlock;
if (textBlock != null)
{
textBlock.FontWeight = !isReleaseSelected ? FontWeights.Bold : FontWeights.Normal;
textBlock.FontWeight = isSelected ? FontWeights.Bold : FontWeights.Normal;
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
}
}
@@ -478,11 +507,22 @@ namespace Ink_Canvas.Windows.SettingsViews
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
}
}
if (UpdateChannelPreviewBorder != null)
{
UpdateChannelPreviewBorder.Background = isDarkTheme
? ThemeHelper.GetButtonBackgroundBrush()
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
var textBlock = UpdateChannelPreviewBorder.Child as TextBlock;
if (textBlock != null)
{
textBlock.Foreground = ThemeHelper.GetTextPrimaryBrush();
}
}
if (UpdateChannelBetaBorder != null)
{
UpdateChannelBetaBorder.Background = isDarkTheme
? new SolidColorBrush(Color.FromRgb(35, 35, 35))
: new SolidColorBrush(Colors.Transparent);
? ThemeHelper.GetButtonBackgroundBrush()
: new SolidColorBrush(Color.FromRgb(225, 225, 225));
var textBlock = UpdateChannelBetaBorder.Child as TextBlock;
if (textBlock != null)
{
@@ -0,0 +1,129 @@
<UserControl x:Class="Ink_Canvas.Windows.SettingsViews.UpdateCenterPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ink_Canvas.Windows.SettingsViews"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
mc:Ignorable="d"
d:DesignHeight="950" d:DesignWidth="640">
<UserControl.Resources>
<Style x:Key="ToggleSwitchStyle" TargetType="Border">
<Setter Property="Width" Value="48"/>
<Setter Property="Height" Value="25"/>
<Setter Property="CornerRadius" Value="12"/>
<Setter Property="Padding" Value="3,0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Margin" Value="0,0,15,0"/>
</Style>
<Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<EventSetter Event="PreviewMouseDown" Handler="ScrollbarThumb_MouseDown"/>
<EventSetter Event="PreviewMouseUp" Handler="ScrollbarThumb_MouseUp"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Name="ScrollbarThumbEx"
SnapsToDevicePixels="True"
Background="#c3c3c3"
Opacity="0.5"
CornerRadius="1.5"
Height="{TemplateBinding Height}"
Width="3" HorizontalAlignment="Center"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ScrollBar}">
<EventSetter Event="Scroll" Handler="ScrollBar_Scroll"/>
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="false"/>
<Setter Property="Width" Value="8"/>
<Setter Property="Margin" Value="-6 3 0 0" />
<Setter Property="MinWidth" Value="{Binding Height, RelativeSource={RelativeSource Self}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="ScrollbarGrid" SnapsToDevicePixels="true">
<Border Width="3" CornerRadius="1.5" Background="#e0e0e0" Opacity="0" IsHitTestVisible="False" Margin="0 4 -2 4" x:Name="ScrollBarBorderTrackBackground"/>
<Border Padding="0 4" Background="Transparent" MouseEnter="ScrollBarTrack_MouseEnter"
MouseLeave="ScrollBarTrack_MouseLeave">
<Track x:Name="PART_Track"
IsDirectionReversed="true"
IsEnabled="True"
Width="6"
Margin="0,0,0,0"
HorizontalAlignment="Right">
<Track.DecreaseRepeatButton>
<RepeatButton Opacity="0" Command="{x:Static ScrollBar.PageUpCommand}" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Opacity="0" Command="{x:Static ScrollBar.PageDownCommand}" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb }" />
</Track.Thumb>
</Track>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<ScrollViewer ScrollChanged="ScrollViewerEx_ScrollChanged" IsManipulationEnabled="True" Name="UpdateCenterScrollViewerEx" IsDeferredScrollingEnabled="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" IsTabStop="False" TabIndex="-1" Margin="0,0,2,2">
<StackPanel Margin="60,24,60,24">
<TextBlock Foreground="#2e3436" FontSize="28" FontWeight="Bold" Text="InkCanvasForClass 更新" Margin="0,0,0,24"/>
<Border BorderBrush="#e6e6e6" BorderThickness="1" CornerRadius="8" Padding="20" Background="#FAFAFA" Margin="0,0,0,20">
<StackPanel>
<TextBlock Name="UpdateStatusText" Foreground="#2e3436" FontSize="18" FontWeight="SemiBold" Text="正在检查更新..." TextWrapping="Wrap" Margin="0,0,0,8"/>
<TextBlock Name="LastCheckTimeText" Foreground="#878787" FontSize="14" Text="上次检查时间: 从未" TextWrapping="Wrap"/>
</StackPanel>
</Border>
<StackPanel Orientation="Horizontal" Margin="0,0,0,24" VerticalAlignment="Center">
<TextBlock Name="CurrentVersionText" Foreground="#878787" FontSize="14" Text="当前版本: 正在获取..." TextWrapping="Wrap" VerticalAlignment="Center"/>
<ui:ProgressRing Name="LoadingSpinner" Width="16" Height="16" IsActive="False" Margin="12,0,0,0" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,24">
<Button Name="CheckUpdateButton" Content="检查更新" HorizontalAlignment="Left" Padding="20,10" FontSize="14" Cursor="Hand" Click="CheckUpdateButton_Click" Background="#F3F3F3" Foreground="#2e3436" BorderThickness="1" BorderBrush="#E1E1E1" Margin="0,0,12,0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4" Padding="{TemplateBinding Padding}">
<StackPanel Orientation="Horizontal">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Path Name="ChevronIcon" Data="M 0 0 L 4 4 L 0 8" Stroke="{TemplateBinding Foreground}" StrokeThickness="1.5" Margin="8,0,0,0" VerticalAlignment="Center" Visibility="Collapsed"/>
</StackPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
<StackPanel Name="UpdateAvailablePanel" Visibility="Collapsed" Margin="0,0,0,24">
<Border BorderBrush="#e6e6e6" BorderThickness="1" CornerRadius="8" Padding="16" Background="#F9F9F9">
<StackPanel>
<TextBlock Foreground="#2e3436" FontSize="16" FontWeight="SemiBold" Text="可用更新" Margin="0,0,0,8"/>
<TextBlock Name="LatestVersionText" Foreground="#878787" FontSize="14" Text="" TextWrapping="Wrap" Margin="0,0,0,16"/>
<Button Name="UpdateNowButton" Content="立即更新" HorizontalAlignment="Left" Padding="20,10" FontSize="14" Cursor="Hand" Click="UpdateNowButton_Click" Background="#0078D4" Foreground="White" BorderThickness="0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</Border>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>
@@ -0,0 +1,316 @@
using iNKORE.UI.WPF.Helpers;
using Ink_Canvas.Helpers;
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace Ink_Canvas.Windows.SettingsViews
{
public partial class UpdateCenterPanel : UserControl
{
private bool isLoaded = false;
public UpdateCenterPanel()
{
InitializeComponent();
Loaded += UpdateCenterPanel_Loaded;
}
private void UpdateCenterPanel_Loaded(object sender, RoutedEventArgs e)
{
if (!isLoaded)
{
isLoaded = true;
LoadSettings();
CheckUpdateStatus();
ApplyTheme();
}
}
private void LoadSettings()
{
try
{
var version = Assembly.GetExecutingAssembly().GetName().Version;
var platform = Environment.Is64BitOperatingSystem ? "windows-x64" : "windows-x86";
CurrentVersionText.Text = $"当前版本: v{version} | {platform}";
LoadLastCheckTime();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"UpdateCenterPanel 加载设置失败: {ex.Message}");
}
}
private void LoadLastCheckTime()
{
try
{
var lastCheckTime = DeviceIdentifier.GetLastUpdateCheck();
if (lastCheckTime != DateTime.MinValue)
{
var now = DateTime.Now;
var timeDiff = now - lastCheckTime;
if (timeDiff.TotalDays < 1 && lastCheckTime.Date == now.Date)
{
LastCheckTimeText.Text = $"上次检查时间: 今天, {lastCheckTime:HH:mm}";
}
else if (timeDiff.TotalDays < 2 && lastCheckTime.Date == now.Date.AddDays(-1))
{
LastCheckTimeText.Text = $"上次检查时间: 昨天, {lastCheckTime:HH:mm}";
}
else
{
LastCheckTimeText.Text = $"上次检查时间: {lastCheckTime:yyyy-MM-dd HH:mm}";
}
return;
}
LastCheckTimeText.Text = "上次检查时间: 从未";
}
catch
{
LastCheckTimeText.Text = "上次检查时间: 从未";
}
}
private void SaveLastCheckTime()
{
try
{
DeviceIdentifier.RecordUpdateCheck();
LoadLastCheckTime();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"保存上次检查时间失败: {ex.Message}");
}
}
private void CheckUpdateButton_Click(object sender, RoutedEventArgs e)
{
CheckUpdateStatus(true);
}
private void UpdateNowButton_Click(object sender, RoutedEventArgs e)
{
try
{
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null)
{
var method = typeof(MainWindow).GetMethod("CheckForUpdates", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (method != null)
{
method.Invoke(mainWindow, null);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"立即更新失败: {ex.Message}");
}
}
private void CheckUpdateStatus(bool manualCheck = false)
{
UpdateStatusText.Text = "正在检查更新...";
UpdateAvailablePanel.Visibility = Visibility.Collapsed;
CheckUpdateButton.IsEnabled = false;
StartLoadingAnimation();
Task.Run(async () =>
{
try
{
var updateChannel = UpdateChannel.Release;
if (MainWindow.Settings?.Startup != null)
{
updateChannel = MainWindow.Settings.Startup.UpdateChannel;
}
var (remoteVersion, lineGroup, releaseNotes) = await AutoUpdateHelper.CheckForUpdates(updateChannel, manualCheck, false);
Dispatcher.BeginInvoke(new Action(() =>
{
StopLoadingAnimation();
if (!string.IsNullOrEmpty(remoteVersion))
{
var localVersion = Assembly.GetExecutingAssembly().GetName().Version;
var localVersionStr = localVersion.ToString();
var remoteVersionStr = remoteVersion.TrimStart('v', 'V');
Version local = new Version(localVersionStr);
Version remote = new Version(remoteVersionStr);
if (remote > local)
{
UpdateStatusText.Text = "有可用更新";
LatestVersionText.Text = $"版本 {remoteVersion} 现已可用";
UpdateAvailablePanel.Visibility = Visibility.Visible;
}
else
{
UpdateStatusText.Text = "你使用的是最新版本";
UpdateAvailablePanel.Visibility = Visibility.Collapsed;
}
}
else
{
UpdateStatusText.Text = "你使用的是最新版本";
UpdateAvailablePanel.Visibility = Visibility.Collapsed;
}
if (manualCheck)
{
SaveLastCheckTime();
}
CheckUpdateButton.IsEnabled = true;
}));
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(new Action(() =>
{
StopLoadingAnimation();
UpdateStatusText.Text = "检查更新失败";
UpdateAvailablePanel.Visibility = Visibility.Collapsed;
CheckUpdateButton.IsEnabled = true;
}));
System.Diagnostics.Debug.WriteLine($"检查更新状态失败: {ex.Message}");
}
});
}
public event EventHandler<RoutedEventArgs> IsTopBarNeedShadowEffect;
public event EventHandler<RoutedEventArgs> IsTopBarNeedNoShadowEffect;
private void ScrollViewerEx_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
var scrollViewer = (ScrollViewer)sender;
if (scrollViewer.VerticalOffset >= 10)
{
IsTopBarNeedShadowEffect?.Invoke(this, new RoutedEventArgs());
}
else
{
IsTopBarNeedNoShadowEffect?.Invoke(this, new RoutedEventArgs());
}
}
private void ScrollBar_Scroll(object sender, RoutedEventArgs e)
{
var scrollbar = (ScrollBar)sender;
var scrollviewer = scrollbar.FindAscendant<ScrollViewer>();
if (scrollviewer != null) scrollviewer.ScrollToVerticalOffset(scrollbar.Track.Value);
}
private void ScrollBarTrack_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
var border = (Border)sender;
if (border.Child is Track track)
{
track.Width = 16;
track.Margin = new Thickness(0, 0, -2, 0);
var scrollbar = track.FindAscendant<ScrollBar>();
if (scrollbar != null) scrollbar.Width = 16;
}
}
private void ScrollBarTrack_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
var border = (Border)sender;
if (border.Child is Track track)
{
track.Width = 6;
track.Margin = new Thickness(0, 0, 0, 0);
var scrollbar = track.FindAscendant<ScrollBar>();
if (scrollbar != null) scrollbar.Width = 6;
}
}
private void ScrollbarThumb_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var thumb = (System.Windows.Controls.Primitives.Thumb)sender;
var border = thumb.Template.FindName("ScrollbarThumbEx", thumb);
if (border is Border borderElement)
{
borderElement.Background = new SolidColorBrush(Color.FromRgb(95, 95, 95));
}
}
private void ScrollbarThumb_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var thumb = (System.Windows.Controls.Primitives.Thumb)sender;
var border = thumb.Template.FindName("ScrollbarThumbEx", thumb);
if (border is Border borderElement)
{
borderElement.Background = new SolidColorBrush(Color.FromRgb(195, 195, 195));
}
}
public void ApplyTheme()
{
try
{
ThemeHelper.ApplyThemeToControl(this);
if (CheckUpdateButton != null)
{
CheckUpdateButton.Background = ThemeHelper.GetButtonBackgroundBrush();
CheckUpdateButton.Foreground = ThemeHelper.GetTextPrimaryBrush();
CheckUpdateButton.BorderBrush = ThemeHelper.GetBorderPrimaryBrush();
}
if (UpdateNowButton != null)
{
UpdateNowButton.Background = new SolidColorBrush(Color.FromRgb(0, 120, 212));
UpdateNowButton.Foreground = Brushes.White;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"UpdateCenterPanel 应用主题时出错: {ex.Message}");
}
}
private void StartLoadingAnimation()
{
try
{
if (LoadingSpinner != null)
{
LoadingSpinner.IsActive = true;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"启动加载动画失败: {ex.Message}");
}
}
private void StopLoadingAnimation()
{
try
{
if (LoadingSpinner != null)
{
LoadingSpinner.IsActive = false;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"停止加载动画失败: {ex.Message}");
}
}
}
}
@@ -196,6 +196,13 @@
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="UpdateCenterIcon">
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#FF222222" Geometry="F1 M16,16z M0,0z M8,0C3.58172,0 0,3.58172 0,8 0,12.4183 3.58172,16 8,16 12.4183,16 16,12.4183 16,8 16,3.58172 12.4183,0 8,0z M8,14C4.68629,14 2,11.3137 2,8 2,4.68629 4.68629,2 8,2 11.3137,2 14,4.68629 14,8 14,11.3137 11.3137,14 8,14z M8,4C5.79086,4 4,5.79086 4,8 4,10.2091 5.79086,12 8,12 10.2091,12 12,10.2091 12,8 12,5.79086 10.2091,4 8,4z M8,10C6.89543,10 6,9.10457 6,8 6,6.89543 6.89543,6 8,6 9.10457,6 10,6.89543 10,8 10,9.10457 9.10457,10 8,10z"/>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="AboutIcon">
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V16 H16 V0 H0 Z">
@@ -742,6 +749,11 @@
</ItemsControl>
</Grid>
</Border>
<!--UpdateCenterPanel-->
<Grid Margin="250,48,0,0" Visibility="Collapsed" Name="UpdateCenterPane">
<settingsViews:UpdateCenterPanel x:Name="UpdateCenterPanel"/>
</Grid>
<!--AboutPanel-->
<Grid Margin="250,48,0,0" Visibility="Collapsed" Name="AboutPane">
<settingsViews:AboutPanel x:Name="SettingsAboutPanel"/>
@@ -1,4 +1,4 @@
using iNKORE.UI.WPF.Helpers;
using iNKORE.UI.WPF.Helpers;
using Ink_Canvas.Windows.SettingsViews;
using System;
using System.Collections.Generic;
@@ -153,6 +153,14 @@ namespace Ink_Canvas.Windows
Type = SidebarItemType.Separator
});
SidebarItems.Add(new SidebarItem()
{
Type = SidebarItemType.Item,
Title = "更新中心",
Name = "UpdateCenterItem",
IconSource = FindResource("UpdateCenterIcon") as DrawingImage,
Selected = false,
});
SidebarItems.Add(new SidebarItem()
{
Type = SidebarItemType.Item,
Title = "关于 InkCanvasForClass",
@@ -161,6 +169,7 @@ namespace Ink_Canvas.Windows
Selected = false,
});
SettingsPanes = new Grid[] {
UpdateCenterPane,
AboutPane,
CanvasAndInkPane,
GesturesPane,
@@ -177,6 +186,7 @@ namespace Ink_Canvas.Windows
};
SettingsPaneScrollViewers = new ScrollViewer[] {
UpdateCenterPanel.UpdateCenterScrollViewerEx,
SettingsAboutPanel.AboutScrollViewerEx,
CanvasAndInkPanel.ScrollViewerEx,
GesturesPanel.ScrollViewerEx,
@@ -193,6 +203,7 @@ namespace Ink_Canvas.Windows
};
SettingsPaneTitles = new string[] {
"更新中心",
"关于",
"画板和墨迹",
"手势操作",
@@ -209,6 +220,7 @@ namespace Ink_Canvas.Windows
};
SettingsPaneNames = new string[] {
"UpdateCenterItem",
"AboutItem",
"CanvasAndInkItem",
"GesturesItem",
@@ -259,6 +271,8 @@ namespace Ink_Canvas.Windows
AdvancedPanel.IsTopBarNeedNoShadowEffect += (o, s) => DropShadowEffectTopBar.Opacity = 0;
SnapshotPanel.IsTopBarNeedShadowEffect += (o, s) => DropShadowEffectTopBar.Opacity = 0.25;
SnapshotPanel.IsTopBarNeedNoShadowEffect += (o, s) => DropShadowEffectTopBar.Opacity = 0;
UpdateCenterPanel.IsTopBarNeedShadowEffect += (o, s) => DropShadowEffectTopBar.Opacity = 0.25;
UpdateCenterPanel.IsTopBarNeedNoShadowEffect += (o, s) => DropShadowEffectTopBar.Opacity = 0;
_selectedSidebarItemName = "StartupItem";
@@ -307,7 +321,7 @@ namespace Ink_Canvas.Windows
// 使用反射调用所有面板的 ApplyTheme 方法(如果存在)
var panels = new UserControl[]
{
StartupPanel, CanvasAndInkPanel, GesturesPanel, InkRecognitionPanel,
UpdateCenterPanel, StartupPanel, CanvasAndInkPanel, GesturesPanel, InkRecognitionPanel,
ThemePanel, ShortcutsPanel, CrashActionPanel, PowerPointPanel,
AutomationPanel, LuckyRandomPanel, AdvancedPanel, SnapshotPanel,
SettingsAboutPanel, AppearancePanel, SearchPanelControl
@@ -353,11 +367,10 @@ namespace Ink_Canvas.Windows
if (isDarkTheme)
{
// 深色主题 - 参考 Windows 系统设置
if (MainBorder != null)
{
MainBorder.Background = ThemeHelper.GetBackgroundPrimaryBrush(); // Windows 系统主背景 #202020
MainBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(0, 120, 215)); // Windows 系统强调色(蓝色)
MainBorder.Background = new SolidColorBrush(Color.FromRgb(43, 43, 43));
MainBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(0, 120, 215));
}
if (SidebarBorder != null)
{
@@ -435,10 +448,9 @@ namespace Ink_Canvas.Windows
}
else
{
// 浅色主题(默认)
if (MainBorder != null)
{
MainBorder.Background = new SolidColorBrush(Color.FromRgb(250, 250, 250));
MainBorder.Background = new SolidColorBrush(Color.FromRgb(255, 255, 255));
MainBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(53, 132, 228));
}
if (SidebarBorder != null)
@@ -754,6 +766,7 @@ namespace Ink_Canvas.Windows
LuckyRandomPanel,
AdvancedPanel,
SnapshotPanel,
UpdateCenterPanel,
SettingsAboutPanel,
AppearancePanel
};
@@ -947,6 +960,7 @@ namespace Ink_Canvas.Windows
{ "LuckyRandomItem", LuckyRandomPanel },
{ "AdvancedItem", AdvancedPanel },
{ "SnapshotItem", SnapshotPanel },
{ "UpdateCenterItem", UpdateCenterPanel },
{ "AppearanceItem", AppearancePanel }
};
@@ -964,6 +978,7 @@ namespace Ink_Canvas.Windows
if (LuckyRandomPane != null) LuckyRandomPane.Visibility = _selectedSidebarItemName == "LuckyRandomItem" ? Visibility.Visible : Visibility.Collapsed;
if (AdvancedPane != null) AdvancedPane.Visibility = _selectedSidebarItemName == "AdvancedItem" ? Visibility.Visible : Visibility.Collapsed;
if (SnapshotPane != null) SnapshotPane.Visibility = _selectedSidebarItemName == "SnapshotItem" ? Visibility.Visible : Visibility.Collapsed;
if (UpdateCenterPane != null) UpdateCenterPane.Visibility = _selectedSidebarItemName == "UpdateCenterItem" ? Visibility.Visible : Visibility.Collapsed;
// 为新显示的面板应用主题(延迟执行,确保面板已完全显示)
if (panelMappings.ContainsKey(_selectedSidebarItemName))