using Ink_Canvas.Helpers; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; using System.Windows.Interop; using System.Windows.Media; using System.Threading.Tasks; using System.IO; namespace Ink_Canvas { /// /// HasNewUpdateWindow.xaml 的交互逻辑 /// /// 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; } // 存储更新版本信息 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(); // 设置版本信息 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); // 窗口加载完成后再次确保按钮可见 this.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; DownloadProgressBar.Value = 0; DownloadProgressText.Text = "正在准备下载..."; // 启动下载 bool downloadSuccess = false; try { downloadSuccess = await DownloadUpdateAsync(); } catch (Exception ex) { DownloadProgressText.Text = $"下载失败: {ex.Message}"; LogHelper.WriteLogToFile($"AutoUpdate | 下载异常: {ex.Message}", LogHelper.LogType.Error); } if (downloadSuccess) { DownloadProgressBar.Value = 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 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 (this.Height > maxHeight) { this.Height = maxHeight; needsAdjustment = true; LogHelper.WriteLogToFile($"AutoUpdate | Adjusted window height to: {this.Height}"); } // 如果窗口宽度超过最大允许宽度,调整窗口宽度 if (this.Width > maxWidth) { this.Width = maxWidth; needsAdjustment = true; LogHelper.WriteLogToFile($"AutoUpdate | Adjusted window width to: {this.Width}"); } // 如果屏幕分辨率较低,调整更多UI元素 if (screenHeight < 768 || screenWidth < 1024 || needsAdjustment) { // 查找相关控件并调整大小 var markdownViewer = this.FindName("markdownContent") as MdXaml.MarkdownScrollViewer; var updateNowButton = this.FindName("UpdateNowButton") as Button; var updateLaterButton = this.FindName("UpdateLaterButton") as Button; var skipVersionButton = this.FindName("SkipVersionButton") as Button; // 查找包含ScrollViewer的边框控件,减小其高度 var contentBorders = this.FindVisualChildren().ToList(); foreach (var border in contentBorders) { if (border.Child is ScrollViewer || border.Child is iNKORE.UI.WPF.Modern.Controls.ScrollViewerEx) { // 减小内容显示区域的高度 if (border.Height > 180) { border.Height = 160; LogHelper.WriteLogToFile("AutoUpdate | Reduced content area height"); } else if (border.Child is iNKORE.UI.WPF.Modern.Controls.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 (this.Left < 0) this.Left = 0; if (this.Top < 0) this.Top = 0; if (this.Left + this.Width > screenWidth) this.Left = screenWidth - this.Width; if (this.Top + this.Height > screenHeight) this.Top = screenHeight - this.Height; LogHelper.WriteLogToFile($"AutoUpdate | Final window size: {this.Width}x{this.Height}"); } catch (Exception ex) { LogHelper.WriteLogToFile($"AutoUpdate | Error adjusting window size: {ex.Message}", LogHelper.LogType.Error); } } // 递归查找指定类型的所有子控件 private IEnumerable FindVisualChildren(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(child)) { yield return childOfChild; } } } } // 下载更新并实时更新进度条(支持断点续传) private async Task DownloadUpdateAsync() { string version = NewVersion; string updatesFolderPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "AutoUpdate"); if (!Directory.Exists(updatesFolderPath)) Directory.CreateDirectory(updatesFolderPath); string zipFilePath = Path.Combine(updatesFolderPath, $"InkCanvasForClass.CE.{version}.zip"); string tmpFilePath = zipFilePath + ".tmp"; // 获取下载链接(此处假设主程序已将下载直链传入,或可通过AutoUpdateHelper获取) string downloadUrl = null; if (App.Current.Properties.Contains("UpdateDirectDownloadUrl")) downloadUrl = App.Current.Properties["UpdateDirectDownloadUrl"] as string; if (string.IsNullOrEmpty(downloadUrl)) { // 兜底:用GitHub主线格式 downloadUrl = $"https://github.com/InkCanvasForClass/community/releases/download/{version}/InkCanvasForClass.CE.{version}.zip"; } long existingLength = 0; if (File.Exists(tmpFilePath)) existingLength = new FileInfo(tmpFilePath).Length; try { using (var client = new System.Net.Http.HttpClient()) { client.Timeout = System.TimeSpan.FromMinutes(10); client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater"); if (existingLength > 0) client.DefaultRequestHeaders.Range = new System.Net.Http.Headers.RangeHeaderValue(existingLength, null); using (var response = await client.GetAsync(downloadUrl, System.Net.Http.HttpCompletionOption.ResponseHeadersRead)) { response.EnsureSuccessStatusCode(); var totalBytes = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value + existingLength : -1L; using (var stream = await response.Content.ReadAsStreamAsync()) using (var fs = new FileStream(tmpFilePath, FileMode.Append, FileAccess.Write, FileShare.None)) { byte[] buffer = new byte[8192]; long totalRead = existingLength; int read; var lastUpdate = DateTime.Now; while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0) { await fs.WriteAsync(buffer, 0, read); totalRead += read; if ((DateTime.Now - lastUpdate).TotalMilliseconds > 200) { int percent = totalBytes > 0 ? (int)(totalRead * 100 / totalBytes) : 0; DownloadProgressBar.Value = percent; DownloadProgressText.Text = totalBytes > 0 ? $"已下载 {totalRead / 1024 / 1024.0:F2} MB / {totalBytes / 1024 / 1024.0:F2} MB ({percent}%)" : $"已下载 {totalRead / 1024 / 1024.0:F2} MB"; lastUpdate = DateTime.Now; } } DownloadProgressBar.Value = 100; DownloadProgressText.Text = "下载完成,正在校验..."; await fs.FlushAsync(); } if (File.Exists(zipFilePath)) File.Delete(zipFilePath); File.Move(tmpFilePath, zipFilePath); return true; } } } catch (Exception ex) { LogHelper.WriteLogToFile($"AutoUpdate | 断点续传下载失败: {ex.Message}", LogHelper.LogType.Error); // 不删除 .tmp 文件,便于下次断点续传 return false; } } } }