2025-05-25 09:29:48 +08:00
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 ;
2025-06-11 15:09:34 +08:00
using System.Windows.Input ;
2025-06-18 09:08:38 +08:00
using System.Windows.Data ;
using System.Windows.Documents ;
using System.Windows.Media.Animation ;
2025-06-18 18:16:48 +08:00
using System.Reflection ;
2025-07-15 21:10:49 +08:00
using Point = System . Windows . Point ;
2025-05-25 09:29:48 +08:00
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 ;
2025-06-06 16:09:28 +08:00
var workingArea = System . Windows . Forms . Screen . PrimaryScreen . WorkingArea ;
ViewboxFloatingBar . Margin = new Thickness (
( workingArea . Width - 284 ) / 2 ,
workingArea . Bottom - 60 - workingArea . Top ,
- 2000 , - 200 ) ;
2025-05-25 09:29:48 +08:00
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 ( ) ;
2025-06-12 11:21:45 +08:00
// 注册输入事件
inkCanvas . PreviewMouseDown + = inkCanvas_PreviewMouseDown ;
inkCanvas . StylusDown + = inkCanvas_StylusDown ;
inkCanvas . TouchDown + = inkCanvas_TouchDown ;
inkCanvas . TouchUp + = inkCanvas_TouchUp ;
2025-05-25 09:29:48 +08:00
}
#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 ;
2025-06-18 09:08:38 +08:00
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode ( inkCanvas1 ) ;
2025-05-25 09:29:48 +08:00
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 ) ;
2025-07-15 21:10:49 +08:00
// 注册设置面板滚动事件
if ( SettingsPanelScrollViewer ! = null )
{
SettingsPanelScrollViewer . ScrollChanged + = SettingsPanelScrollViewer_ScrollChanged ;
}
2025-05-25 09:29:48 +08:00
// 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 ;
2025-05-31 21:13:02 +08:00
// 自动收纳到侧边栏
if ( Settings . Startup . IsFoldAtStartup )
{
2025-06-29 15:07:13 +08:00
FoldFloatingBar_MouseUp ( new object ( ) , null ) ;
2025-05-31 21:13:02 +08:00
}
2025-06-12 10:13:47 +08:00
// 恢复崩溃后操作设置
if ( App . CrashAction = = App . CrashActionType . SilentRestart )
RadioCrashSilentRestart . IsChecked = true ;
else
RadioCrashNoAction . IsChecked = true ;
2025-07-15 18:09:38 +08:00
// 注册系统关机事件处理
RegisterShutdownHandler ( ) ;
2025-05-25 09:29:48 +08:00
}
private void SystemEventsOnDisplaySettingsChanged ( object sender , EventArgs e ) {
if ( ! Settings . Advanced . IsEnableResolutionChangeDetection ) return ;
2025-07-15 20:30:10 +08:00
ShowNotification ( $"检测到显示器信息变化,变为{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width}x{System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height}) " ) ;
2025-05-25 09:29:48 +08:00
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 ) ;
2025-06-13 09:19:44 +08:00
private void MainWindow_OnSizeChanged ( object sender , SizeChangedEventArgs e ) {
2025-05-25 09:29:48 +08:00
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 ) ;
}
}
2025-06-06 16:09:28 +08:00
2025-05-25 09:29:48 +08:00
private void Window_Closed ( object sender , EventArgs e ) {
SystemEvents . DisplaySettingsChanged - = SystemEventsOnDisplaySettingsChanged ;
LogHelper . WriteLogToFile ( "Ink Canvas closed" , LogHelper . LogType . Event ) ;
2025-06-18 18:16:48 +08:00
// 检查是否有待安装的更新
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 ) ;
}
2025-05-25 09:29:48 +08:00
}
private async void AutoUpdate ( ) {
2025-06-29 11:56:38 +08:00
// 使用当前选择的更新通道检查更新
AvailableLatestVersion = await AutoUpdateHelper . CheckForUpdates ( null , Settings . Startup . UpdateChannel ) ;
// 声明下载状态变量,用于整个方法
bool isDownloadSuccessful = false ;
2025-05-25 09:29:48 +08:00
if ( AvailableLatestVersion ! = null ) {
2025-06-29 11:56:38 +08:00
// 检测到新版本
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( $"AutoUpdate | New version available: {AvailableLatestVersion}" ) ;
2025-06-29 11:56:38 +08:00
// 检查是否是用户选择跳过的版本
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 ( ) ;
}
// 获取当前版本
2025-06-18 18:16:48 +08:00
string currentVersion = Assembly . GetExecutingAssembly ( ) . GetName ( ) . Version . ToString ( ) ;
2025-06-29 11:56:38 +08:00
// 如果启用了静默更新,则自动下载更新而不显示提示
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 ;
}
// 如果没有启用静默更新,则显示常规更新窗口
2025-06-18 18:16:48 +08:00
string releaseDate = DateTime . Now . ToString ( "yyyy年MM月dd日" ) ;
2025-06-29 11:56:38 +08:00
// 从服务器获取更新日志
string releaseNotes = await AutoUpdateHelper . GetUpdateLog ( Settings . Startup . UpdateChannel ) ;
2025-06-18 18:16:48 +08:00
2025-06-29 11:56:38 +08:00
// 如果获取失败,使用默认文本
if ( string . IsNullOrEmpty ( releaseNotes ) )
{
releaseNotes = $@"# InkCanvasForClass v{AvailableLatestVersion}更新
2025-06-29 14:15:20 +08:00
2025-06-29 11:56:38 +08:00
无法获取更新日志,但新版本已准备就绪。" ;
}
2025-06-18 18:16:48 +08:00
// 创建并显示更新窗口
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow ( currentVersion , AvailableLatestVersion , releaseDate , releaseNotes ) ;
bool? dialogResult = updateWindow . ShowDialog ( ) ;
2025-06-29 11:56:38 +08:00
// 如果窗口被关闭但没有点击按钮,则不执行任何操作
2025-06-18 18:16:48 +08:00
if ( dialogResult ! = true ) {
LogHelper . WriteLogToFile ( "AutoUpdate | Update dialog closed without selection" ) ;
return ;
}
2025-06-29 11:56:38 +08:00
// 不再从更新窗口获取自动更新设置
2025-06-18 18:16:48 +08:00
// 根据用户选择处理更新
switch ( updateWindow . Result ) {
case HasNewUpdateWindow . UpdateResult . UpdateNow :
// 立即更新:显示下载进度,下载完成后立即安装
LogHelper . WriteLogToFile ( "AutoUpdate | User chose to update now" ) ;
// 显示下载进度提示
MessageBox . Show ( "开始下载更新,请稍候..." , "正在更新" , MessageBoxButton . OK , MessageBoxImage . Information ) ;
2025-06-29 11:56:38 +08:00
// 下载更新文件,传递当前选择的更新通道
isDownloadSuccessful = await AutoUpdateHelper . DownloadSetupFileAndSaveStatus ( AvailableLatestVersion , "" , Settings . Startup . UpdateChannel ) ;
2025-06-18 18:16:48 +08:00
if ( isDownloadSuccessful ) {
// 下载成功,提示用户准备安装
MessageBoxResult result = MessageBox . Show ( "更新已下载完成,点击确定后将关闭软件并安装新版本!" , "安装更新" , MessageBoxButton . OKCancel , MessageBoxImage . Information ) ;
// 只有当用户点击确定按钮后才关闭软件
if ( result = = MessageBoxResult . OK ) {
// 设置为用户主动退出,避免被看门狗判定为崩溃
App . IsAppExitByUser = true ;
// 准备批处理脚本
2025-06-18 22:40:48 +08:00
AutoUpdateHelper . InstallNewVersionApp ( AvailableLatestVersion , false ) ;
2025-06-18 18:16:48 +08:00
// 关闭软件,让安装程序接管
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" ) ;
// 不管设置如何,都进行下载
2025-06-29 11:56:38 +08:00
isDownloadSuccessful = await AutoUpdateHelper . DownloadSetupFileAndSaveStatus ( AvailableLatestVersion , "" , Settings . Startup . UpdateChannel ) ;
2025-06-18 18:16:48 +08:00
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}" ) ;
2025-06-29 11:56:38 +08:00
// 记录要跳过的版本号
Settings . Startup . SkippedVersion = AvailableLatestVersion ;
// 保存设置到文件
SaveSettingsToFile ( ) ;
// 通知用户
MessageBox . Show ( $"已设置跳过版本 {AvailableLatestVersion},在下次发布新版本之前不会再提示更新。" ,
"已跳过此版本" ,
MessageBoxButton . OK ,
MessageBoxImage . Information ) ;
2025-06-18 18:16:48 +08:00
break ;
2025-05-25 09:29:48 +08:00
}
} else {
AutoUpdateHelper . DeleteUpdatesFolder ( ) ;
}
}
2025-06-12 10:13:47 +08:00
// 新增:崩溃后操作设置按钮事件
private void RadioCrashAction_Checked ( object sender , RoutedEventArgs e )
{
if ( RadioCrashSilentRestart ! = null & & RadioCrashSilentRestart . IsChecked = = true )
{
App . CrashAction = App . CrashActionType . SilentRestart ;
2025-07-06 15:39:37 +08:00
Settings . Startup . CrashAction = 0 ;
2025-06-12 10:13:47 +08:00
}
else if ( RadioCrashNoAction ! = null & & RadioCrashNoAction . IsChecked = = true )
{
App . CrashAction = App . CrashActionType . NoAction ;
2025-07-06 15:39:37 +08:00
Settings . Startup . CrashAction = 1 ;
2025-06-12 10:13:47 +08:00
}
2025-06-12 11:21:45 +08:00
SaveSettingsToFile ( ) ;
2025-07-06 15:39:37 +08:00
// 强制同步全局变量,防止后台逻辑未及时感知
App . SyncCrashActionFromSettings ( ) ;
2025-06-12 11:21:45 +08:00
}
2025-06-18 09:08:38 +08:00
// 添加一个辅助方法,根据当前编辑模式设置光标
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 ;
}
2025-06-19 11:25:15 +08:00
// 确保光标可见,无论是鼠标、触控还是手写笔
2025-06-18 09:08:38 +08:00
System . Windows . Forms . Cursor . Show ( ) ;
2025-06-19 11:25:15 +08:00
// 强制应用光标设置
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 ;
}
}
}
2025-06-18 09:08:38 +08:00
} else {
canvas . UseCustomCursor = false ;
canvas . ForceCursor = false ;
System . Windows . Forms . Cursor . Show ( ) ;
}
}
2025-06-12 11:21:45 +08:00
// 鼠标输入
private void inkCanvas_PreviewMouseDown ( object sender , MouseButtonEventArgs e )
{
2025-06-18 09:08:38 +08:00
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode ( inkCanvas ) ;
2025-06-12 11:21:45 +08:00
}
// 手写笔输入
private void inkCanvas_StylusDown ( object sender , StylusDownEventArgs e )
{
2025-06-18 09:08:38 +08:00
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode ( inkCanvas ) ;
2025-06-12 11:21:45 +08:00
}
2025-06-12 22:48:25 +08:00
// 触摸输入,不隐藏光标
2025-06-12 11:21:45 +08:00
private void inkCanvas_TouchDown ( object sender , TouchEventArgs e )
{
2025-06-18 09:08:38 +08:00
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode ( inkCanvas ) ;
}
// 触摸结束,恢复光标
private void inkCanvas_TouchUp ( object sender , TouchEventArgs e )
{
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode ( inkCanvas ) ;
2025-06-18 15:10:33 +08:00
// 确保光标可见
if ( Settings . Canvas . IsShowCursor ) {
inkCanvas . ForceCursor = true ;
inkCanvas . UseCustomCursor = true ;
System . Windows . Forms . Cursor . Show ( ) ;
}
2025-06-18 09:08:38 +08:00
}
#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 ;
2025-06-19 14:30:24 +08:00
// 设置蒙版为不可点击,并清除背景
2025-06-18 09:08:38 +08:00
BorderSettingsMask . IsHitTestVisible = false ;
2025-06-19 14:30:24 +08:00
BorderSettingsMask . Background = null ; // 确保清除蒙层背景
2025-06-18 09:08:38 +08:00
}
// 新增:折叠侧边栏
private void CollapseNavSidebar_Click ( object sender , RoutedEventArgs e )
{
// 折叠/展开侧边栏
var columnDefinitions = ( ( Grid ) BorderSettings . Child ) . ColumnDefinitions ;
if ( columnDefinitions [ 0 ] . Width . Value = = 50 )
2025-06-17 13:20:06 +08:00
{
2025-06-18 09:08:38 +08:00
// 折叠侧边栏
columnDefinitions [ 0 ] . Width = new GridLength ( 0 ) ;
2025-06-17 13:20:06 +08:00
}
else
{
2025-06-18 09:08:38 +08:00
// 展开侧边栏
columnDefinitions [ 0 ] . Width = new GridLength ( 50 ) ;
2025-06-13 11:43:50 +08:00
}
2025-06-12 11:21:45 +08:00
}
2025-06-18 09:08:38 +08:00
// 新增:显示侧边栏
private void ShowNavSidebar_Click ( object sender , RoutedEventArgs e )
{
// 确保侧边栏展开
var columnDefinitions = ( ( Grid ) BorderSettings . Child ) . ColumnDefinitions ;
columnDefinitions [ 0 ] . Width = new GridLength ( 50 ) ;
}
2025-06-12 11:21:45 +08:00
2025-06-18 09:08:38 +08:00
// 辅助方法:显示指定的设置部分
2025-07-15 21:10:49 +08:00
private async void ShowSettingsSection ( string sectionTag )
2025-06-12 11:21:45 +08:00
{
2025-06-18 09:08:38 +08:00
// 显示设置面板
BorderSettings . Visibility = Visibility . Visible ;
2025-06-19 14:32:16 +08:00
// 设置蒙版为可点击,并添加半透明背景
2025-06-18 09:08:38 +08:00
BorderSettingsMask . IsHitTestVisible = true ;
2025-06-19 14:32:16 +08:00
BorderSettingsMask . Background = new SolidColorBrush ( System . Windows . Media . Color . FromArgb ( 1 , 0 , 0 , 0 ) ) ;
2025-06-18 09:08:38 +08:00
// 获取SettingsPanelScrollViewer中的所有GroupBox
var stackPanel = SettingsPanelScrollViewer . Content as StackPanel ;
if ( stackPanel = = null ) return ;
2025-07-15 21:10:49 +08:00
// 确保所有GroupBox都是可见的
2025-06-18 09:08:38 +08:00
foreach ( var child in stackPanel . Children )
{
if ( child is GroupBox groupBox )
{
2025-07-15 21:10:49 +08:00
groupBox . Visibility = Visibility . Visible ;
2025-06-18 09:08:38 +08:00
}
}
2025-07-15 21:10:49 +08:00
// 确保UI完全更新
await Dispatcher . InvokeAsync ( ( ) = > { } , System . Windows . Threading . DispatcherPriority . Render ) ;
// 根据传入的sectionTag滚动到相应的设置部分
GroupBox targetGroupBox = null ;
2025-06-18 09:08:38 +08:00
switch ( sectionTag . ToLower ( ) )
{
case "startup" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "启动" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "canvas" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "画板和墨迹" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "gesture" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "手势" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "inkrecognition" :
2025-07-15 21:10:49 +08:00
targetGroupBox = GroupBoxInkRecognition ;
2025-06-18 09:08:38 +08:00
break ;
case "crashaction" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "崩溃后操作" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "ppt" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "PPT联动" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "advanced" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "高级设置" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "automation" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "自动化" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "randomwindow" :
2025-07-15 21:10:49 +08:00
targetGroupBox = GroupBoxRandWindow ;
2025-06-18 09:08:38 +08:00
break ;
case "theme" :
2025-07-15 21:10:49 +08:00
targetGroupBox = GroupBoxAppearanceNewUI ;
2025-06-18 09:08:38 +08:00
break ;
case "shortcuts" :
// 快捷键设置部分可能尚未实现
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "快捷键" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "about" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "关于" ) ;
2025-06-18 09:08:38 +08:00
break ;
default :
2025-07-15 21:10:49 +08:00
// 默认滚动到顶部
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 )
2025-06-18 09:08:38 +08:00
{
2025-07-15 21:10:49 +08:00
// 如果出现异常,恢复到原来的滚动位置
SettingsPanelScrollViewer . ScrollToVerticalOffset ( originalOffset ) ;
2025-06-18 09:08:38 +08:00
}
2025-07-15 21:10:49 +08:00
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" ) ;
2025-06-18 09:08:38 +08:00
break ;
2025-07-15 21:10:49 +08:00
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 ;
}
2025-06-18 09:08:38 +08:00
}
2025-07-15 21:10:49 +08:00
}
// 设置导航按钮的Tag属性
private void SetNavButtonTag ( string tag )
{
var grid = BorderSettings . Child as Grid ;
if ( grid = = null ) return ;
2025-06-18 09:08:38 +08:00
2025-07-15 21:10:49 +08:00
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 ;
}
}
}
2025-06-18 09:08:38 +08:00
}
// 根据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 ;
}
}
2025-06-13 11:43:50 +08:00
}
2025-06-12 10:13:47 +08:00
}
2025-06-18 09:08:38 +08:00
#endregion Navigation Sidebar Methods
2025-05-25 09:29:48 +08:00
}
}