using Ink_Canvas.Helpers; using iNKORE.UI.WPF.Modern; using System; using System.Collections.ObjectModel; using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Ink; using System.Windows.Media; using System.Diagnostics; using File = System.IO.File; using MessageBox = System.Windows.MessageBox; using System.Runtime.InteropServices; using System.Windows.Interop; using System.Windows.Controls.Primitives; using System.Drawing; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32; using System.Windows.Input; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Media.Animation; using System.Reflection; using Point = System.Windows.Point; namespace Ink_Canvas { public partial class MainWindow : Window { #region Window Initialization public MainWindow() { /* 处于画板模式内:Topmost == false / currentMode != 0 处于 PPT 放映内:BtnPPTSlideShowEnd.Visibility */ InitializeComponent(); BlackboardLeftSide.Visibility = Visibility.Collapsed; BlackboardCenterSide.Visibility = Visibility.Collapsed; BlackboardRightSide.Visibility = Visibility.Collapsed; BorderTools.Visibility = Visibility.Collapsed; BorderSettings.Visibility = Visibility.Collapsed; LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; BorderSettings.Margin = new Thickness(0, 0, 0, 0); TwoFingerGestureBorder.Visibility = Visibility.Collapsed; BoardTwoFingerGestureBorder.Visibility = Visibility.Collapsed; BorderDrawShape.Visibility = Visibility.Collapsed; BoardBorderDrawShape.Visibility = Visibility.Collapsed; GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed; //if (!App.StartArgs.Contains("-o")) ViewBoxStackPanelMain.Visibility = Visibility.Collapsed; ViewBoxStackPanelShapes.Visibility = Visibility.Collapsed; var workingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea; ViewboxFloatingBar.Margin = new Thickness( (workingArea.Width - 284) / 2, workingArea.Bottom - 60 - workingArea.Top, -2000, -200); ViewboxFloatingBarMarginAnimation(100, true); try { if (File.Exists("debug.ini")) Label.Visibility = Visibility.Visible; } catch (Exception ex) { LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error); } try { if (File.Exists("Log.txt")) { var fileInfo = new FileInfo("Log.txt"); var fileSizeInKB = fileInfo.Length / 1024; if (fileSizeInKB > 512) try { File.Delete("Log.txt"); LogHelper.WriteLogToFile( "The Log.txt file has been successfully deleted. Original file size: " + fileSizeInKB + " KB", LogHelper.LogType.Info); } catch (Exception ex) { LogHelper.WriteLogToFile( ex + " | Can not delete the Log.txt file. File size: " + fileSizeInKB + " KB", LogHelper.LogType.Error); } } } catch (Exception ex) { LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error); } InitTimers(); timeMachine.OnRedoStateChanged += TimeMachine_OnRedoStateChanged; timeMachine.OnUndoStateChanged += TimeMachine_OnUndoStateChanged; inkCanvas.Strokes.StrokesChanged += StrokesOnStrokesChanged; Microsoft.Win32.SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged; try { if (File.Exists("SpecialVersion.ini")) SpecialVersionResetToSuggestion_Click(); } catch (Exception ex) { LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error); } CheckColorTheme(true); CheckPenTypeUIState(); // 注册输入事件 inkCanvas.PreviewMouseDown += inkCanvas_PreviewMouseDown; inkCanvas.StylusDown += inkCanvas_StylusDown; inkCanvas.TouchDown += inkCanvas_TouchDown; inkCanvas.TouchUp += inkCanvas_TouchUp; } #endregion #region Ink Canvas Functions private System.Windows.Media.Color Ink_DefaultColor = Colors.Red; private DrawingAttributes drawingAttributes; private void loadPenCanvas() { try { //drawingAttributes = new DrawingAttributes(); drawingAttributes = inkCanvas.DefaultDrawingAttributes; drawingAttributes.Color = Ink_DefaultColor; drawingAttributes.Height = 2.5; drawingAttributes.Width = 2.5; drawingAttributes.IsHighlighter = false; drawingAttributes.FitToCurve = Settings.Canvas.FitToCurve; inkCanvas.EditingMode = InkCanvasEditingMode.Ink; inkCanvas.Gesture += InkCanvas_Gesture; } catch { } } //ApplicationGesture lastApplicationGesture = ApplicationGesture.AllGestures; private DateTime lastGestureTime = DateTime.Now; private void InkCanvas_Gesture(object sender, InkCanvasGestureEventArgs e) { var gestures = e.GetGestureRecognitionResults(); try { foreach (var gest in gestures) //Trace.WriteLine(string.Format("Gesture: {0}, Confidence: {1}", gest.ApplicationGesture, gest.RecognitionConfidence)); if (StackPanelPPTControls.Visibility == Visibility.Visible) { if (gest.ApplicationGesture == ApplicationGesture.Left) BtnPPTSlidesDown_Click(BtnPPTSlidesDown, null); if (gest.ApplicationGesture == ApplicationGesture.Right) BtnPPTSlidesUp_Click(BtnPPTSlidesUp, null); } } catch { } } private void inkCanvas_EditingModeChanged(object sender, RoutedEventArgs e) { var inkCanvas1 = sender as InkCanvas; if (inkCanvas1 == null) return; // 使用辅助方法设置光标 SetCursorBasedOnEditingMode(inkCanvas1); if (inkCanvas1.EditingMode == InkCanvasEditingMode.Ink) forcePointEraser = !forcePointEraser; } #endregion Ink Canvas #region Definations and Loading public static Settings Settings = new Settings(); public static string settingsFileName = "Settings.json"; private bool isLoaded = false; private void Window_Loaded(object sender, RoutedEventArgs e) { loadPenCanvas(); //加载设置 LoadSettings(true); // 注册设置面板滚动事件 if (SettingsPanelScrollViewer != null) { SettingsPanelScrollViewer.ScrollChanged += SettingsPanelScrollViewer_ScrollChanged; } // HasNewUpdateWindow hasNewUpdateWindow = new HasNewUpdateWindow(); if (Environment.Is64BitProcess) GroupBoxInkRecognition.Visibility = Visibility.Collapsed; ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light; SystemEvents_UserPreferenceChanged(null, null); //TextBlockVersion.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString(); LogHelper.WriteLogToFile("Ink Canvas Loaded", LogHelper.LogType.Event); isLoaded = true; BlackBoardLeftSidePageListView.ItemsSource = blackBoardSidePageListViewObservableCollection; BlackBoardRightSidePageListView.ItemsSource = blackBoardSidePageListViewObservableCollection; BtnLeftWhiteBoardSwitchPreviousGeometry.Brush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(127, 24, 24, 27)); BtnLeftWhiteBoardSwitchPreviousLabel.Opacity = 0.5; BtnRightWhiteBoardSwitchPreviousGeometry.Brush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(127, 24, 24, 27)); BtnRightWhiteBoardSwitchPreviousLabel.Opacity = 0.5; BtnWhiteBoardSwitchPrevious.IsEnabled = CurrentWhiteboardIndex != 1; BorderInkReplayToolBox.Visibility = Visibility.Collapsed; // 提前加载IA库,优化第一笔等待时间 if (Settings.InkToShape.IsInkToShapeEnabled && !Environment.Is64BitProcess) { var strokeEmpty = new StrokeCollection(); InkRecognizeHelper.RecognizeShape(strokeEmpty); } SystemEvents.DisplaySettingsChanged += SystemEventsOnDisplaySettingsChanged; // 自动收纳到侧边栏 if (Settings.Startup.IsFoldAtStartup) { FoldFloatingBar_MouseUp(new object(), null); } // 恢复崩溃后操作设置 if (App.CrashAction == App.CrashActionType.SilentRestart) RadioCrashSilentRestart.IsChecked = true; else RadioCrashNoAction.IsChecked = true; // 注册系统关机事件处理 RegisterShutdownHandler(); } private void SystemEventsOnDisplaySettingsChanged(object sender, EventArgs e) { if (!Settings.Advanced.IsEnableResolutionChangeDetection) return; ShowNotification($"检测到显示器信息变化,变为{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width}x{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height})"); new Thread(() => { var isFloatingBarOutsideScreen = false; var isInPPTPresentationMode = false; Dispatcher.Invoke(() => { isFloatingBarOutsideScreen = IsOutsideOfScreenHelper.IsOutsideOfScreen(ViewboxFloatingBar); isInPPTPresentationMode = BtnPPTSlideShowEnd.Visibility == Visibility.Visible; }); if (isFloatingBarOutsideScreen) dpiChangedDelayAction.DebounceAction(3000, null, () => { if (!isFloatingBarFolded) { if (isInPPTPresentationMode) ViewboxFloatingBarMarginAnimation(60); else ViewboxFloatingBarMarginAnimation(100, true); } }); }).Start(); } public DelayAction dpiChangedDelayAction = new DelayAction(); private void MainWindow_OnDpiChanged(object sender, DpiChangedEventArgs e) { if (e.OldDpi.DpiScaleX != e.NewDpi.DpiScaleX && e.OldDpi.DpiScaleY != e.NewDpi.DpiScaleY && Settings.Advanced.IsEnableDPIChangeDetection) { ShowNotification($"系统DPI发生变化,从 {e.OldDpi.DpiScaleX}x{e.OldDpi.DpiScaleY} 变化为 {e.NewDpi.DpiScaleX}x{e.NewDpi.DpiScaleY}"); new Thread(() => { var isFloatingBarOutsideScreen = false; var isInPPTPresentationMode = false; Dispatcher.Invoke(() => { isFloatingBarOutsideScreen = IsOutsideOfScreenHelper.IsOutsideOfScreen(ViewboxFloatingBar); isInPPTPresentationMode = BtnPPTSlideShowEnd.Visibility == Visibility.Visible; }); if (isFloatingBarOutsideScreen) dpiChangedDelayAction.DebounceAction(3000,null, () => { if (!isFloatingBarFolded) { if (isInPPTPresentationMode) ViewboxFloatingBarMarginAnimation(60); else ViewboxFloatingBarMarginAnimation(100, true); } }); }).Start(); } } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { LogHelper.WriteLogToFile("Ink Canvas closing", LogHelper.LogType.Event); if (!CloseIsFromButton && Settings.Advanced.IsSecondConfirmWhenShutdownApp) { e.Cancel = true; if (MessageBox.Show("是否继续关闭 InkCanvasForClass,这将丢失当前未保存的墨迹。", "InkCanvasForClass", MessageBoxButton.OKCancel, MessageBoxImage.Warning) == MessageBoxResult.OK) if (MessageBox.Show("真的狠心关闭 InkCanvasForClass吗?", "InkCanvasForClass", MessageBoxButton.OKCancel, MessageBoxImage.Error) == MessageBoxResult.OK) if (MessageBox.Show("是否取消关闭 InkCanvasForClass?", "InkCanvasForClass", MessageBoxButton.OKCancel, MessageBoxImage.Error) != MessageBoxResult.OK) e.Cancel = false; } if (e.Cancel) LogHelper.WriteLogToFile("Ink Canvas closing cancelled", LogHelper.LogType.Event); } [DllImport("user32.dll", SetLastError = true)] public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); private void MainWindow_OnSizeChanged(object sender, SizeChangedEventArgs e) { if (Settings.Advanced.IsEnableForceFullScreen) { if (isLoaded) ShowNotification( $"检测到窗口大小变化,已自动恢复到全屏:{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width}x{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height}(缩放比例为{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth}x{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height / SystemParameters.PrimaryScreenHeight})"); WindowState = WindowState.Maximized; MoveWindow(new WindowInteropHelper(this).Handle, 0, 0, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height, true); } } private void Window_Closed(object sender, EventArgs e) { SystemEvents.DisplaySettingsChanged -= SystemEventsOnDisplaySettingsChanged; LogHelper.WriteLogToFile("Ink Canvas closed", LogHelper.LogType.Event); // 检查是否有待安装的更新 CheckPendingUpdates(); } private void CheckPendingUpdates() { try { // 如果有可用的更新版本且启用了自动更新 if (AvailableLatestVersion != null && Settings.Startup.IsAutoUpdate) { // 检查更新文件是否已下载 string updatesFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoUpdate"); string statusFilePath = Path.Combine(updatesFolderPath, $"DownloadV{AvailableLatestVersion}Status.txt"); if (File.Exists(statusFilePath) && File.ReadAllText(statusFilePath).Trim().ToLower() == "true") { LogHelper.WriteLogToFile($"AutoUpdate | Installing pending update v{AvailableLatestVersion} on application close"); // 设置为用户主动退出,避免被看门狗判定为崩溃 App.IsAppExitByUser = true; // 创建批处理脚本并启动,软件关闭后会执行更新操作 AutoUpdateHelper.InstallNewVersionApp(AvailableLatestVersion, true); } } } catch (Exception ex) { LogHelper.WriteLogToFile($"AutoUpdate | Error checking pending updates: {ex.Message}", LogHelper.LogType.Error); } } private async void AutoUpdate() { // 使用当前选择的更新通道检查更新 AvailableLatestVersion = await AutoUpdateHelper.CheckForUpdates(null, Settings.Startup.UpdateChannel); // 声明下载状态变量,用于整个方法 bool isDownloadSuccessful = false; if (AvailableLatestVersion != null) { // 检测到新版本 LogHelper.WriteLogToFile($"AutoUpdate | New version available: {AvailableLatestVersion}"); // 检查是否是用户选择跳过的版本 if (!string.IsNullOrEmpty(Settings.Startup.SkippedVersion) && Settings.Startup.SkippedVersion == AvailableLatestVersion) { LogHelper.WriteLogToFile($"AutoUpdate | Version {AvailableLatestVersion} was marked to be skipped by the user"); return; // 跳过此版本,不执行更新操作 } // 如果检测到的版本与跳过的版本不同,则清除跳过版本记录 // 这确保用户只能跳过当前最新版本,而不是永久跳过所有更新 if (!string.IsNullOrEmpty(Settings.Startup.SkippedVersion) && Settings.Startup.SkippedVersion != AvailableLatestVersion) { LogHelper.WriteLogToFile($"AutoUpdate | Detected new version {AvailableLatestVersion} different from skipped version {Settings.Startup.SkippedVersion}, clearing skip record"); Settings.Startup.SkippedVersion = ""; SaveSettingsToFile(); } // 获取当前版本 string currentVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); // 如果启用了静默更新,则自动下载更新而不显示提示 if (Settings.Startup.IsAutoUpdateWithSilence) { LogHelper.WriteLogToFile("AutoUpdate | Silent update enabled, downloading update automatically without notification"); // 静默下载更新,传递当前选择的更新通道 isDownloadSuccessful = await AutoUpdateHelper.DownloadSetupFileAndSaveStatus(AvailableLatestVersion, "", Settings.Startup.UpdateChannel); if (isDownloadSuccessful) { LogHelper.WriteLogToFile("AutoUpdate | Update downloaded successfully, will install when conditions are met"); // 启动检查定时器,定期检查是否可以安装 timerCheckAutoUpdateWithSilence.Start(); } else { LogHelper.WriteLogToFile("AutoUpdate | Silent update download failed", LogHelper.LogType.Error); } return; } // 如果没有启用静默更新,则显示常规更新窗口 string releaseDate = DateTime.Now.ToString("yyyy年MM月dd日"); // 从服务器获取更新日志 string releaseNotes = await AutoUpdateHelper.GetUpdateLog(Settings.Startup.UpdateChannel); // 如果获取失败,使用默认文本 if (string.IsNullOrEmpty(releaseNotes)) { releaseNotes = $@"# InkCanvasForClass v{AvailableLatestVersion}更新 无法获取更新日志,但新版本已准备就绪。"; } // 创建并显示更新窗口 HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, AvailableLatestVersion, releaseDate, releaseNotes); 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 AutoUpdateHelper.DownloadSetupFileAndSaveStatus(AvailableLatestVersion, "", Settings.Startup.UpdateChannel); if (isDownloadSuccessful) { // 下载成功,提示用户准备安装 MessageBoxResult result = MessageBox.Show("更新已下载完成,点击确定后将关闭软件并安装新版本!", "安装更新", MessageBoxButton.OKCancel, MessageBoxImage.Information); // 只有当用户点击确定按钮后才关闭软件 if (result == MessageBoxResult.OK) { // 设置为用户主动退出,避免被看门狗判定为崩溃 App.IsAppExitByUser = true; // 准备批处理脚本 AutoUpdateHelper.InstallNewVersionApp(AvailableLatestVersion, false); // 关闭软件,让安装程序接管 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 AutoUpdateHelper.DownloadSetupFileAndSaveStatus(AvailableLatestVersion, "", 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 { AutoUpdateHelper.DeleteUpdatesFolder(); } } // 新增:崩溃后操作设置按钮事件 private void RadioCrashAction_Checked(object sender, RoutedEventArgs e) { if (RadioCrashSilentRestart != null && RadioCrashSilentRestart.IsChecked == true) { App.CrashAction = App.CrashActionType.SilentRestart; Settings.Startup.CrashAction = 0; } else if (RadioCrashNoAction != null && RadioCrashNoAction.IsChecked == true) { App.CrashAction = App.CrashActionType.NoAction; Settings.Startup.CrashAction = 1; } SaveSettingsToFile(); // 强制同步全局变量,防止后台逻辑未及时感知 App.SyncCrashActionFromSettings(); } // 添加一个辅助方法,根据当前编辑模式设置光标 private void SetCursorBasedOnEditingMode(InkCanvas canvas) { if (Settings.Canvas.IsShowCursor) { canvas.UseCustomCursor = true; canvas.ForceCursor = true; // 根据编辑模式设置不同的光标 if (canvas.EditingMode == InkCanvasEditingMode.EraseByPoint) { canvas.Cursor = Cursors.Cross; } else if (canvas.EditingMode == InkCanvasEditingMode.Ink) { var sri = Application.GetResourceStream(new Uri("Resources/Cursors/Pen.cur", UriKind.Relative)); if (sri != null) canvas.Cursor = new Cursor(sri.Stream); } else if (canvas.EditingMode == InkCanvasEditingMode.Select) { canvas.Cursor = Cursors.Cross; } // 确保光标可见,无论是鼠标、触控还是手写笔 System.Windows.Forms.Cursor.Show(); // 强制应用光标设置 canvas.ForceCursor = true; // 确保手写笔模式下也能显示光标 if (Tablet.TabletDevices.Count > 0) { foreach (TabletDevice device in Tablet.TabletDevices) { if (device.Type == TabletDeviceType.Stylus) { // 手写笔设备存在,强制显示光标 System.Windows.Forms.Cursor.Show(); break; } } } } else { canvas.UseCustomCursor = false; canvas.ForceCursor = false; System.Windows.Forms.Cursor.Show(); } } // 鼠标输入 private void inkCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e) { // 使用辅助方法设置光标 SetCursorBasedOnEditingMode(inkCanvas); } // 手写笔输入 private void inkCanvas_StylusDown(object sender, StylusDownEventArgs e) { // 使用辅助方法设置光标 SetCursorBasedOnEditingMode(inkCanvas); } // 触摸输入,不隐藏光标 private void inkCanvas_TouchDown(object sender, TouchEventArgs e) { // 使用辅助方法设置光标 SetCursorBasedOnEditingMode(inkCanvas); } // 触摸结束,恢复光标 private void inkCanvas_TouchUp(object sender, TouchEventArgs e) { // 使用辅助方法设置光标 SetCursorBasedOnEditingMode(inkCanvas); // 确保光标可见 if (Settings.Canvas.IsShowCursor) { inkCanvas.ForceCursor = true; inkCanvas.UseCustomCursor = true; System.Windows.Forms.Cursor.Show(); } } #endregion Definations and Loading #region Navigation Sidebar Methods // 侧边栏导航按钮事件处理 private void NavStartup_Click(object sender, RoutedEventArgs e) { // 切换到启动设置页面 ShowSettingsSection("startup"); } private void NavCanvas_Click(object sender, RoutedEventArgs e) { // 切换到画布设置页面 ShowSettingsSection("canvas"); } private void NavGesture_Click(object sender, RoutedEventArgs e) { // 切换到手势设置页面 ShowSettingsSection("gesture"); } private void NavInkRecognition_Click(object sender, RoutedEventArgs e) { // 切换到墨迹识别设置页面 ShowSettingsSection("inkrecognition"); } private void NavCrashAction_Click(object sender, RoutedEventArgs e) { // 切换到崩溃处理设置页面 ShowSettingsSection("crashaction"); } private void NavPPT_Click(object sender, RoutedEventArgs e) { // 切换到PPT设置页面 ShowSettingsSection("ppt"); } private void NavAdvanced_Click(object sender, RoutedEventArgs e) { // 切换到高级设置页面 ShowSettingsSection("advanced"); } private void NavAutomation_Click(object sender, RoutedEventArgs e) { // 切换到自动化设置页面 ShowSettingsSection("automation"); } private void NavRandomWindow_Click(object sender, RoutedEventArgs e) { // 切换到随机窗口设置页面 ShowSettingsSection("randomwindow"); } private void NavAbout_Click(object sender, RoutedEventArgs e) { // 切换到关于页面 ShowSettingsSection("about"); } // 新增:个性化设置 private void NavTheme_Click(object sender, RoutedEventArgs e) { // 切换到个性化设置页面 ShowSettingsSection("theme"); } // 新增:快捷键设置 private void NavShortcuts_Click(object sender, RoutedEventArgs e) { // 切换到快捷键设置页面 ShowSettingsSection("shortcuts"); // 如果设置部分尚未快捷键 MessageBox.Show("设置功能正在开发中", "提示", MessageBoxButton.OK, MessageBoxImage.Information); } private void BtnCloseSettings_Click(object sender, RoutedEventArgs e) { // 关闭设置面板 BorderSettings.Visibility = Visibility.Collapsed; // 设置蒙版为不可点击,并清除背景 BorderSettingsMask.IsHitTestVisible = false; BorderSettingsMask.Background = null; // 确保清除蒙层背景 } // 新增:折叠侧边栏 private void CollapseNavSidebar_Click(object sender, RoutedEventArgs e) { // 折叠/展开侧边栏 var columnDefinitions = ((Grid)BorderSettings.Child).ColumnDefinitions; if (columnDefinitions[0].Width.Value == 50) { // 折叠侧边栏 columnDefinitions[0].Width = new GridLength(0); } else { // 展开侧边栏 columnDefinitions[0].Width = new GridLength(50); } } // 新增:显示侧边栏 private void ShowNavSidebar_Click(object sender, RoutedEventArgs e) { // 确保侧边栏展开 var columnDefinitions = ((Grid)BorderSettings.Child).ColumnDefinitions; columnDefinitions[0].Width = new GridLength(50); } // 辅助方法:显示指定的设置部分 private async void ShowSettingsSection(string sectionTag) { // 显示设置面板 BorderSettings.Visibility = Visibility.Visible; // 设置蒙版为可点击,并添加半透明背景 BorderSettingsMask.IsHitTestVisible = true; BorderSettingsMask.Background = new SolidColorBrush(System.Windows.Media.Color.FromArgb(1, 0, 0, 0)); // 获取SettingsPanelScrollViewer中的所有GroupBox var stackPanel = SettingsPanelScrollViewer.Content as StackPanel; if (stackPanel == null) return; // 确保所有GroupBox都是可见的 foreach (var child in stackPanel.Children) { if (child is GroupBox groupBox) { groupBox.Visibility = Visibility.Visible; } } // 确保UI完全更新 await Dispatcher.InvokeAsync(() => {}, System.Windows.Threading.DispatcherPriority.Render); // 根据传入的sectionTag滚动到相应的设置部分 GroupBox targetGroupBox = null; switch (sectionTag.ToLower()) { case "startup": targetGroupBox = FindGroupBoxByHeader(stackPanel, "启动"); break; case "canvas": targetGroupBox = FindGroupBoxByHeader(stackPanel, "画板和墨迹"); break; case "gesture": targetGroupBox = FindGroupBoxByHeader(stackPanel, "手势"); break; case "inkrecognition": targetGroupBox = GroupBoxInkRecognition; break; case "crashaction": targetGroupBox = FindGroupBoxByHeader(stackPanel, "崩溃后操作"); break; case "ppt": targetGroupBox = FindGroupBoxByHeader(stackPanel, "PPT联动"); break; case "advanced": targetGroupBox = FindGroupBoxByHeader(stackPanel, "高级设置"); break; case "automation": targetGroupBox = FindGroupBoxByHeader(stackPanel, "自动化"); break; case "randomwindow": targetGroupBox = GroupBoxRandWindow; break; case "theme": targetGroupBox = GroupBoxAppearanceNewUI; break; case "shortcuts": // 快捷键设置部分可能尚未实现 targetGroupBox = FindGroupBoxByHeader(stackPanel, "快捷键"); break; case "about": targetGroupBox = FindGroupBoxByHeader(stackPanel, "关于"); break; default: // 默认滚动到顶部 SettingsPanelScrollViewer.ScrollToTop(); return; } // 如果找到目标GroupBox,则滚动到它的位置 if (targetGroupBox != null) { // 使用动画平滑滚动到目标位置 ScrollToElement(targetGroupBox); // 高亮显示当前选中的导航项 UpdateNavigationButtonState(sectionTag); } else { // 如果没有找到目标GroupBox,则滚动到顶部 SettingsPanelScrollViewer.ScrollToTop(); } } // 根据Header文本查找GroupBox private GroupBox FindGroupBoxByHeader(StackPanel parent, string headerText) { foreach (var child in parent.Children) { if (child is GroupBox groupBox) { // 查找GroupBox的Header if (groupBox.Header is TextBlock headerTextBlock && headerTextBlock.Text != null && headerTextBlock.Text.Contains(headerText)) { return groupBox; } } } return null; } // 平滑滚动到指定元素 private async void ScrollToElement(FrameworkElement element) { if (element == null || SettingsPanelScrollViewer == null) return; try { // 暂时禁用滚动事件处理 SettingsPanelScrollViewer.ScrollChanged -= SettingsPanelScrollViewer_ScrollChanged; // 记录当前滚动位置 double originalOffset = SettingsPanelScrollViewer.VerticalOffset; // 将ScrollViewer内部的位置信息重置到顶部(不会触发视觉更新) SettingsPanelScrollViewer.ScrollToHome(); // 使用Dispatcher进行延迟处理,确保布局更新 await Dispatcher.InvokeAsync(() => { try { // 强制更新布局 SettingsPanelScrollViewer.UpdateLayout(); // 获取元素相对于顶部的准确位置 Point elementPosition = element.TransformToAncestor(SettingsPanelScrollViewer).Transform(new Point(0, 0)); // 计算目标位置,减去一些偏移,使元素不会贴在顶部 double targetPosition = elementPosition.Y - 20; // 确保目标位置不小于0 targetPosition = Math.Max(0, targetPosition); // 直接设置滚动位置,不使用动画 SettingsPanelScrollViewer.ScrollToVerticalOffset(targetPosition); } catch (Exception ex) { // 如果出现异常,恢复到原来的滚动位置 SettingsPanelScrollViewer.ScrollToVerticalOffset(originalOffset); } finally { // 重新启用滚动事件处理 SettingsPanelScrollViewer.ScrollChanged += SettingsPanelScrollViewer_ScrollChanged; } }, System.Windows.Threading.DispatcherPriority.Render); } catch (Exception) { // 确保在异常情况下也重新启用滚动事件处理 SettingsPanelScrollViewer.ScrollChanged += SettingsPanelScrollViewer_ScrollChanged; } } // 滚动条变化事件处理 private void SettingsPanelScrollViewer_ScrollChanged(object sender, System.Windows.Controls.ScrollChangedEventArgs e) { // 可以在这里添加滚动事件的处理逻辑,如果需要的话 } // 更新导航按钮状态 private void UpdateNavigationButtonState(string activeTag) { // 清除所有导航按钮的Tag属性 ClearAllNavButtonTags(); // 设置当前活动按钮的Tag属性 switch (activeTag.ToLower()) { case "startup": SetNavButtonTag("startup"); break; case "canvas": SetNavButtonTag("canvas"); break; case "gesture": SetNavButtonTag("gesture"); break; case "inkrecognition": SetNavButtonTag("inkrecognition"); break; case "crashaction": SetNavButtonTag("crashaction"); break; case "ppt": SetNavButtonTag("ppt"); break; case "advanced": SetNavButtonTag("advanced"); break; case "automation": SetNavButtonTag("automation"); break; case "randomwindow": SetNavButtonTag("randomwindow"); break; case "theme": SetNavButtonTag("theme"); break; case "shortcuts": SetNavButtonTag("shortcuts"); break; case "about": SetNavButtonTag("about"); break; } } // 清除所有导航按钮的Tag属性 private void ClearAllNavButtonTags() { var grid = BorderSettings.Child as Grid; if (grid == null) return; var navSidebar = grid.Children[0] as Border; if (navSidebar == null) return; var navGrid = navSidebar.Child as Grid; if (navGrid == null) return; var scrollViewer = navGrid.Children[1] as ScrollViewer; if (scrollViewer == null) return; var stackPanel = scrollViewer.Content as StackPanel; if (stackPanel == null) return; foreach (var child in stackPanel.Children) { if (child is Button button) { button.Tag = null; } } } // 设置导航按钮的Tag属性 private void SetNavButtonTag(string tag) { var grid = BorderSettings.Child as Grid; if (grid == null) return; var navSidebar = grid.Children[0] as Border; if (navSidebar == null) return; var navGrid = navSidebar.Child as Grid; if (navGrid == null) return; var scrollViewer = navGrid.Children[1] as ScrollViewer; if (scrollViewer == null) return; var stackPanel = scrollViewer.Content as StackPanel; if (stackPanel == null) return; foreach (var child in stackPanel.Children) { if (child is Button button) { // 检查按钮的ToolTip属性,根据tag设置对应的按钮 string buttonTag = button.Tag as string; // 如果按钮的Tag与要设置的tag匹配,则设置Tag if (buttonTag != null && buttonTag.ToLower() == tag.ToLower()) { button.Tag = tag; return; } } } } // 根据Header文本查找并显示GroupBox private void ShowGroupBoxByHeader(StackPanel parent, string headerText) { foreach (var child in parent.Children) { if (child is GroupBox groupBox) { // 查找GroupBox的Header if (groupBox.Header is TextBlock headerTextBlock && headerTextBlock.Text != null && headerTextBlock.Text.Contains(headerText)) { groupBox.Visibility = Visibility.Visible; return; } } } } #endregion Navigation Sidebar Methods } }