add:更新面板

This commit is contained in:
2026-05-01 18:55:03 +08:00
parent c27759189d
commit 5a387eef96
10 changed files with 1011 additions and 1796 deletions
+105 -4
View File
@@ -27,6 +27,37 @@ namespace Ink_Canvas.Helpers
private static readonly string updatesFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoUpdate"); private static readonly string updatesFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoUpdate");
private static string statusFilePath; private static string statusFilePath;
// 全局下载取消令牌;UI 通过 RequestCancelDownload 取消当前下载
private static CancellationTokenSource _activeDownloadCts;
private static readonly object _activeDownloadLock = new object();
public static void RequestCancelDownload()
{
lock (_activeDownloadLock)
{
try { _activeDownloadCts?.Cancel(); } catch { }
}
}
private static CancellationTokenSource BeginDownloadSession()
{
lock (_activeDownloadLock)
{
try { _activeDownloadCts?.Cancel(); } catch { }
_activeDownloadCts = new CancellationTokenSource();
return _activeDownloadCts;
}
}
private static void EndDownloadSession(CancellationTokenSource cts)
{
lock (_activeDownloadLock)
{
if (ReferenceEquals(_activeDownloadCts, cts)) _activeDownloadCts = null;
}
try { cts?.Dispose(); } catch { }
}
public static bool IsX64UpdatePackageSelected() public static bool IsX64UpdatePackageSelected()
{ {
try try
@@ -383,6 +414,8 @@ namespace Ink_Canvas.Helpers
// 获取所有可用线路组,按延迟排序 // 获取所有可用线路组,按延迟排序
public static async Task<List<UpdateLineGroup>> GetAvailableLineGroupsOrdered(UpdateChannel channel) public static async Task<List<UpdateLineGroup>> GetAvailableLineGroupsOrdered(UpdateChannel channel)
{ {
var cached = TryGetCachedOrderedGroups(channel);
if (cached != null) return cached;
var groups = ChannelLineGroups[channel]; var groups = ChannelLineGroups[channel];
var availableGroups = new List<(UpdateLineGroup group, long delay)>(); var availableGroups = new List<(UpdateLineGroup group, long delay)>();
@@ -468,9 +501,46 @@ namespace Ink_Canvas.Helpers
LogHelper.WriteLogToFile("AutoUpdate | 所有线路组均不可用", LogHelper.LogType.Error); LogHelper.WriteLogToFile("AutoUpdate | 所有线路组均不可用", LogHelper.LogType.Error);
} }
CacheOrderedGroups(channel, orderedGroups);
return orderedGroups; return orderedGroups;
} }
// 缓存按延迟排序后的线路组,避免短时间内重复测速
private static readonly Dictionary<UpdateChannel, (List<UpdateLineGroup> groups, DateTime cachedAt)> _orderedGroupsCache
= new Dictionary<UpdateChannel, (List<UpdateLineGroup>, DateTime)>();
private static readonly TimeSpan _orderedGroupsCacheTtl = TimeSpan.FromMinutes(15);
private static List<UpdateLineGroup> TryGetCachedOrderedGroups(UpdateChannel channel)
{
lock (_orderedGroupsCache)
{
if (_orderedGroupsCache.TryGetValue(channel, out var entry) &&
entry.groups != null && entry.groups.Count > 0 &&
DateTime.UtcNow - entry.cachedAt < _orderedGroupsCacheTtl)
{
LogHelper.WriteLogToFile($"AutoUpdate | 复用线路组延迟检测缓存({entry.groups.Count} 个)");
return new List<UpdateLineGroup>(entry.groups);
}
return null;
}
}
private static void CacheOrderedGroups(UpdateChannel channel, List<UpdateLineGroup> groups)
{
lock (_orderedGroupsCache)
{
_orderedGroupsCache[channel] = (new List<UpdateLineGroup>(groups), DateTime.UtcNow);
}
}
public static void InvalidateOrderedGroupsCache()
{
lock (_orderedGroupsCache)
{
_orderedGroupsCache.Clear();
}
}
private static async Task<long> GetDownloadUrlDelay(string url) private static async Task<long> GetDownloadUrlDelay(string url)
{ {
try try
@@ -945,6 +1015,7 @@ namespace Ink_Canvas.Helpers
// 使用多线路组下载新版(支持自动切换) // 使用多线路组下载新版(支持自动切换)
public static async Task<bool> DownloadSetupFileWithFallback(string version, List<UpdateLineGroup> groups, Action<double, string> progressCallback = null) public static async Task<bool> DownloadSetupFileWithFallback(string version, List<UpdateLineGroup> groups, Action<double, string> progressCallback = null)
{ {
var session = BeginDownloadSession();
try try
{ {
version = NormalizeVersionForUpdate(version); version = NormalizeVersionForUpdate(version);
@@ -1021,6 +1092,13 @@ namespace Ink_Canvas.Helpers
progressCallback?.Invoke(0, "所有线路组下载均失败"); progressCallback?.Invoke(0, "所有线路组下载均失败");
return false; return false;
} }
catch (OperationCanceledException)
{
LogHelper.WriteLogToFile("AutoUpdate | 下载已被用户取消", LogHelper.LogType.Warning);
SaveDownloadStatus(false);
progressCallback?.Invoke(0, "下载已取消");
return false;
}
catch (Exception ex) catch (Exception ex)
{ {
LogHelper.WriteLogToFile($"AutoUpdate | 下载更新时出错: {ex.Message}", LogHelper.LogType.Error); LogHelper.WriteLogToFile($"AutoUpdate | 下载更新时出错: {ex.Message}", LogHelper.LogType.Error);
@@ -1033,6 +1111,10 @@ namespace Ink_Canvas.Helpers
progressCallback?.Invoke(0, $"下载异常: {ex.Message}"); progressCallback?.Invoke(0, $"下载异常: {ex.Message}");
return false; return false;
} }
finally
{
EndDownloadSession(session);
}
} }
// 下载文件的具体实现 // 下载文件的具体实现
@@ -1043,6 +1125,12 @@ namespace Ink_Canvas.Helpers
// 降低并发数,减少网络压力 // 降低并发数,减少网络压力
int[] threadOptions = { 32, 16, 8, 4, 1 }; int[] threadOptions = { 32, 16, 8, 4, 1 };
CancellationToken externalToken;
lock (_activeDownloadLock)
{
externalToken = _activeDownloadCts?.Token ?? CancellationToken.None;
}
// 检查服务器是否支持Range分块下载 // 检查服务器是否支持Range分块下载
bool supportRange = false; bool supportRange = false;
long totalSize = -1; long totalSize = -1;
@@ -1146,7 +1234,7 @@ namespace Ink_Canvas.Helpers
// 增加连接超时设置 // 增加连接超时设置
client.Timeout = TimeSpan.FromSeconds(30); client.Timeout = TimeSpan.FromSeconds(30);
var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token); var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, externalToken);
var lastReadTime = DateTime.UtcNow; var lastReadTime = DateTime.UtcNow;
bool dataReceived = false; bool dataReceived = false;
@@ -1339,12 +1427,18 @@ namespace Ink_Canvas.Helpers
LogHelper.WriteLogToFile($"AutoUpdate | 开始单线程下载: {fileUrl}"); LogHelper.WriteLogToFile($"AutoUpdate | 开始单线程下载: {fileUrl}");
progressCallback?.Invoke(0, "开始单线程下载"); progressCallback?.Invoke(0, "开始单线程下载");
CancellationToken token;
lock (_activeDownloadLock)
{
token = _activeDownloadCts?.Token ?? CancellationToken.None;
}
using (var client = new HttpClient()) using (var client = new HttpClient())
{ {
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
client.Timeout = TimeSpan.FromMinutes(10); // 单线程下载设置更长的超时时间 client.Timeout = TimeSpan.FromMinutes(10); // 单线程下载设置更长的超时时间
using (var resp = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead)) using (var resp = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, token))
{ {
resp.EnsureSuccessStatusCode(); resp.EnsureSuccessStatusCode();
using (var fs = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (var fs = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None))
@@ -1355,9 +1449,9 @@ namespace Ink_Canvas.Helpers
long downloaded = 0; long downloaded = 0;
var lastProgressUpdate = DateTime.UtcNow; var lastProgressUpdate = DateTime.UtcNow;
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0) while ((read = await stream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
{ {
await fs.WriteAsync(buffer, 0, read); await fs.WriteAsync(buffer, 0, read, token);
downloaded += read; downloaded += read;
// 限制进度更新频率,避免UI卡顿 // 限制进度更新频率,避免UI卡顿
@@ -1379,6 +1473,13 @@ namespace Ink_Canvas.Helpers
LogHelper.WriteLogToFile("AutoUpdate | 单线程下载完成"); LogHelper.WriteLogToFile("AutoUpdate | 单线程下载完成");
return true; return true;
} }
catch (OperationCanceledException)
{
LogHelper.WriteLogToFile("AutoUpdate | 单线程下载已被取消", LogHelper.LogType.Warning);
progressCallback?.Invoke(0, "下载已取消");
try { if (File.Exists(destinationPath)) File.Delete(destinationPath); } catch { }
return false;
}
catch (Exception ex) catch (Exception ex)
{ {
LogHelper.WriteLogToFile($"AutoUpdate | 单线程下载失败: {ex.Message}", LogHelper.LogType.Error); LogHelper.WriteLogToFile($"AutoUpdate | 单线程下载失败: {ex.Message}", LogHelper.LogType.Error);
+9 -5
View File
@@ -2268,11 +2268,15 @@
<!-- 通知弹窗 --> <!-- 通知弹窗 -->
<Grid Name="GridNotifications" Margin="0,110" Visibility="Collapsed" HorizontalAlignment="Center" <Grid Name="GridNotifications" Margin="0,110" Visibility="Collapsed" HorizontalAlignment="Center"
VerticalAlignment="Bottom"> VerticalAlignment="Bottom">
<Border CornerRadius="6" MaxHeight="200" Padding="20 10" Background="{DynamicResource SettingsPageBackground}" Opacity="0.95" <ui:InfoBar x:Name="NotificationInfoBar"
BorderBrush="#b91c1c" BorderThickness="1.5"> IsOpen="True"
<TextBlock Name="TextBlockNotice" Text="{i18n:I18n Key=Notification_TestText}" VerticalAlignment="Center" HorizontalAlignment="Center" IsClosable="False"
FontSize="16" Foreground="{DynamicResource SettingsPageForeground}" /> Severity="Warning"
</Border> Title="提醒"
MaxWidth="900"
Message="{Binding Text, ElementName=TextBlockNotice}" />
<TextBlock x:Name="TextBlockNotice" Visibility="Collapsed"
Text="{i18n:I18n Key=Notification_TestText}" />
</Grid> </Grid>
<!--// Old UI //--> <!--// Old UI //-->
<Viewbox Name="ViewBoxStackPanelMain" Visibility="Collapsed" Margin="10,10,10,55" HorizontalAlignment="Right" <Viewbox Name="ViewBoxStackPanelMain" Visibility="Collapsed" Margin="10,10,10,55" HorizontalAlignment="Right"
+10 -123
View File
@@ -45,7 +45,7 @@ namespace Ink_Canvas
private List<System.Windows.Controls.Canvas> whiteboardPages = new List<System.Windows.Controls.Canvas>(); private List<System.Windows.Controls.Canvas> whiteboardPages = new List<System.Windows.Controls.Canvas>();
private int currentPageIndex; private int currentPageIndex;
private System.Windows.Controls.Canvas currentCanvas; private System.Windows.Controls.Canvas currentCanvas;
private AutoUpdateHelper.UpdateLineGroup AvailableLatestLineGroup; internal AutoUpdateHelper.UpdateLineGroup AvailableLatestLineGroup;
// 全局快捷键管理器 // 全局快捷键管理器
private GlobalHotkeyManager _globalHotkeyManager; private GlobalHotkeyManager _globalHotkeyManager;
@@ -1203,7 +1203,6 @@ namespace Ink_Canvas
LoadCustomBackgroundColor(); LoadCustomBackgroundColor();
SetWindowMode(); SetWindowMode();
// HasNewUpdateWindow hasNewUpdateWindow = new HasNewUpdateWindow();
// 根据设置应用主题 // 根据设置应用主题
switch (Settings.Appearance.Theme) switch (Settings.Appearance.Theme)
{ {
@@ -1846,6 +1845,7 @@ namespace Ink_Canvas
var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(Settings.Startup.UpdateChannel); var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(Settings.Startup.UpdateChannel);
AvailableLatestVersion = remoteVersion; AvailableLatestVersion = remoteVersion;
AvailableLatestLineGroup = lineGroup; AvailableLatestLineGroup = lineGroup;
AvailableLatestReleaseNotes = apiReleaseNotes;
// 声明下载状态变量,用于整个方法 // 声明下载状态变量,用于整个方法
bool isDownloadSuccessful = false; bool isDownloadSuccessful = false;
@@ -1882,9 +1882,6 @@ namespace Ink_Canvas
SaveSettingsToFile(); SaveSettingsToFile();
} }
// 获取当前版本
string currentVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
// 如果启用了静默更新,则自动下载更新而不显示提示 // 如果启用了静默更新,则自动下载更新而不显示提示
if (Settings.Startup.IsAutoUpdateWithSilence) if (Settings.Startup.IsAutoUpdateWithSilence)
{ {
@@ -1908,121 +1905,10 @@ namespace Ink_Canvas
return; return;
} }
// 如果没有启用静默更新,则显示常规更新窗口 // 如果没有启用静默更新,则记录日志并依赖 Toast 通知用户。
string releaseDate = DateTime.Now.ToString("yyyy年MM月dd日"); // 用户可在 设置 → 更新 中查看版本说明并选择更新方式。
LogHelper.WriteLogToFile(
// 从服务器获取更新日志 $"AutoUpdate | New version {AvailableLatestVersion} available; user notified via toast, will act from settings page.");
string releaseNotes = await AutoUpdateHelper.GetUpdateLog(Settings.Startup.UpdateChannel);
// 如果获取失败,使用默认文本
if (string.IsNullOrEmpty(releaseNotes))
{
releaseNotes = $@"# InkCanvasForClass v{AvailableLatestVersion}更新
无法获取更新日志,但新版本已准备就绪。";
}
// 创建并显示更新窗口
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, AvailableLatestVersion, releaseDate, releaseNotes);
updateWindow.Owner = this;
bool? dialogResult = updateWindow.ShowDialog();
// 如果窗口被关闭但没有点击按钮,则不执行任何操作
if (dialogResult != true)
{
LogHelper.WriteLogToFile("AutoUpdate | Update dialog closed without selection");
return;
}
// 不再从更新窗口获取自动更新设置
// 根据用户选择处理更新
switch (updateWindow.Result)
{
case HasNewUpdateWindow.UpdateResult.UpdateNow:
// 立即更新:显示下载进度,下载完成后立即安装
LogHelper.WriteLogToFile("AutoUpdate | User chose to update now");
// 显示下载进度提示
MessageBox.Show("开始下载更新,请稍候...", "正在更新", MessageBoxButton.OK, MessageBoxImage.Information);
// 下载更新文件,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel);
if (isDownloadSuccessful)
{
// 下载成功,提示用户准备安装
MessageBoxResult result = MessageBox.Show("更新已下载完成,点击确定后将关闭软件并安装新版本!", "安装更新", MessageBoxButton.OKCancel, MessageBoxImage.Information);
// 只有当用户点击确定按钮后才关闭软件
if (result == MessageBoxResult.OK)
{
// 设置为用户主动退出,避免被看门狗判定为崩溃
App.IsAppExitByUser = true;
// 准备批处理脚本
AutoUpdateHelper.InstallNewVersionApp(AvailableLatestVersion, true); // 修改为静默模式,避免重复启动进程
// 关闭软件,让安装程序接管
Application.Current.Shutdown();
}
else
{
LogHelper.WriteLogToFile("AutoUpdate | User cancelled update installation");
}
}
else
{
// 下载失败
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
break;
case HasNewUpdateWindow.UpdateResult.UpdateLater:
// 稍后更新:静默下载,在软件关闭时自动安装
LogHelper.WriteLogToFile("AutoUpdate | User chose to update later");
// 不管设置如何,都进行下载,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback(AvailableLatestVersion, AvailableLatestLineGroup, Settings.Startup.UpdateChannel);
if (isDownloadSuccessful)
{
LogHelper.WriteLogToFile("AutoUpdate | Update downloaded successfully, will install when application closes");
// 设置标志,在应用程序关闭时安装
Settings.Startup.IsAutoUpdate = true;
Settings.Startup.IsAutoUpdateWithSilence = true;
// 启动检查定时器
timerCheckAutoUpdateWithSilence.Start();
// 通知用户
MessageBox.Show("更新已下载完成,将在软件关闭时自动安装。", "更新已准备就绪", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
LogHelper.WriteLogToFile("AutoUpdate | Update download failed", LogHelper.LogType.Error);
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
break;
case HasNewUpdateWindow.UpdateResult.SkipVersion:
// 跳过该版本:记录到设置中
LogHelper.WriteLogToFile($"AutoUpdate | User chose to skip version {AvailableLatestVersion}");
// 记录要跳过的版本号
Settings.Startup.SkippedVersion = AvailableLatestVersion;
// 保存设置到文件
SaveSettingsToFile();
// 通知用户
MessageBox.Show($"已设置跳过版本 {AvailableLatestVersion},在下次发布新版本之前不会再提示更新。",
"已跳过此版本",
MessageBoxButton.OK,
MessageBoxImage.Information);
break;
}
} }
else if (hasValidLineGroup) else if (hasValidLineGroup)
{ {
@@ -2271,9 +2157,10 @@ namespace Ink_Canvas
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e) private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e)
{ {
var win = new HistoryRollbackWindow(Settings.Startup.UpdateChannel); var settingsWindow = new Windows.SettingsViews.SettingsWindow();
win.Owner = this; settingsWindow.Owner = this;
win.ShowDialog(); settingsWindow.Show();
settingsWindow.NavigateToPage("UpdatePage");
} }
[DllImport("user32.dll")] [DllImport("user32.dll")]
+5 -1
View File
@@ -95,7 +95,11 @@ namespace Ink_Canvas
/// <summary> /// <summary>
/// 可用的最新版本号 /// 可用的最新版本号
/// </summary> /// </summary>
private string AvailableLatestVersion; internal string AvailableLatestVersion;
/// <summary>
/// 最近一次自动检查得到的更新说明(Markdown)
/// </summary>
internal string AvailableLatestReleaseNotes;
/// <summary> /// <summary>
/// 静默更新检查定时器 /// 静默更新检查定时器
/// </summary> /// </summary>
-346
View File
@@ -1,346 +0,0 @@
<Window x:Class="Ink_Canvas.HasNewUpdateWindow"
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:local="clr-namespace:Ink_Canvas"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
mc:Ignorable="d"
ui:WindowHelper.UseModernWindowStyle="False"
ui:WindowHelper.SystemBackdropType="Mica"
Title="InkCanvasForClass CE有新版本可用" Height="650" Width="900" ResizeMode="NoResize"
WindowStartupLocation="CenterScreen" WindowStyle="None"
Background="Transparent">
<Window.Template>
<ControlTemplate TargetType="Window">
<Border Background="{DynamicResource SettingsPageBackground}"
CornerRadius="8"
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
BorderThickness="1">
<Border.Effect>
<DropShadowEffect Color="#000000" BlurRadius="20" ShadowDepth="0" Opacity="0.1"/>
</Border.Effect>
<ContentPresenter/>
</Border>
</ControlTemplate>
</Window.Template>
<Window.Resources>
<!-- 主题相关颜色资源 -->
<SolidColorBrush x:Key="UpdateWindowPrimaryBrush" Color="#2563eb"/>
<SolidColorBrush x:Key="UpdateWindowPrimaryHoverBrush" Color="#1d4ed8"/>
<SolidColorBrush x:Key="UpdateWindowPrimaryPressedBrush" Color="#1e40af"/>
<SolidColorBrush x:Key="UpdateWindowCardBackgroundBrush" Color="#f8fafc"/>
<SolidColorBrush x:Key="UpdateWindowCardBorderBrush" Color="#e5e7eb"/>
<SolidColorBrush x:Key="UpdateWindowTextPrimaryBrush" Color="#1f2937"/>
<SolidColorBrush x:Key="UpdateWindowTextSecondaryBrush" Color="#6b7280"/>
<SolidColorBrush x:Key="UpdateWindowCloseButtonBrush" Color="#666666"/>
<!-- 渐变背景定义 -->
<LinearGradientBrush x:Key="HeaderGradient" StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#2563eb" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
<!-- 现代按钮样式 -->
<Style x:Key="ModernPrimaryButton" TargetType="Button">
<Setter Property="Background" Value="{DynamicResource UpdateWindowPrimaryBrush}"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="20,10"/>
<Setter Property="Height" Value="40"/>
<Setter Property="MinWidth" Value="200"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="6"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource UpdateWindowPrimaryHoverBrush}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource UpdateWindowPrimaryPressedBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ModernSecondaryButton" TargetType="Button">
<Setter Property="Background" Value="{DynamicResource SettingsPageBackground}"/>
<Setter Property="Foreground" Value="{DynamicResource UpdateWindowTextPrimaryBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{DynamicResource SettingsPageBorderBrush}"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="20,10"/>
<Setter Property="Height" Value="40"/>
<Setter Property="MinWidth" Value="200"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="6"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF2563eb" Opacity="0.1"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF2563eb" Opacity="0.2"/>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ModernTertiaryButton" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="20,10"/>
<Setter Property="Height" Value="40"/>
<Setter Property="MinWidth" Value="200"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="6"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF2563eb" Opacity="0.1"/>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="{DynamicResource UpdateWindowTextPrimaryBrush}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF2563eb" Opacity="0.2"/>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<Grid Margin="0,0,0,0" Background="Transparent">
<ikw:SimpleStackPanel VerticalAlignment="Stretch">
<!-- 统一的主容器 -->
<Border Background="{DynamicResource SettingsPageBackground}" CornerRadius="8" Margin="0,0,0,0"
BorderThickness="1" BorderBrush="{DynamicResource SettingsPageBorderBrush}" Padding="0">
<ikw:SimpleStackPanel>
<!-- 现代化标题栏 -->
<Border Background="{StaticResource HeaderGradient}" CornerRadius="8,8,0,0" Margin="0,0,0,0"
MouseLeftButtonDown="TitleBar_MouseLeftButtonDown">
<Grid>
<!-- 标题栏内容 -->
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="32,24,80,24" Spacing="16">
<Border Background="White" CornerRadius="16" Padding="16" Margin="0,0,0,0">
<Image Source="/Resources/Icons-fluent/party.png" Width="24" Height="24"/>
</Border>
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="4">
<TextBlock Text="新版本发布" FontSize="24" FontWeight="Bold" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" TextAlignment="Left"/>
<TextBlock Text="InkCanvasForClass CE 为您带来全新体验" FontSize="14" TextAlignment="Left" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
<!-- 自定义关闭按钮 -->
<Button x:Name="CloseButton"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Margin="0,12,12,0"
Width="32" Height="32"
Background="Transparent"
BorderThickness="0"
Click="CloseButton_Click"
Cursor="Hand"
ToolTip="关闭">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="16"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<Path Data="M8,8 L16,16 M16,8 L8,16"
Stroke="{DynamicResource UpdateWindowCloseButtonBrush}"
StrokeThickness="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF2563eb" Opacity="0.1"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF2563eb" Opacity="0.2"/>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</Border>
<!-- 内容标题 -->
<Border Background="{DynamicResource UpdateWindowCardBackgroundBrush}" Padding="24,20,24,16">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12">
<Border Background="{DynamicResource UpdateWindowPrimaryBrush}" CornerRadius="8" Padding="8">
<TextBlock Text="📝" FontSize="16" Foreground="White"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="2">
<TextBlock Text="更新内容" FontSize="18" FontWeight="SemiBold" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}"/>
<TextBlock Text="查看本次更新的详细内容" FontSize="14" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</Border>
<!-- 内容区域 -->
<ui:ScrollViewerEx Margin="0" VerticalScrollBarVisibility="Auto" Height="200" PanningMode="VerticalOnly">
<mdxam:MarkdownScrollViewer x:Name="markdownContent" xml:space="preserve"
Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" MarkdownStyleName="GithubLike"
Margin="24,16,24,16">
# InkCanvasForClass v5.0.2更新
你好,旅行者们,本次InkCanvasForClass Community Edition更新带来了如下新功能供您探索:
1. 全新设计的UI界面,包括浮动工具栏和白板页面均经过重新设计,更加现代化的UI让您在使用的过程中更加舒适。
2. 带来了实时修改橡皮大小和橡皮形状的菜单。您可以选择使用圆形橡皮,方形橡皮,和类似希沃白板的真实黑板擦(矩形)橡皮。
3. 白板页面支持显示当前时间和日期
4. 自动收纳新增对希沃轻白板、智绘教、鸿合屏幕书写等软件的支持,自动查杀新增对鸿合屏幕书写、希沃轻白板等软件的支持。
5. 为设置界面重写了全新的UI。
6. 重写了随机抽选模块,现在支持更丰富的抽选机制和自定义选项。
7. 修复了部分小Bug,提升了整体的用户体验。
8. 带来了基于FitToCurve的笔迹平滑,基于贝塞尔曲线平滑,让墨迹线条更加优美好看。
</mdxam:MarkdownScrollViewer>
</ui:ScrollViewerEx>
</ikw:SimpleStackPanel>
</Border>
<!-- 现代化版本信息 -->
<Border Background="{DynamicResource UpdateWindowCardBackgroundBrush}" Margin="0,0,0,0" Padding="24,20">
<ikw:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="24">
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="4" HorizontalAlignment="Center">
<TextBlock Text="版本信息" FontSize="14" FontWeight="Medium" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
<TextBlock x:Name="updateVersionInfo" Text="本次更新: 4.9.1 -> 5.9.1"
FontWeight="SemiBold" FontSize="16" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" TextAlignment="Center"/>
</ikw:SimpleStackPanel>
<Border Width="1" Background="{DynamicResource SettingsPageBorderBrush}" Margin="0,8,0,8"/>
<ikw:SimpleStackPanel Orientation="Vertical" Spacing="4" HorizontalAlignment="Center">
<TextBlock Text="发布日期" FontSize="14" FontWeight="Medium" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}"/>
<TextBlock x:Name="updateDateInfo" Text="2024年8月4日发布更新"
FontSize="16" Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" TextAlignment="Center"/>
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</Border>
<!-- 现代化按钮区域 -->
<Border Background="{DynamicResource UpdateWindowCardBackgroundBrush}" Margin="0,0,0,0" Padding="32,24" CornerRadius="0,0,8,8">
<ikw:SimpleStackPanel Orientation="Vertical" HorizontalAlignment="Center" Spacing="16">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12" HorizontalAlignment="Center">
<Border Background="{DynamicResource UpdateWindowPrimaryBrush}" CornerRadius="8" Padding="8">
<TextBlock Text="⚡" FontSize="16" Foreground="White"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<TextBlock Text="选择更新方式" FontWeight="SemiBold" FontSize="18"
Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" VerticalAlignment="Center"/>
</ikw:SimpleStackPanel>
<!-- 立即更新按钮 -->
<Button x:Name="UpdateNowButton" Content="立即下载并安装"
Style="{StaticResource ModernPrimaryButton}"
Click="UpdateNowButton_Click" ToolTip="立即下载更新并在完成后安装"
Visibility="Visible" IsEnabled="True"/>
<!-- 稍后更新按钮 -->
<Button x:Name="UpdateLaterButton" Content="下载并在软件关闭时安装"
Style="{StaticResource ModernSecondaryButton}"
Click="UpdateLaterButton_Click"
ToolTip="后台下载更新,在软件关闭时自动安装"
Visibility="Visible" IsEnabled="True"/>
<!-- 跳过版本按钮 -->
<Button x:Name="SkipVersionButton" Content="跳过该版本"
Style="{StaticResource ModernTertiaryButton}"
Click="SkipVersionButton_Click"
ToolTip="跳过此版本更新" Visibility="Visible" IsEnabled="True"/>
</ikw:SimpleStackPanel>
</Border>
<!-- 现代化下载进度指示器 -->
<Border x:Name="DownloadProgressPanel" Background="{DynamicResource UpdateWindowCardBackgroundBrush}"
Margin="0,0,0,0" Padding="32,24" Visibility="Collapsed">
<ikw:SimpleStackPanel Orientation="Vertical" HorizontalAlignment="Center" Spacing="16">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12" HorizontalAlignment="Center">
<Border Background="#10b981" CornerRadius="8" Padding="8">
<TextBlock Text="📥" FontSize="16" Foreground="White"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<TextBlock Text="正在下载更新" FontWeight="SemiBold" FontSize="18"
Foreground="{DynamicResource UpdateWindowTextPrimaryBrush}" VerticalAlignment="Center"/>
</ikw:SimpleStackPanel>
<!-- 现代化进度条 -->
<Border Background="{DynamicResource SettingsPageBackground}" CornerRadius="6" Height="6" Width="400">
<Border x:Name="ProgressFill" Background="{DynamicResource UpdateWindowPrimaryBrush}" CornerRadius="6"
Width="0" HorizontalAlignment="Left">
</Border>
</Border>
<TextBlock x:Name="DownloadProgressText" Text="正在准备下载..."
FontSize="14" Foreground="{DynamicResource UpdateWindowTextSecondaryBrush}" HorizontalAlignment="Center"/>
</ikw:SimpleStackPanel>
</Border>
</ikw:SimpleStackPanel>
</Grid>
</ScrollViewer>
</Window>
@@ -1,513 +0,0 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
using iNKORE.UI.WPF.Modern.Controls;
using MdXaml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
namespace Ink_Canvas
{
/// <summary>
/// HasNewUpdateWindow.xaml 的交互逻辑
/// </summary>
///
public partial class HasNewUpdateWindow : Window
{
[DllImport("dwmapi.dll")]
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
private static bool UseImmersiveDarkMode(IntPtr handle, bool enabled)
{
if (IsWindows10OrGreater(17763))
{
var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
if (IsWindows10OrGreater(18985))
{
attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
}
int useImmersiveDarkMode = enabled ? 1 : 0;
return DwmSetWindowAttribute(handle, attribute, ref useImmersiveDarkMode, sizeof(int)) == 0;
}
return false;
}
private static bool IsWindows10OrGreater(int build = -1)
{
return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build;
}
/// <summary>
/// 应用当前主题设置
/// </summary>
private void ApplyCurrentTheme()
{
try
{
// 根据主窗口的主题设置应用主题
switch (MainWindow.Settings.Appearance.Theme)
{
case 0: // 浅色主题
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
UpdateColorsForLightTheme();
break;
case 1: // 深色主题
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
UpdateColorsForDarkTheme();
break;
case 2: // 跟随系统
if (IsSystemThemeLight())
{
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
UpdateColorsForLightTheme();
}
else
{
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
UpdateColorsForDarkTheme();
}
break;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用主题时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新为浅色主题颜色
/// </summary>
private void UpdateColorsForLightTheme()
{
try
{
// 更新主要颜色资源
Resources["UpdateWindowPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb));
Resources["UpdateWindowPrimaryHoverBrush"] = new SolidColorBrush(Color.FromRgb(0x1d, 0x4e, 0xd8));
Resources["UpdateWindowPrimaryPressedBrush"] = new SolidColorBrush(Color.FromRgb(0x1e, 0x40, 0xaf));
Resources["UpdateWindowCardBackgroundBrush"] = new SolidColorBrush(Color.FromRgb(0xf8, 0xfa, 0xfc));
Resources["UpdateWindowCardBorderBrush"] = new SolidColorBrush(Color.FromRgb(0xe5, 0xe7, 0xeb));
Resources["UpdateWindowTextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x1f, 0x29, 0x37));
Resources["UpdateWindowTextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x6b, 0x72, 0x80));
Resources["UpdateWindowCloseButtonBrush"] = new SolidColorBrush(Color.FromRgb(0x66, 0x66, 0x66));
// 更新渐变背景
var gradient = new LinearGradientBrush();
gradient.StartPoint = new Point(0, 0);
gradient.EndPoint = new Point(1, 1);
gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0x25, 0x63, 0xeb), 0));
gradient.GradientStops.Add(new GradientStop(Colors.White, 1));
Resources["HeaderGradient"] = gradient;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新浅色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新为深色主题颜色
/// </summary>
private void UpdateColorsForDarkTheme()
{
try
{
// 更新主要颜色资源
Resources["UpdateWindowPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x3b, 0x82, 0xf6));
Resources["UpdateWindowPrimaryHoverBrush"] = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb));
Resources["UpdateWindowPrimaryPressedBrush"] = new SolidColorBrush(Color.FromRgb(0x1d, 0x4e, 0xd8));
Resources["UpdateWindowCardBackgroundBrush"] = new SolidColorBrush(Color.FromRgb(0x2a, 0x2a, 0x2a));
Resources["UpdateWindowCardBorderBrush"] = new SolidColorBrush(Color.FromRgb(0x40, 0x40, 0x40));
Resources["UpdateWindowTextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0xf9, 0xfa, 0xfb));
Resources["UpdateWindowTextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x9c, 0xa3, 0xaf));
Resources["UpdateWindowCloseButtonBrush"] = new SolidColorBrush(Color.FromRgb(0x9c, 0xa3, 0xaf));
// 更新渐变背景
var gradient = new LinearGradientBrush();
gradient.StartPoint = new Point(0, 0);
gradient.EndPoint = new Point(1, 1);
gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0x3b, 0x82, 0xf6), 0));
gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0x1f, 0x1f, 0x1f), 1));
Resources["HeaderGradient"] = gradient;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新深色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 检查系统是否为浅色主题
/// </summary>
private bool IsSystemThemeLight()
{
try
{
using (var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"))
{
if (key?.GetValue("AppsUseLightTheme") is int value)
{
return value == 1;
}
}
}
catch
{
// 如果无法读取注册表,默认返回true(浅色主题)
}
return true;
}
// 存储更新版本信息
public string CurrentVersion { get; set; }
public string NewVersion { get; set; }
public string ReleaseDate { get; set; }
public string ReleaseNotes { get; set; }
// 更新按钮结果
public enum UpdateResult
{
UpdateNow,
UpdateLater,
SkipVersion
}
public UpdateResult Result { get; private set; } = UpdateResult.UpdateLater;
public HasNewUpdateWindow(string currentVersion, string newVersion, string releaseDate, string releaseNotes = null)
{
InitializeComponent();
// 应用当前主题
ApplyCurrentTheme();
// 设置版本信息
CurrentVersion = currentVersion;
NewVersion = newVersion;
ReleaseDate = releaseDate;
ReleaseNotes = releaseNotes;
// 更新UI
updateVersionInfo.Text = $"本次更新: {CurrentVersion} -> {NewVersion}";
updateDateInfo.Text = $"{ReleaseDate}发布更新";
// 如果有发布说明,设置到Markdown内容中
if (!string.IsNullOrEmpty(ReleaseNotes))
{
markdownContent.Markdown = ReleaseNotes;
}
// 自动更新和静默更新设置已移至设置界面,此处不再需要
// 确保按钮可见且可用
EnsureButtonsVisibility();
// 显示窗口动画
AnimationsHelper.ShowWithFadeIn(this, 0.25);
// 设置深色模式
UseImmersiveDarkMode(new WindowInteropHelper(this).Handle, true);
// 窗口加载完成后再次确保按钮可见
Loaded += HasNewUpdateWindow_Loaded;
}
private void HasNewUpdateWindow_Loaded(object sender, RoutedEventArgs e)
{
// 窗口加载完成后再次确保按钮可见
EnsureButtonsVisibility();
// 调整窗口大小以适应屏幕分辨率
AdjustWindowSizeForScreenResolution();
}
// 确保按钮可见并启用
private void EnsureButtonsVisibility()
{
// 确保立即更新按钮可见
UpdateNowButton.Visibility = Visibility.Visible;
UpdateNowButton.IsEnabled = true;
// 确保稍后更新按钮可见
UpdateLaterButton.Visibility = Visibility.Visible;
UpdateLaterButton.IsEnabled = true;
// 确保跳过版本按钮可见
SkipVersionButton.Visibility = Visibility.Visible;
SkipVersionButton.IsEnabled = true;
// 强制刷新UI
UpdateLayout();
// 记录日志
LogHelper.WriteLogToFile("AutoUpdate | Update dialog buttons visibility ensured");
}
private async void UpdateNowButton_Click(object sender, RoutedEventArgs e)
{
LogHelper.WriteLogToFile("AutoUpdate | Update Now button clicked");
// 禁用按钮,显示进度条
UpdateNowButton.IsEnabled = false;
UpdateLaterButton.IsEnabled = false;
SkipVersionButton.IsEnabled = false;
DownloadProgressPanel.Visibility = Visibility.Visible;
// 重置进度条
var progressFill = FindName("ProgressFill") as Border;
if (progressFill != null)
{
progressFill.Width = 0;
}
DownloadProgressText.Text = "正在准备下载...";
// 启动多线路下载
bool downloadSuccess = false;
try
{
// 获取当前通道的所有线路组
var groups = AutoUpdateHelper.ChannelLineGroups[MainWindow.Settings.Startup.UpdateChannel];
downloadSuccess = await AutoUpdateHelper.DownloadSetupFileWithFallback(NewVersion, groups, (percent, text) =>
{
Dispatcher.Invoke(() =>
{
// 更新自定义进度条
progressFill = FindName("ProgressFill") as Border;
if (progressFill != null)
{
progressFill.Width = (percent / 100.0) * 400; // 400是进度条总宽度
}
DownloadProgressText.Text = text;
});
});
if (downloadSuccess)
{
// 下载完成后自动安装
await DownloadAndInstallVersion(NewVersion, null, CancellationToken.None);
}
}
catch (Exception ex)
{
DownloadProgressText.Text = $"下载失败: {ex.Message}";
LogHelper.WriteLogToFile($"AutoUpdate | 下载异常: {ex.Message}", LogHelper.LogType.Error);
}
if (downloadSuccess)
{
// 设置进度条为100%
progressFill = FindName("ProgressFill") as Border;
if (progressFill != null)
{
progressFill.Width = 400; // 100%完成
}
DownloadProgressText.Text = "下载完成,准备安装...";
await Task.Delay(800);
// 设置结果为立即更新
Result = UpdateResult.UpdateNow;
DialogResult = true;
Close();
}
else
{
DownloadProgressText.Text = "下载失败,请检查网络后重试。";
UpdateNowButton.IsEnabled = true;
UpdateLaterButton.IsEnabled = true;
SkipVersionButton.IsEnabled = true;
}
}
private void UpdateLaterButton_Click(object sender, RoutedEventArgs e)
{
LogHelper.WriteLogToFile("AutoUpdate | Update Later button clicked");
// 设置结果为稍后更新
Result = UpdateResult.UpdateLater;
// 关闭窗口
DialogResult = true;
Close();
}
private void SkipVersionButton_Click(object sender, RoutedEventArgs e)
{
LogHelper.WriteLogToFile("AutoUpdate | Skip Version button clicked");
// 设置结果为跳过该版本
Result = UpdateResult.SkipVersion;
// 关闭窗口
DialogResult = true;
Close();
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
LogHelper.WriteLogToFile("AutoUpdate | Close button clicked");
// 设置结果为稍后更新(默认行为)
Result = UpdateResult.UpdateLater;
// 关闭窗口
DialogResult = false;
Close();
}
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// 开始拖动窗口
if (e.LeftButton == MouseButtonState.Pressed)
{
DragMove();
}
}
// 根据屏幕分辨率调整窗口大小
private void AdjustWindowSizeForScreenResolution()
{
try
{
// 获取主屏幕分辨率
double screenWidth = SystemParameters.PrimaryScreenWidth;
double screenHeight = SystemParameters.PrimaryScreenHeight;
LogHelper.WriteLogToFile($"AutoUpdate | Screen resolution: {screenWidth}x{screenHeight}");
// 始终确保窗口不超过屏幕大小的85%
double maxHeight = screenHeight * 0.85;
double maxWidth = screenWidth * 0.85;
bool needsAdjustment = false;
// 如果窗口高度超过最大允许高度,调整窗口高度
if (Height > maxHeight)
{
Height = maxHeight;
needsAdjustment = true;
LogHelper.WriteLogToFile($"AutoUpdate | Adjusted window height to: {Height}");
}
// 如果窗口宽度超过最大允许宽度,调整窗口宽度
if (Width > maxWidth)
{
Width = maxWidth;
needsAdjustment = true;
LogHelper.WriteLogToFile($"AutoUpdate | Adjusted window width to: {Width}");
}
// 如果屏幕分辨率较低,调整更多UI元素
if (screenHeight < 768 || screenWidth < 1024 || needsAdjustment)
{
// 查找相关控件并调整大小
var markdownViewer = FindName("markdownContent") as MarkdownScrollViewer;
var updateNowButton = FindName("UpdateNowButton") as Button;
var updateLaterButton = FindName("UpdateLaterButton") as Button;
var skipVersionButton = FindName("SkipVersionButton") as Button;
// 查找包含ScrollViewer的边框控件,减小其高度
var contentBorders = FindVisualChildren<Border>().ToList();
foreach (var border in contentBorders)
{
if (border.Child is ScrollViewer || border.Child is ScrollViewerEx)
{
// 减小内容显示区域的高度
if (border.Height > 180)
{
border.Height = 160;
LogHelper.WriteLogToFile("AutoUpdate | Reduced content area height");
}
else if (border.Child is ScrollViewerEx scrollViewer && scrollViewer.Height > 160)
{
scrollViewer.Height = 160;
LogHelper.WriteLogToFile("AutoUpdate | Reduced scroll viewer height");
}
}
}
// 调整按钮大小
if (updateNowButton != null && updateLaterButton != null && skipVersionButton != null)
{
updateNowButton.Height = 42;
updateLaterButton.Height = 42;
skipVersionButton.Height = 42;
updateNowButton.Padding = new Thickness(15, 8, 15, 8);
updateLaterButton.Padding = new Thickness(15, 8, 15, 8);
skipVersionButton.Padding = new Thickness(15, 8, 15, 8);
LogHelper.WriteLogToFile("AutoUpdate | Reduced button sizes for small screen");
}
}
// 确保窗口在屏幕范围内
if (Left < 0) Left = 0;
if (Top < 0) Top = 0;
if (Left + Width > screenWidth) Left = screenWidth - Width;
if (Top + Height > screenHeight) Top = screenHeight - Height;
LogHelper.WriteLogToFile($"AutoUpdate | Final window size: {Width}x{Height}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | Error adjusting window size: {ex.Message}", LogHelper.LogType.Error);
}
}
// 递归查找指定类型的所有子控件
private IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj = null) where T : DependencyObject
{
if (depObj == null)
depObj = this;
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
// 多线程分块下载并自动安装
private async Task<bool> DownloadAndInstallVersion(string version, string downloadUrl, CancellationToken token)
{
if (string.IsNullOrEmpty(downloadUrl))
{
// 自动更新场景下,downloadUrl为null,直接用主下载目录
downloadUrl = AutoUpdateHelper.GetLocalUpdateZipFilePath(version);
}
LogHelper.WriteLogToFile($"AutoUpdate | 开始安装版本: {version}");
AutoUpdateHelper.InstallNewVersionApp(version, true);
App.IsAppExitByUser = true;
Application.Current.Dispatcher.Invoke(() =>
{
Application.Current.Shutdown();
});
return true;
}
}
}
@@ -1,182 +0,0 @@
<Window x:Class="Ink_Canvas.HistoryRollbackWindow"
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:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
mc:Ignorable="d"
Title="历史版本回滚" Height="650" Width="900" ResizeMode="CanResize"
WindowStartupLocation="CenterScreen"
Topmost="True"
Background="{DynamicResource SettingsPageBackground}" MinHeight="550" MinWidth="800"
SnapsToDevicePixels="True"
TextOptions.TextRenderingMode="ClearType"
TextOptions.TextFormattingMode="Display"
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="PrimaryBrush" Color="#FF2563eb"/>
<SolidColorBrush x:Key="TextPrimaryBrush" Color="#FF1f2937"/>
<SolidColorBrush x:Key="TextSecondaryBrush" Color="#FF6b7280"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</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>
<!-- 主内容区 -->
<Grid Grid.Row="1" Background="{DynamicResource SettingsPageBackground}" Margin="20,20,20,20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 标题区域 -->
<ikw:SimpleStackPanel Grid.Row="0" Orientation="Horizontal" Spacing="12" Margin="0,0,0,16">
<Border Background="{DynamicResource PrimaryBrush}" CornerRadius="16" Padding="12" Margin="0,0,0,0">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Undo}" FontSize="20" Foreground="White"
VerticalAlignment="Center"/>
</Border>
<ikw:SimpleStackPanel VerticalAlignment="Center" Spacing="4">
<TextBlock Text="选择要回滚到的历史版本" FontSize="22" FontWeight="Bold"
Foreground="{DynamicResource TextPrimaryBrush}"/>
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
<!-- 版本选择卡片 -->
<Border Grid.Row="1"
Background="{DynamicResource SettingsPageBackground}"
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
BorderThickness="1"
CornerRadius="12"
Margin="0,0,0,16"
Padding="20">
<Border.Effect>
<DropShadowEffect Color="#000000" BlurRadius="8" ShadowDepth="0" Opacity="0.05"/>
</Border.Effect>
<ikw:SimpleStackPanel Spacing="16">
<TextBlock Text="选择版本" FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource TextPrimaryBrush}"/>
<ComboBox x:Name="VersionComboBox"
Width="300" Height="40"
DisplayMemberPath="Version"
SelectionChanged="VersionComboBox_SelectionChanged"/>
</ikw:SimpleStackPanel>
</Border>
<!-- 发布说明卡片 -->
<Border Grid.Row="2"
Background="{DynamicResource SettingsPageBackground}"
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
BorderThickness="1"
CornerRadius="12"
Margin="0,0,0,16"
Padding="20">
<Border.Effect>
<DropShadowEffect Color="#000000" BlurRadius="8" ShadowDepth="0" Opacity="0.05"/>
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="版本更新说明" FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource TextPrimaryBrush}"
Margin="0,0,0,16"/>
<Border Grid.Row="1"
Background="{DynamicResource SettingsPageBackground}"
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
BorderThickness="1"
CornerRadius="8">
<Border.Effect>
<DropShadowEffect Color="#000000" BlurRadius="4" ShadowDepth="0" Opacity="0.03"/>
</Border.Effect>
<ScrollViewer x:Name="InnerReleaseNotesScrollViewer"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
Padding="16"
PanningMode="VerticalOnly"
PanningRatio="1.0"
IsManipulationEnabled="True">
<mdxam:MarkdownScrollViewer x:Name="ReleaseNotesViewer"
Foreground="{DynamicResource TextPrimaryBrush}"
MarkdownStyleName="GithubLike"
IsHitTestVisible="False"
IsManipulationEnabled="False"/>
</ScrollViewer>
</Border>
</Grid>
</Border>
<!-- 操作按钮区域 -->
<ikw:SimpleStackPanel Grid.Row="3" Spacing="16" Margin="0,0,0,0">
<Button x:Name="RollbackButton"
Content="回滚到此版本"
HorizontalAlignment="Center"
Click="RollbackButton_Click"
Style="{DynamicResource AccentButtonStyle}"
Width="200" Height="48"
FontSize="16" FontWeight="SemiBold"/>
<!-- 下载进度面板 -->
<Border x:Name="DownloadProgressPanel"
Background="{DynamicResource SettingsPageBackground}"
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
BorderThickness="1"
CornerRadius="12"
Visibility="Collapsed"
Margin="0,8,0,0"
Padding="24">
<Border.Effect>
<DropShadowEffect Color="#000000" BlurRadius="8" ShadowDepth="0" Opacity="0.05"/>
</Border.Effect>
<ikw:SimpleStackPanel Spacing="12">
<ProgressBar x:Name="DownloadProgressBar"
Width="300" Height="8"
Minimum="0" Maximum="100" Value="0"/>
<TextBlock x:Name="DownloadProgressText"
Text="正在下载..."
FontSize="14"
Foreground="{DynamicResource TextPrimaryBrush}"
HorizontalAlignment="Center"/>
</ikw:SimpleStackPanel>
</Border>
</ikw:SimpleStackPanel>
</Grid>
</Grid>
</Window>
@@ -1,370 +0,0 @@
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
// Added for OrderByDescending
namespace Ink_Canvas
{
public partial class HistoryRollbackWindow : Window
{
private class VersionItem
{
public string Version { get; set; }
public string DownloadUrl { get; set; }
public string ReleaseNotes { get; set; }
}
private List<VersionItem> versionList = new List<VersionItem>();
private VersionItem selectedItem;
private UpdateChannel channel = UpdateChannel.Release;
private CancellationTokenSource downloadCts = null;
public HistoryRollbackWindow(UpdateChannel channel = UpdateChannel.Release)
{
InitializeComponent();
this.channel = channel;
// 设置窗口置顶
this.Topmost = true;
// 应用当前主题
ApplyCurrentTheme();
// 隐藏主窗口
HideMainWindow();
LoadVersions();
}
/// <summary>
/// 隐藏主窗口
/// </summary>
private void HideMainWindow()
{
try
{
// 获取主窗口实例
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null)
{
// 隐藏主窗口
mainWindow.Visibility = Visibility.Hidden;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"隐藏主窗口时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 应用当前主题设置
/// </summary>
private void ApplyCurrentTheme()
{
try
{
// 根据主窗口的主题设置应用主题
switch (MainWindow.Settings.Appearance.Theme)
{
case 0: // 浅色主题
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
UpdateColorsForLightTheme();
break;
case 1: // 深色主题
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
UpdateColorsForDarkTheme();
break;
case 2: // 跟随系统
if (IsSystemThemeLight())
{
ThemeManager.SetRequestedTheme(this, ElementTheme.Light);
UpdateColorsForLightTheme();
}
else
{
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
UpdateColorsForDarkTheme();
}
break;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用主题时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新为浅色主题颜色
/// </summary>
private void UpdateColorsForLightTheme()
{
try
{
// 更新主要颜色资源
Resources["PrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb));
Resources["TextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x1f, 0x29, 0x37));
Resources["TextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x6b, 0x72, 0x80));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新浅色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新为深色主题颜色
/// </summary>
private void UpdateColorsForDarkTheme()
{
try
{
// 更新主要颜色资源
Resources["PrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0x3b, 0x82, 0xf6));
Resources["TextPrimaryBrush"] = new SolidColorBrush(Color.FromRgb(0xf9, 0xfa, 0xfb));
Resources["TextSecondaryBrush"] = new SolidColorBrush(Color.FromRgb(0x9c, 0xa3, 0xaf));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新深色主题颜色时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 检查系统是否为浅色主题
/// </summary>
private bool IsSystemThemeLight()
{
try
{
using (var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"))
{
if (key?.GetValue("AppsUseLightTheme") is int value)
{
return value == 1;
}
}
}
catch
{
// 如果无法读取注册表,默认返回true(浅色主题)
}
return true;
}
private async void LoadVersions()
{
LogHelper.WriteLogToFile($"HistoryRollback | 开始加载历史版本,通道: {channel}");
RollbackButton.IsEnabled = false;
VersionComboBox.Items.Clear();
DownloadProgressPanel.Visibility = Visibility.Collapsed;
DownloadProgressBar.Value = 0;
DownloadProgressText.Text = "";
ReleaseNotesViewer.Markdown = "正在获取历史版本...";
var releases = await AutoUpdateHelper.GetAllGithubReleases(channel);
versionList.Clear();
foreach (var (version, url, notes) in releases)
{
versionList.Add(new VersionItem { Version = version, DownloadUrl = url, ReleaseNotes = notes });
}
// 按版本号数字降序排列
versionList = versionList.OrderByDescending(v => ParseVersionForSort(v.Version)).ToList();
VersionComboBox.ItemsSource = versionList;
if (versionList.Count > 0)
{
VersionComboBox.SelectedIndex = 0;
RollbackButton.IsEnabled = true;
LogHelper.WriteLogToFile($"HistoryRollback | 加载到 {versionList.Count} 个历史版本");
}
else
{
ReleaseNotesViewer.Markdown = "未获取到历史版本信息。";
LogHelper.WriteLogToFile("HistoryRollback | 未获取到历史版本信息", LogHelper.LogType.Warning);
}
}
// 辅助方法:解析版本号用于排序
private Version ParseVersionForSort(string version)
{
var v = version.TrimStart('v', 'V');
Version result;
if (Version.TryParse(v, out result))
return result;
return new Version(0, 0, 0, 0);
}
private void VersionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
selectedItem = VersionComboBox.SelectedItem as VersionItem;
if (selectedItem != null)
{
ReleaseNotesViewer.Markdown = selectedItem.ReleaseNotes ?? "无更新日志";
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择版本: {selectedItem.Version}");
}
// 取消聚焦,防止父级自动滚动
Keyboard.ClearFocus();
}
private async void RollbackButton_Click(object sender, RoutedEventArgs e)
{
if (selectedItem == null) return;
var dialog = new ContentDialog
{
Title = "暂停自动更新",
PrimaryButtonText = "确定",
SecondaryButtonText = "取消"
};
var panel = new iNKORE.UI.WPF.Controls.SimpleStackPanel
{
Spacing = 16,
Margin = new Thickness(0, 10, 0, 0)
};
var textBlock = new TextBlock
{
Text = "请选择在回滚后多久不再接收自动更新:",
FontSize = 14,
Foreground = (Brush)Resources["TextPrimaryBrush"]
};
var daysComboBox = new System.Windows.Controls.ComboBox
{
Width = 200,
Height = 36,
HorizontalAlignment = System.Windows.HorizontalAlignment.Left
};
for (int i = 0; i <= 7; i++)
{
daysComboBox.Items.Add(new System.Windows.Controls.ComboBoxItem
{
Content = $"{i} 天",
Tag = i
});
}
daysComboBox.SelectedIndex = 0;
panel.Children.Add(textBlock);
panel.Children.Add(daysComboBox);
dialog.Content = panel;
var dialogResult = await dialog.ShowAsync();
if (dialogResult == ContentDialogResult.Primary)
{
int days = 1;
if (daysComboBox.SelectedItem is System.Windows.Controls.ComboBoxItem selectedItemCombo &&
selectedItemCombo.Tag != null &&
int.TryParse(selectedItemCombo.Tag.ToString(), out int selectedDays))
{
days = selectedDays;
}
if (days == 0)
{
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = "";
}
else
{
DateTime pauseUntilDate = DateTime.Now.AddDays(days);
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = pauseUntilDate.ToString("yyyy-MM-dd");
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择暂停自动更新 {days} 天,截止日期: {pauseUntilDate:yyyy-MM-dd}");
}
MainWindow.SaveSettingsToFile();
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择暂停自动更新 {days} 天");
}
else
{
LogHelper.WriteLogToFile("HistoryRollback | 用户取消了回滚操作");
return;
}
LogHelper.WriteLogToFile($"HistoryRollback | 用户确认回滚,目标版本: {selectedItem.Version}");
RollbackButton.IsEnabled = false;
VersionComboBox.IsEnabled = false;
DownloadProgressPanel.Visibility = Visibility.Visible;
DownloadProgressBar.Value = 0;
DownloadProgressText.Text = "正在准备下载...";
bool downloadSuccess = false;
try
{
downloadSuccess = await AutoUpdateHelper.StartManualDownloadAndInstall(
selectedItem.Version,
channel,
(percent, text) =>
{
Dispatcher.Invoke(() =>
{
DownloadProgressBar.Value = percent;
DownloadProgressText.Text = text;
});
}
);
}
catch (Exception ex)
{
DownloadProgressText.Text = $"下载失败: {ex.Message}";
LogHelper.WriteLogToFile($"HistoryRollback | 下载异常: {ex.Message}", LogHelper.LogType.Error);
}
if (downloadSuccess)
{
DownloadProgressBar.Value = 100;
DownloadProgressText.Text = "下载完成,准备安装...";
await Task.Delay(800);
DialogResult = true;
Close();
}
else
{
DownloadProgressText.Text = "下载失败,请检查网络后重试。";
RollbackButton.IsEnabled = true;
VersionComboBox.IsEnabled = true;
}
}
protected override void OnClosing(CancelEventArgs e)
{
downloadCts?.Cancel();
try
{
// 获取主窗口实例
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null)
{
// 重新显示主窗口
mainWindow.Visibility = Visibility.Visible;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"显示主窗口时出错: {ex.Message}", LogHelper.LogType.Error);
}
base.OnClosing(e);
}
}
}
@@ -10,140 +10,308 @@
xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:controls="clr-namespace:Ink_Canvas.Controls;assembly=InkCanvas.Controls" xmlns:controls="clr-namespace:Ink_Canvas.Controls;assembly=InkCanvas.Controls"
xmlns:c="clr-namespace:Ink_Canvas.Converter" xmlns:c="clr-namespace:Ink_Canvas.Converter"
xmlns:mdxam="clr-namespace:MdXaml;assembly=MdXaml"
mc:Ignorable="d" mc:Ignorable="d"
Title="更新"> Title="更新">
<ScrollViewer PanningMode="VerticalFirst"> <ScrollViewer PanningMode="VerticalFirst">
<Grid Margin="59,0,59,0"> <Grid Margin="59,0,59,0">
<FrameworkElement.Resources> <FrameworkElement.Resources>
<sys:Double x:Key="SettingsCardSpacing">4</sys:Double> <sys:Double x:Key="SettingsCardSpacing">4</sys:Double>
<c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> <c:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<Style x:Key="SettingsSectionHeaderTextBlockStyle" <Style x:Key="SettingsSectionHeaderTextBlockStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}" BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock"> TargetType="TextBlock">
<Style.Setters>
<Setter Property="Margin" Value="1,30,0,6" /> <Setter Property="Margin" Value="1,30,0,6" />
</Style.Setters> </Style>
</Style> </FrameworkElement.Resources>
</FrameworkElement.Resources>
<Grid>
<ikw:SimpleStackPanel MaxWidth="1000"
HorizontalAlignment="Stretch"
Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" <ikw:SimpleStackPanel MaxWidth="1000"
Text="自动更新" /> HorizontalAlignment="Stretch"
Spacing="{StaticResource SettingsCardSpacing}">
<controls:LabeledSettingsCard x:Name="CardAutoUpdate" <!-- 状态横幅 -->
Header="{i18n:I18n Key=Header_AutoUpdate}" <Border Margin="0,16,0,0"
Description="{i18n:I18n Key=Startup_AutoUpdateHint}" Padding="20"
Icon="{x:Static ui:SegoeFluentIcons.Sync}" CornerRadius="8"
SwitchName="ToggleSwitchIsAutoUpdate" Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
Toggled="ToggleSwitchIsAutoUpdate_Toggled" /> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:LabeledSettingsCard x:Name="CardSilentUpdate" <Border Grid.Column="0"
Header="{i18n:I18n Key=Header_SilentUpdate}" Width="48" Height="48"
Description="{i18n:I18n Key=Startup_SilentUpdateHint}" CornerRadius="24"
Icon="{x:Static ui:SegoeFluentIcons.QuietHours}" VerticalAlignment="Top"
SwitchName="ToggleSwitchIsAutoUpdateWithSilence" Margin="0,0,16,0"
ShowWhen="{Binding IsOn, ElementName=CardAutoUpdate}" Background="{DynamicResource SystemControlHighlightAccentBrush}">
d:Visibility="Visible" <ui:FontIcon x:Name="StatusIcon"
Toggled="ToggleSwitchIsAutoUpdateWithSilence_Toggled" /> FontSize="22"
Foreground="White"
Icon="{x:Static ui:SegoeFluentIcons.Completed}"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ui:SettingsExpander x:Name="ExpanderSilentUpdateTime" <ikw:SimpleStackPanel Grid.Column="1" Spacing="4">
Header="{i18n:I18n Key=Startup_SilentUpdateTimePeriod}" <TextBlock x:Name="StatusTitle"
Visibility="{Binding IsOn, ElementName=CardSilentUpdate, Converter={StaticResource BooleanToVisibilityConverter}}" FontSize="22" FontWeight="SemiBold"
IsExpanded="{Binding IsOn, ElementName=CardSilentUpdate, Mode=OneWay}" Text="正在加载更新状态..." />
d:Visibility="Visible" d:IsExpanded="True"> <TextBlock x:Name="StatusSubtitle"
<ui:SettingsExpander.HeaderIcon> Opacity="0.75"
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Settings}" /> TextWrapping="Wrap"
</ui:SettingsExpander.HeaderIcon> Text="" />
<ui:SettingsExpander.Items>
<ui:SettingsCard Header="{i18n:I18n Key=Startup_SilentUpdateFullHint}"> <!-- 进度区 -->
<ui:SettingsCard.Content> <ikw:SimpleStackPanel x:Name="ProgressPanel"
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12"> Visibility="Collapsed"
<ikw:SimpleStackPanel Orientation="Horizontal"> Margin="0,8,0,0"
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center" Spacing="4">
Text="{i18n:I18n Key=Startup_StartTime}" FontSize="14" /> <TextBlock x:Name="ProgressText" FontSize="13" Opacity="0.8" />
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox" <ProgressBar x:Name="ProgressBar"
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged" /> Style="{DynamicResource DefaultProgressBarStyle}"
</ikw:SimpleStackPanel> Height="6"
<ikw:SimpleStackPanel Orientation="Horizontal"> Minimum="0" Maximum="100"
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center" Value="0" />
Text="{i18n:I18n Key=Startup_EndTime}" FontSize="14" /> </ikw:SimpleStackPanel>
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged" /> <!-- 操作按钮 -->
</ikw:SimpleStackPanel> <ikw:SimpleStackPanel Orientation="Horizontal"
Margin="0,12,0,0"
Spacing="8">
<Button x:Name="CheckUpdateButton"
Style="{DynamicResource AccentButtonStyle}"
Click="CheckUpdateButton_Click">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Refresh}" FontSize="14" />
<TextBlock Text="检查更新" />
</ikw:SimpleStackPanel> </ikw:SimpleStackPanel>
</ui:SettingsCard.Content> </Button>
<Button x:Name="UpdateNowButton"
Visibility="Collapsed"
Style="{DynamicResource AccentButtonStyle}"
Click="UpdateNowButton_Click">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Download}" FontSize="14" />
<TextBlock Text="立即下载并安装" />
</ikw:SimpleStackPanel>
</Button>
<Button x:Name="UpdateLaterButton"
Visibility="Collapsed"
Click="UpdateLaterButton_Click">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.QuietHours}" FontSize="14" />
<TextBlock Text="退出时安装" />
</ikw:SimpleStackPanel>
</Button>
<Button x:Name="SkipVersionButton"
Visibility="Collapsed"
Click="SkipVersionButton_Click">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Cancel}" FontSize="14" />
<TextBlock Text="跳过此版本" />
</ikw:SimpleStackPanel>
</Button>
<Button x:Name="CancelDownloadButton"
Visibility="Collapsed"
Click="CancelDownloadButton_Click">
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Cancel}" FontSize="14" />
<TextBlock Text="取消" />
</ikw:SimpleStackPanel>
</Button>
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</Grid>
</Border>
<!-- Tab 切换 -->
<TabControl x:Name="UpdateTabControl"
Margin="0,16,0,0"
HorizontalContentAlignment="Left"
Style="{StaticResource TabControlPivotStyle}"
BorderThickness="0"
Background="Transparent"
SelectionChanged="UpdateTabControl_SelectionChanged">
<TabControl.Resources>
<sys:Double x:Key="PivotHeaderItemFontSize">16</sys:Double>
</TabControl.Resources>
<!-- 更新日志 -->
<TabItem Header="更新日志">
<Border Margin="0,8,0,0"
Padding="16"
CornerRadius="8"
MinHeight="200"
Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
PanningMode="VerticalOnly"
MaxHeight="500">
<mdxam:MarkdownScrollViewer x:Name="ChangelogViewer"
MarkdownStyleName="GithubLike" />
</ScrollViewer>
</Border>
</TabItem>
<!-- 更新设置 -->
<TabItem Header="更新设置">
<ikw:SimpleStackPanel Margin="0,8,0,0"
Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="自动更新" />
<controls:LabeledSettingsCard x:Name="CardAutoUpdate"
Header="{i18n:I18n Key=Header_AutoUpdate}"
Description="{i18n:I18n Key=Startup_AutoUpdateHint}"
Icon="{x:Static ui:SegoeFluentIcons.Sync}"
SwitchName="ToggleSwitchIsAutoUpdate"
Toggled="ToggleSwitchIsAutoUpdate_Toggled" />
<controls:LabeledSettingsCard x:Name="CardSilentUpdate"
Header="{i18n:I18n Key=Header_SilentUpdate}"
Description="{i18n:I18n Key=Startup_SilentUpdateHint}"
Icon="{x:Static ui:SegoeFluentIcons.QuietHours}"
SwitchName="ToggleSwitchIsAutoUpdateWithSilence"
ShowWhen="{Binding IsOn, ElementName=CardAutoUpdate}"
d:Visibility="Visible"
Toggled="ToggleSwitchIsAutoUpdateWithSilence_Toggled" />
<ui:SettingsExpander x:Name="ExpanderSilentUpdateTime"
Header="{i18n:I18n Key=Startup_SilentUpdateTimePeriod}"
Visibility="{Binding IsOn, ElementName=CardSilentUpdate, Converter={StaticResource BooleanToVisibilityConverter}}"
IsExpanded="{Binding IsOn, ElementName=CardSilentUpdate, Mode=OneWay}"
d:Visibility="Visible" d:IsExpanded="True">
<ui:SettingsExpander.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Settings}" />
</ui:SettingsExpander.HeaderIcon>
<ui:SettingsExpander.Items>
<ui:SettingsCard Header="{i18n:I18n Key=Startup_SilentUpdateFullHint}">
<ui:SettingsCard.Content>
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="12">
<ikw:SimpleStackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
Text="{i18n:I18n Key=Startup_StartTime}" FontSize="14" />
<ComboBox x:Name="AutoUpdateWithSilenceStartTimeComboBox"
SelectionChanged="AutoUpdateWithSilenceStartTimeComboBox_SelectionChanged" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center"
Text="{i18n:I18n Key=Startup_EndTime}" FontSize="14" />
<ComboBox x:Name="AutoUpdateWithSilenceEndTimeComboBox"
SelectionChanged="AutoUpdateWithSilenceEndTimeComboBox_SelectionChanged" />
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</ui:SettingsCard.Content>
</ui:SettingsCard>
<ui:SettingsCard Header="{i18n:I18n Key=Startup_TimePeriodHint}" />
</ui:SettingsExpander.Items>
</ui:SettingsExpander>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="更新通道" />
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdateChannel}"
Description="{i18n:I18n Key=Startup_UpdateChannelHint}">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.SyncFolder}" />
</ui:SettingsCard.HeaderIcon>
<ComboBox x:Name="UpdateChannelSelector"
SelectionChanged="UpdateChannelSelector_SelectionChanged">
<ComboBoxItem Content="{i18n:I18n Key=Update_Release}" Tag="Release" />
<ComboBoxItem Content="{i18n:I18n Key=Update_Preview}" Tag="Preview" />
<ComboBoxItem Content="{i18n:I18n Key=Update_Beta}" Tag="Beta" />
</ComboBox>
</ui:SettingsCard> </ui:SettingsCard>
<ui:SettingsCard Header="{i18n:I18n Key=Startup_TimePeriodHint}" />
</ui:SettingsExpander.Items>
</ui:SettingsExpander>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" <ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdatePackageArchitecture}"
Text="更新通道" /> Description="{i18n:I18n Key=Startup_UpdatePackageArchitectureHint}">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.DeveloperTools}" />
</ui:SettingsCard.HeaderIcon>
<ComboBox x:Name="UpdatePackageArchitectureSelector"
SelectionChanged="UpdatePackageArchitectureSelector_SelectionChanged">
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X86}" Tag="X86" />
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X64}" Tag="X64" />
</ComboBox>
</ui:SettingsCard>
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdateChannel}" <TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Description="{i18n:I18n Key=Startup_UpdateChannelHint}"> Text="维护" />
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.SyncFolder}" />
</ui:SettingsCard.HeaderIcon>
<ComboBox x:Name="UpdateChannelSelector"
SelectionChanged="UpdateChannelSelector_SelectionChanged">
<ComboBoxItem Content="{i18n:I18n Key=Update_Release}" Tag="Release" />
<ComboBoxItem Content="{i18n:I18n Key=Update_Preview}" Tag="Preview" />
<ComboBoxItem Content="{i18n:I18n Key=Update_Beta}" Tag="Beta" />
</ComboBox>
</ui:SettingsCard>
<ui:SettingsCard Header="{i18n:I18n Key=Startup_UpdatePackageArchitecture}" <ui:SettingsCard Header="{i18n:I18n Key=Btn_FixVersion}"
Description="{i18n:I18n Key=Startup_UpdatePackageArchitectureHint}"> Description="{i18n:I18n Key=Startup_FixVersionHint}">
<ui:SettingsCard.HeaderIcon> <ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.DeveloperTools}" /> <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Repair}" />
</ui:SettingsCard.HeaderIcon> </ui:SettingsCard.HeaderIcon>
<ComboBox x:Name="UpdatePackageArchitectureSelector" <Button x:Name="FixVersionButton" Content="{i18n:I18n Key=Btn_FixVersion}"
SelectionChanged="UpdatePackageArchitectureSelector_SelectionChanged"> Click="FixVersionButton_Click" />
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X86}" Tag="X86" /> </ui:SettingsCard>
<ComboBoxItem Content="{i18n:I18n Key=Update_PackageArch_X64}" Tag="X64" /> </ikw:SimpleStackPanel>
</ComboBox> </TabItem>
</ui:SettingsCard>
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" <!-- 历史版本 -->
Text="手动操作" /> <TabItem x:Name="HistoryTabItem" Header="历史版本">
<ikw:SimpleStackPanel Margin="0,8,0,0" Spacing="12">
<ui:SettingsCard Header="选择历史版本"
Description="选择一个旧版本进行回滚(将下载并安装该版本)。">
<ui:SettingsCard.HeaderIcon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.History}" />
</ui:SettingsCard.HeaderIcon>
<ComboBox x:Name="VersionComboBox"
MinWidth="220"
DisplayMemberPath="Version"
SelectionChanged="VersionComboBox_SelectionChanged" />
</ui:SettingsCard>
<ui:SettingsCard Header="{i18n:I18n Key=Btn_ManualUpdate}" <Border Padding="16"
Description="{i18n:I18n Key=Startup_ManualUpdateHint}"> CornerRadius="8"
<ui:SettingsCard.HeaderIcon> MinHeight="200"
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Search}" /> Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
</ui:SettingsCard.HeaderIcon> <ScrollViewer x:Name="ReleaseNotesScrollViewer"
<Button x:Name="ManualUpdateButton" Content="{i18n:I18n Key=Btn_ManualUpdate}" VerticalScrollBarVisibility="Auto"
Click="ManualUpdateButton_Click" /> HorizontalScrollBarVisibility="Disabled"
</ui:SettingsCard> PanningMode="VerticalOnly"
MaxHeight="350">
<mdxam:MarkdownScrollViewer x:Name="ReleaseNotesViewer"
MarkdownStyleName="GithubLike"
IsHitTestVisible="False" />
</ScrollViewer>
</Border>
<ui:SettingsCard Header="{i18n:I18n Key=Btn_FixVersion}" <Button x:Name="RollbackButton"
Description="{i18n:I18n Key=Startup_FixVersionHint}"> HorizontalAlignment="Left"
<ui:SettingsCard.HeaderIcon> IsEnabled="False"
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Repair}" /> Style="{DynamicResource AccentButtonStyle}"
</ui:SettingsCard.HeaderIcon> Click="RollbackButton_Click">
<Button x:Name="FixVersionButton" Content="{i18n:I18n Key=Btn_FixVersion}" <ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
Click="FixVersionButton_Click" /> <ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Undo}" FontSize="14" />
</ui:SettingsCard> <TextBlock Text="回滚到此版本" />
</ikw:SimpleStackPanel>
</Button>
<ui:SettingsCard Header="{i18n:I18n Key=Btn_HistoryRollback}" <Border x:Name="RollbackProgressPanel"
Description="{i18n:I18n Key=Startup_HistoryRollbackHint}"> Visibility="Collapsed"
<ui:SettingsCard.HeaderIcon> Padding="12"
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.History}" /> CornerRadius="8"
</ui:SettingsCard.HeaderIcon> Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
<Button Content="{i18n:I18n Key=Btn_HistoryRollback}" BorderBrush="{DynamicResource SettingsPageBorderBrush}"
Click="HistoryRollbackButton_Click" /> BorderThickness="1">
</ui:SettingsCard> <ikw:SimpleStackPanel Spacing="6">
<TextBlock x:Name="RollbackProgressText" Text="正在准备下载..." FontSize="13" />
<ProgressBar x:Name="RollbackProgressBar"
Style="{DynamicResource DefaultProgressBarStyle}"
Height="6"
Minimum="0" Maximum="100" Value="0" />
</ikw:SimpleStackPanel>
</Border>
</ikw:SimpleStackPanel>
</TabItem>
</TabControl>
<Rectangle Height="48" /> <Rectangle Height="48" />
</ikw:SimpleStackPanel> </ikw:SimpleStackPanel>
</Grid>
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</ui:Page> </ui:Page>
@@ -1,9 +1,15 @@
using Ink_Canvas.Helpers; using Ink_Canvas.Helpers;
using Ink_Canvas.Windows.SettingsViews.Helpers; using Ink_Canvas.Windows.SettingsViews.Helpers;
using iNKORE.UI.WPF.Modern.Common.IconKeys;
using iNKORE.UI.WPF.Modern.Controls;
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading; using System.Windows.Threading;
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox; using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
@@ -11,9 +17,35 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
{ {
public partial class UpdatePage : iNKORE.UI.WPF.Modern.Controls.Page public partial class UpdatePage : iNKORE.UI.WPF.Modern.Controls.Page
{ {
private bool _isLoaded = false; private enum UpdateUiState
private bool _isChangingUpdateChannelInternally = false; {
private bool _isChangingUpdatePackageArchInternally = false; Idle,
Checking,
UpdateAvailable,
Downloading,
Downloaded,
NetworkError
}
private class VersionItem
{
public string Version { get; set; }
public string DownloadUrl { get; set; }
public string ReleaseNotes { get; set; }
}
private bool _isLoaded;
private bool _isChangingUpdateChannelInternally;
private bool _isChangingUpdatePackageArchInternally;
private UpdateUiState _state = UpdateUiState.Idle;
private string _remoteVersion;
private AutoUpdateHelper.UpdateLineGroup _remoteLineGroup;
private string _remoteReleaseNotes;
private List<VersionItem> _versionList = new List<VersionItem>();
private VersionItem _selectedHistoricalItem;
private bool _isHistoryLoaded;
public UpdatePage() public UpdatePage()
{ {
@@ -21,12 +53,52 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
Loaded += UpdatePage_Loaded; Loaded += UpdatePage_Loaded;
} }
private void UpdatePage_Loaded(object sender, RoutedEventArgs e) private async void UpdatePage_Loaded(object sender, RoutedEventArgs e)
{ {
LoadSettings(); LoadSettings();
_isLoaded = true; _isLoaded = true;
// 复用启动时自动检查的结果,避免二次检查
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow != null && !string.IsNullOrEmpty(mainWindow.AvailableLatestVersion))
{
_remoteVersion = mainWindow.AvailableLatestVersion;
_remoteLineGroup = mainWindow.AvailableLatestLineGroup;
_remoteReleaseNotes = mainWindow.AvailableLatestReleaseNotes;
try
{
var statusFile = AutoUpdateHelper.GetUpdateDownloadStatusFilePath(_remoteVersion);
if (System.IO.File.Exists(statusFile) &&
System.IO.File.ReadAllText(statusFile).Trim().ToLower() == "true")
{
ApplyState(UpdateUiState.Downloaded);
}
else
{
ApplyState(UpdateUiState.UpdateAvailable);
}
}
catch
{
ApplyState(UpdateUiState.UpdateAvailable);
}
if (!string.IsNullOrEmpty(_remoteReleaseNotes))
ChangelogViewer.Markdown = _remoteReleaseNotes;
else
ChangelogViewer.Markdown = "切换到 *历史版本* 或点击 *检查更新* 查看具体更新日志。";
return;
}
// 没有缓存的检查结果时,仅展示空白;不主动联网,避免打开页面就触发请求
ApplyState(UpdateUiState.Idle);
ChangelogViewer.Markdown = "点击 *检查更新* 来获取最新版本及更新日志。";
await System.Threading.Tasks.Task.CompletedTask;
} }
#region
private void LoadSettings() private void LoadSettings()
{ {
_isLoaded = false; _isLoaded = false;
@@ -82,6 +154,8 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
_isLoaded = true; _isLoaded = true;
} }
#endregion
#region #region
private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e) private void ToggleSwitchIsAutoUpdate_Toggled(object sender, RoutedEventArgs e)
@@ -113,8 +187,7 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
try try
{ {
bool newState = CardSilentUpdate.IsOn; SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = CardSilentUpdate.IsOn;
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = newState;
SettingsManager.SaveSettingsToFile(); SettingsManager.SaveSettingsToFile();
} }
catch (Exception ex) catch (Exception ex)
@@ -197,7 +270,7 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
if (isTestChannel && !SettingsManager.Settings.Startup.HasAcceptedTelemetryPrivacy) if (isTestChannel && !SettingsManager.Settings.Startup.HasAcceptedTelemetryPrivacy)
{ {
MessageBox.Show( MessageBox.Show(
"加入预览 / 测试通道前,请先在关于页面勾选\u201C我已阅读并同意 privacy 中的隐私说明\u201D。", "加入预览 / 测试通道前,请先在关于页面勾选我已阅读并同意 privacy 中的隐私说明。",
"需要同意隐私说明", "需要同意隐私说明",
MessageBoxButton.OK, MessageBoxButton.OK,
MessageBoxImage.Warning); MessageBoxImage.Warning);
@@ -238,6 +311,13 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
LogHelper.WriteLogToFile($"Settings | Update channel changed to {SettingsManager.Settings.Startup.UpdateChannel}"); LogHelper.WriteLogToFile($"Settings | Update channel changed to {SettingsManager.Settings.Startup.UpdateChannel}");
SettingsManager.SaveSettingsToFile(); SettingsManager.SaveSettingsToFile();
// 通道切换后强制刷新更新日志和历史版本缓存
_isHistoryLoaded = false;
_versionList.Clear();
VersionComboBox.ItemsSource = null;
ReleaseNotesViewer.Markdown = "";
await LoadChangelogAsync();
if (SettingsManager.Settings.Startup.IsAutoUpdate) if (SettingsManager.Settings.Startup.IsAutoUpdate)
{ {
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check"); LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, performing immediate update check");
@@ -262,10 +342,6 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
}); });
} }
} }
else
{
LogHelper.WriteLogToFile($"AutoUpdate | Channel changed to {newChannel}, but auto-update is disabled");
}
} }
private void RevertChannelSelection(UpdateChannel targetChannel) private void RevertChannelSelection(UpdateChannel targetChannel)
@@ -295,124 +371,520 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
#endregion #endregion
#region #region
private async void ManualUpdateButton_Click(object sender, RoutedEventArgs e) private void ApplyState(UpdateUiState state, string customSubtitle = null)
{ {
ManualUpdateButton.IsEnabled = false; _state = state;
ManualUpdateButton.Content = "正在检查更新...";
CheckUpdateButton.Visibility = Visibility.Collapsed;
UpdateNowButton.Visibility = Visibility.Collapsed;
UpdateLaterButton.Visibility = Visibility.Collapsed;
SkipVersionButton.Visibility = Visibility.Collapsed;
CancelDownloadButton.Visibility = Visibility.Collapsed;
ProgressPanel.Visibility = Visibility.Collapsed;
CheckUpdateButton.IsEnabled = true;
switch (state)
{
case UpdateUiState.Idle:
StatusIcon.Icon = SegoeFluentIcons.Completed;
StatusTitle.Text = "已是最新版本";
StatusSubtitle.Text = customSubtitle ?? BuildLastCheckSubtitle();
CheckUpdateButton.Visibility = Visibility.Visible;
break;
case UpdateUiState.Checking:
StatusIcon.Icon = SegoeFluentIcons.Sync;
StatusTitle.Text = "正在检查更新...";
StatusSubtitle.Text = "";
CheckUpdateButton.Visibility = Visibility.Visible;
CheckUpdateButton.IsEnabled = false;
ProgressPanel.Visibility = Visibility.Visible;
ProgressText.Text = "正在连接更新服务器...";
ProgressBar.IsIndeterminate = true;
break;
case UpdateUiState.UpdateAvailable:
StatusIcon.Icon = SegoeFluentIcons.Upload;
StatusTitle.Text = $"检测到新版本 {_remoteVersion}";
StatusSubtitle.Text = customSubtitle ?? $"当前版本 {GetCurrentVersion()} → {_remoteVersion}";
UpdateNowButton.Visibility = Visibility.Visible;
UpdateLaterButton.Visibility = Visibility.Visible;
SkipVersionButton.Visibility = Visibility.Visible;
break;
case UpdateUiState.Downloading:
StatusIcon.Icon = SegoeFluentIcons.Download;
StatusTitle.Text = "正在下载更新...";
StatusSubtitle.Text = customSubtitle ?? $"目标版本 {_remoteVersion}";
ProgressPanel.Visibility = Visibility.Visible;
ProgressBar.IsIndeterminate = false;
break;
case UpdateUiState.Downloaded:
StatusIcon.Icon = SegoeFluentIcons.Download;
StatusTitle.Text = "更新已下载完成";
StatusSubtitle.Text = customSubtitle ?? $"将在软件关闭时自动安装 {_remoteVersion}";
CheckUpdateButton.Visibility = Visibility.Visible;
break;
case UpdateUiState.NetworkError:
StatusIcon.Icon = SegoeFluentIcons.Error;
StatusTitle.Text = "网络错误";
StatusSubtitle.Text = customSubtitle ?? "请检查网络连接后重试。";
CheckUpdateButton.Visibility = Visibility.Visible;
break;
}
}
private string BuildLastCheckSubtitle()
{
string current = GetCurrentVersion();
return $"当前版本 {current}";
}
private static string GetCurrentVersion()
{
try
{
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
catch
{
return "未知";
}
}
#endregion
#region
private async System.Threading.Tasks.Task LoadChangelogAsync()
{
try
{
ChangelogViewer.Markdown = "正在加载更新日志...";
// 优先尝试从 GitHub API 获取最新 Release 的 body
try
{
var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
var latest = releases?
.OrderByDescending(r => ParseVersionForSort(r.version))
.Select(r => (Tuple<string, string, string>)Tuple.Create(r.version, r.downloadUrl, r.releaseNotes))
.FirstOrDefault();
if (latest != null && !string.IsNullOrWhiteSpace(latest.Item3))
{
ChangelogViewer.Markdown = latest.Item3;
return;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"UpdatePage | GitHub API 获取更新日志失败,回退到镜像源: {ex.Message}", LogHelper.LogType.Warning);
}
// 回退到镜像源
string md = await AutoUpdateHelper.GetUpdateLog(SettingsManager.Settings.Startup.UpdateChannel);
ChangelogViewer.Markdown = string.IsNullOrEmpty(md) ? "暂无更新日志。" : md;
}
catch (Exception ex)
{
ChangelogViewer.Markdown = $"加载更新日志失败:{ex.Message}";
}
}
#endregion
#region / /
private async void CheckUpdateButton_Click(object sender, RoutedEventArgs e)
{
ApplyState(UpdateUiState.Checking);
try try
{ {
LogHelper.WriteLogToFile("ManualUpdate | Manual update button clicked"); LogHelper.WriteLogToFile("ManualUpdate | Manual update button clicked");
var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(SettingsManager.Settings.Startup.UpdateChannel, true, false); string remoteVersion = null;
string apiReleaseNotes = null;
AutoUpdateHelper.UpdateLineGroup lineGroup = null;
if (remoteVersion != null) // 优先通过 GitHub Releases API 获取最新版本
try
{ {
LogHelper.WriteLogToFile($"ManualUpdate | Found new version: {remoteVersion}"); var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
var latest = releases?
string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); .OrderByDescending(r => ParseVersionForSort(r.version))
.Select(r => Tuple.Create(r.version, r.downloadUrl, r.releaseNotes))
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, remoteVersion, "", apiReleaseNotes); .FirstOrDefault();
updateWindow.Owner = Application.Current.MainWindow; if (latest != null && !string.IsNullOrEmpty(latest.Item1))
bool? dialogResult = updateWindow.ShowDialog();
if (dialogResult != true)
{ {
LogHelper.WriteLogToFile("ManualUpdate | Update dialog closed without selection"); var localVersion = new Version(GetCurrentVersion());
return; var remote = ParseVersionForSort(latest.Item1);
if (remote > localVersion)
{
remoteVersion = latest.Item1.TrimStart('v', 'V');
apiReleaseNotes = latest.Item3;
}
} }
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"UpdatePage | GitHub API 检查更新失败,回退到 CheckForUpdates: {ex.Message}", LogHelper.LogType.Warning);
}
var mainWindow = Application.Current.MainWindow as MainWindow; // 回退:调用统一的 CheckForUpdates(包含镜像源 txt 方案)
if (string.IsNullOrEmpty(remoteVersion))
{
var (rv, lg, notes) = await AutoUpdateHelper.CheckForUpdates(
SettingsManager.Settings.Startup.UpdateChannel, true, false);
remoteVersion = rv;
lineGroup = lg;
if (string.IsNullOrEmpty(apiReleaseNotes)) apiReleaseNotes = notes;
}
switch (updateWindow.Result) if (!string.IsNullOrEmpty(remoteVersion))
{ {
case HasNewUpdateWindow.UpdateResult.UpdateNow: _remoteVersion = remoteVersion;
LogHelper.WriteLogToFile("ManualUpdate | User chose to update now"); _remoteLineGroup = lineGroup;
MessageBox.Show("开始下载更新,请稍候...", "正在更新", MessageBoxButton.OK, MessageBoxImage.Information); _remoteReleaseNotes = apiReleaseNotes;
bool isDownloadSuccessful = mainWindow != null if (!string.IsNullOrEmpty(apiReleaseNotes))
&& await mainWindow.DownloadUpdateWithFallback(remoteVersion, lineGroup, SettingsManager.Settings.Startup.UpdateChannel); ChangelogViewer.Markdown = apiReleaseNotes;
if (isDownloadSuccessful) ApplyState(UpdateUiState.UpdateAvailable);
{
MessageBoxResult result = MessageBox.Show("更新已下载完成,点击确定后将关闭软件并安装新版本!", "安装更新", MessageBoxButton.OKCancel, MessageBoxImage.Information);
if (result == MessageBoxResult.OK)
{
App.IsAppExitByUser = true;
AutoUpdateHelper.InstallNewVersionApp(remoteVersion, true);
Application.Current.Shutdown();
}
else
{
LogHelper.WriteLogToFile("ManualUpdate | User cancelled update installation");
}
}
else
{
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
break;
case HasNewUpdateWindow.UpdateResult.UpdateLater:
LogHelper.WriteLogToFile("ManualUpdate | User chose to update later");
isDownloadSuccessful = mainWindow != null
&& await mainWindow.DownloadUpdateWithFallback(remoteVersion, lineGroup, SettingsManager.Settings.Startup.UpdateChannel);
if (isDownloadSuccessful)
{
LogHelper.WriteLogToFile("ManualUpdate | Update downloaded successfully, will install when application closes");
SettingsManager.Settings.Startup.IsAutoUpdate = true;
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = true;
if (mainWindow != null)
mainWindow.StartSilentUpdateTimer();
MessageBox.Show("更新已下载完成,将在软件关闭时自动安装。", "更新已准备就绪", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
LogHelper.WriteLogToFile("ManualUpdate | Update download failed", LogHelper.LogType.Error);
MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
break;
case HasNewUpdateWindow.UpdateResult.SkipVersion:
LogHelper.WriteLogToFile($"ManualUpdate | User chose to skip version {remoteVersion}");
SettingsManager.Settings.Startup.SkippedVersion = remoteVersion;
SettingsManager.SaveSettingsToFile();
MessageBox.Show($"已设置跳过版本 {remoteVersion},在下次发布新版本之前不会再提示更新。",
"已跳过此版本",
MessageBoxButton.OK,
MessageBoxImage.Information);
break;
}
} }
else else
{ {
LogHelper.WriteLogToFile("ManualUpdate | No updates available"); ApplyState(UpdateUiState.Idle);
MessageBox.Show("当前已是最新版本!", "无可用更新", MessageBoxButton.OK, MessageBoxImage.Information);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
LogHelper.WriteLogToFile($"Error in ManualUpdateButton_Click: {ex.Message}", LogHelper.LogType.Error); LogHelper.WriteLogToFile($"Error in CheckUpdateButton_Click: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show( ApplyState(UpdateUiState.NetworkError, ex.Message);
$"手动更新过程中发生错误: {ex.Message}",
"更新错误",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
finally
{
ManualUpdateButton.IsEnabled = true;
ManualUpdateButton.Content = "手动更新";
} }
} }
private bool _downloadCancelled;
private static List<AutoUpdateHelper.UpdateLineGroup> _cachedOrderedGroups;
private static UpdateChannel _cachedGroupsChannel;
private static async System.Threading.Tasks.Task<List<AutoUpdateHelper.UpdateLineGroup>> GetOrderedGroupsCachedAsync(UpdateChannel channel)
{
if (_cachedOrderedGroups != null && _cachedOrderedGroups.Count > 0 && _cachedGroupsChannel == channel)
return _cachedOrderedGroups;
var groups = await AutoUpdateHelper.GetAvailableLineGroupsOrdered(channel);
_cachedOrderedGroups = groups;
_cachedGroupsChannel = channel;
return groups;
}
private async System.Threading.Tasks.Task<bool> DownloadWithProgressAsync()
{
_downloadCancelled = false;
var groups = await GetOrderedGroupsCachedAsync(SettingsManager.Settings.Startup.UpdateChannel);
if (groups == null || groups.Count == 0)
{
LogHelper.WriteLogToFile("UpdatePage | 没有可用的下载线路组", LogHelper.LogType.Error);
return false;
}
return await AutoUpdateHelper.DownloadSetupFileWithFallback(_remoteVersion, groups, (percent, text) =>
{
if (_downloadCancelled) return;
Dispatcher.Invoke(() =>
{
if (_state != UpdateUiState.Downloading) return;
ProgressBar.IsIndeterminate = false;
ProgressBar.Value = percent;
ProgressText.Text = text;
});
});
}
private async void UpdateNowButton_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(_remoteVersion)) return;
ApplyState(UpdateUiState.Downloading);
CancelDownloadButton.Visibility = Visibility.Visible;
ProgressBar.Value = 0;
ProgressText.Text = "正在准备下载...";
try
{
bool ok = await DownloadWithProgressAsync();
if (!ok)
{
ApplyState(UpdateUiState.NetworkError, "更新下载失败,请检查网络连接后重试。");
return;
}
MessageBoxResult result = MessageBox.Show(
"更新已下载完成,点击确定后将关闭软件并安装新版本!",
"安装更新",
MessageBoxButton.OKCancel,
MessageBoxImage.Information);
if (result == MessageBoxResult.OK)
{
App.IsAppExitByUser = true;
AutoUpdateHelper.InstallNewVersionApp(_remoteVersion, true);
Application.Current.Shutdown();
}
else
{
ApplyState(UpdateUiState.Downloaded);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"Error in UpdateNowButton_Click: {ex.Message}", LogHelper.LogType.Error);
ApplyState(UpdateUiState.NetworkError, ex.Message);
}
}
private async void UpdateLaterButton_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(_remoteVersion)) return;
var mainWindow = Application.Current.MainWindow as MainWindow;
if (mainWindow == null) return;
ApplyState(UpdateUiState.Downloading);
CancelDownloadButton.Visibility = Visibility.Visible;
ProgressBar.Value = 0;
ProgressText.Text = "正在后台下载...";
try
{
bool ok = await DownloadWithProgressAsync();
if (!ok)
{
ApplyState(UpdateUiState.NetworkError, "更新下载失败,请检查网络连接后重试。");
return;
}
SettingsManager.Settings.Startup.IsAutoUpdate = true;
SettingsManager.Settings.Startup.IsAutoUpdateWithSilence = true;
SettingsManager.SaveSettingsToFile();
CardAutoUpdate.IsOn = true;
CardSilentUpdate.IsOn = true;
mainWindow.StartSilentUpdateTimer();
ApplyState(UpdateUiState.Downloaded);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"Error in UpdateLaterButton_Click: {ex.Message}", LogHelper.LogType.Error);
ApplyState(UpdateUiState.NetworkError, ex.Message);
}
}
private void SkipVersionButton_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(_remoteVersion)) return;
SettingsManager.Settings.Startup.SkippedVersion = _remoteVersion;
SettingsManager.SaveSettingsToFile();
LogHelper.WriteLogToFile($"ManualUpdate | User chose to skip version {_remoteVersion}");
ApplyState(UpdateUiState.Idle, $"已跳过版本 {_remoteVersion}");
}
private void CancelDownloadButton_Click(object sender, RoutedEventArgs e)
{
_downloadCancelled = true;
AutoUpdateHelper.RequestCancelDownload();
ApplyState(UpdateUiState.UpdateAvailable);
}
#endregion
#region
private async void UpdateTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.OriginalSource != UpdateTabControl) return;
if (UpdateTabControl.SelectedItem == HistoryTabItem && !_isHistoryLoaded)
{
await LoadHistoryAsync();
}
}
private async void HistoryTabItem_GotFocus(object sender, RoutedEventArgs e)
{
// 兼容用户首次切到历史版本 Tab 时再加载
if (_isHistoryLoaded) return;
await LoadHistoryAsync();
}
private async System.Threading.Tasks.Task LoadHistoryAsync()
{
try
{
_isHistoryLoaded = true;
ReleaseNotesViewer.Markdown = "正在获取历史版本...";
RollbackButton.IsEnabled = false;
var releases = await AutoUpdateHelper.GetAllGithubReleases(SettingsManager.Settings.Startup.UpdateChannel);
_versionList = releases
.Select(r => new VersionItem { Version = r.version, DownloadUrl = r.downloadUrl, ReleaseNotes = r.releaseNotes })
.OrderByDescending(v => ParseVersionForSort(v.Version))
.ToList();
VersionComboBox.ItemsSource = _versionList;
if (_versionList.Count > 0)
{
VersionComboBox.SelectedIndex = 0;
RollbackButton.IsEnabled = true;
}
else
{
ReleaseNotesViewer.Markdown = "未获取到历史版本信息。";
}
}
catch (Exception ex)
{
ReleaseNotesViewer.Markdown = $"加载历史版本失败:{ex.Message}";
}
}
private static Version ParseVersionForSort(string version)
{
var v = (version ?? "").TrimStart('v', 'V');
return Version.TryParse(v, out var result) ? result : new Version(0, 0, 0, 0);
}
private void VersionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_selectedHistoricalItem = VersionComboBox.SelectedItem as VersionItem;
if (_selectedHistoricalItem != null)
{
ReleaseNotesViewer.Markdown = _selectedHistoricalItem.ReleaseNotes ?? "无更新日志";
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择版本: {_selectedHistoricalItem.Version}");
}
Keyboard.ClearFocus();
}
private async void RollbackButton_Click(object sender, RoutedEventArgs e)
{
if (_selectedHistoricalItem == null) return;
int days = await AskPauseDaysAsync();
if (days < 0)
{
LogHelper.WriteLogToFile("HistoryRollback | 用户取消了回滚操作");
return;
}
if (days == 0)
{
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = "";
}
else
{
DateTime pauseUntilDate = DateTime.Now.AddDays(days);
MainWindow.Settings.Startup.AutoUpdatePauseUntilDate = pauseUntilDate.ToString("yyyy-MM-dd");
LogHelper.WriteLogToFile($"HistoryRollback | 用户选择暂停自动更新 {days} 天,截止日期: {pauseUntilDate:yyyy-MM-dd}");
}
MainWindow.SaveSettingsToFile();
LogHelper.WriteLogToFile($"HistoryRollback | 用户确认回滚,目标版本: {_selectedHistoricalItem.Version}");
RollbackButton.IsEnabled = false;
VersionComboBox.IsEnabled = false;
RollbackProgressPanel.Visibility = Visibility.Visible;
RollbackProgressBar.Value = 0;
RollbackProgressText.Text = "正在准备下载...";
bool downloadSuccess = false;
try
{
downloadSuccess = await AutoUpdateHelper.StartManualDownloadAndInstall(
_selectedHistoricalItem.Version,
SettingsManager.Settings.Startup.UpdateChannel,
(percent, text) =>
{
Dispatcher.Invoke(() =>
{
RollbackProgressBar.Value = percent;
RollbackProgressText.Text = text;
});
});
}
catch (Exception ex)
{
RollbackProgressText.Text = $"下载失败: {ex.Message}";
LogHelper.WriteLogToFile($"HistoryRollback | 下载异常: {ex.Message}", LogHelper.LogType.Error);
}
if (downloadSuccess)
{
RollbackProgressBar.Value = 100;
RollbackProgressText.Text = "下载完成,准备安装...";
}
else
{
RollbackProgressText.Text = "下载失败,请检查网络后重试。";
RollbackButton.IsEnabled = true;
VersionComboBox.IsEnabled = true;
}
}
private async System.Threading.Tasks.Task<int> AskPauseDaysAsync()
{
var dialog = new ContentDialog
{
Title = "暂停自动更新",
PrimaryButtonText = "确定",
SecondaryButtonText = "取消"
};
var panel = new iNKORE.UI.WPF.Controls.SimpleStackPanel
{
Spacing = 16,
Margin = new Thickness(0, 10, 0, 0)
};
var textBlock = new TextBlock
{
Text = "请选择在回滚后多久不再接收自动更新:",
FontSize = 14
};
var daysComboBox = new ComboBox
{
Width = 200,
Height = 36,
HorizontalAlignment = HorizontalAlignment.Left
};
for (int i = 0; i <= 7; i++)
{
daysComboBox.Items.Add(new ComboBoxItem { Content = $"{i} 天", Tag = i });
}
daysComboBox.SelectedIndex = 0;
panel.Children.Add(textBlock);
panel.Children.Add(daysComboBox);
dialog.Content = panel;
var result = await dialog.ShowAsync();
if (result != ContentDialogResult.Primary) return -1;
if (daysComboBox.SelectedItem is ComboBoxItem cbi && cbi.Tag is int days)
return days;
return 1;
}
#endregion
#region
private async void FixVersionButton_Click(object sender, RoutedEventArgs e) private async void FixVersionButton_Click(object sender, RoutedEventArgs e)
{ {
var confirm = MessageBox.Show( var confirm = MessageBox.Show(
@@ -421,49 +893,39 @@ namespace Ink_Canvas.Windows.SettingsViews.Pages
MessageBoxButton.YesNo, MessageBoxButton.YesNo,
MessageBoxImage.Question); MessageBoxImage.Question);
if (confirm == MessageBoxResult.Yes) if (confirm != MessageBoxResult.Yes) return;
FixVersionButton.IsEnabled = false;
FixVersionButton.Content = "正在修复...";
try
{ {
FixVersionButton.IsEnabled = false; bool result = await AutoUpdateHelper.FixVersion(SettingsManager.Settings.Startup.UpdateChannel);
FixVersionButton.Content = "正在修复..."; if (!result)
try
{ {
bool result = await AutoUpdateHelper.FixVersion(SettingsManager.Settings.Startup.UpdateChannel);
if (!result)
{
MessageBox.Show(
"版本修复失败,可能是网络问题或当前已是最新版本。",
"修复失败",
MessageBoxButton.OK,
MessageBoxImage.Error);
FixVersionButton.IsEnabled = true;
FixVersionButton.Content = "版本修复";
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"Error in FixVersionButton_Click: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show( MessageBox.Show(
$"版本修复过程中发生错误: {ex.Message}", "版本修复失败,可能是网络问题或当前已是最新版本。",
"修复错误", "修复失败",
MessageBoxButton.OK, MessageBoxButton.OK,
MessageBoxImage.Error); MessageBoxImage.Error);
FixVersionButton.IsEnabled = true;
FixVersionButton.Content = "版本修复";
} }
} }
} catch (Exception ex)
{
private void HistoryRollbackButton_Click(object sender, RoutedEventArgs e) LogHelper.WriteLogToFile($"Error in FixVersionButton_Click: {ex.Message}", LogHelper.LogType.Error);
{ MessageBox.Show(
var win = new HistoryRollbackWindow(SettingsManager.Settings.Startup.UpdateChannel); $"版本修复过程中发生错误: {ex.Message}",
win.Owner = Application.Current.MainWindow; "修复错误",
win.ShowDialog(); MessageBoxButton.OK,
MessageBoxImage.Error);
}
finally
{
FixVersionButton.IsEnabled = true;
FixVersionButton.Content = "版本修复";
}
} }
#endregion #endregion
} }
} }