2025-08-31 11:43:52 +08:00
using Ink_Canvas.Helpers ;
using Ink_Canvas.Helpers.Plugins ;
using Ink_Canvas.Windows ;
using iNKORE.UI.WPF.Modern ;
using iNKORE.UI.WPF.Modern.Controls ;
using Microsoft.Win32 ;
2025-05-25 09:29:48 +08:00
using System ;
2025-07-28 14:40:44 +08:00
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Diagnostics ;
2025-05-25 09:29:48 +08:00
using System.IO ;
2025-08-24 11:08:13 +08:00
using System.Linq ;
2025-07-28 14:40:44 +08:00
using System.Reflection ;
using System.Runtime.InteropServices ;
2025-10-18 18:26:13 +08:00
using System.Security.Principal ;
2025-07-28 14:40:44 +08:00
using System.Threading ;
using System.Threading.Tasks ;
2025-05-25 09:29:48 +08:00
using System.Windows ;
using System.Windows.Controls ;
2025-09-06 13:16:10 +08:00
using System.Windows.Controls.Primitives ;
2025-07-28 14:40:44 +08:00
using System.Windows.Forms ;
2025-05-25 09:29:48 +08:00
using System.Windows.Ink ;
2025-07-28 14:40:44 +08:00
using System.Windows.Input ;
using System.Windows.Interop ;
2025-05-25 09:29:48 +08:00
using System.Windows.Media ;
2025-07-28 14:40:44 +08:00
using System.Windows.Threading ;
using Application = System . Windows . Application ;
2025-07-16 09:16:36 +08:00
using Brushes = System . Windows . Media . Brushes ;
2025-07-28 14:40:44 +08:00
using Button = System . Windows . Controls . Button ;
using Cursor = System . Windows . Input . Cursor ;
using Cursors = System . Windows . Input . Cursors ;
using DpiChangedEventArgs = System . Windows . DpiChangedEventArgs ;
2025-08-03 16:46:33 +08:00
using File = System . IO . File ;
2025-07-28 14:40:44 +08:00
using GroupBox = System . Windows . Controls . GroupBox ;
2025-12-20 13:56:46 +08:00
using HorizontalAlignment = System . Windows . HorizontalAlignment ;
2025-09-13 11:04:04 +08:00
using MessageBox = iNKORE . UI . WPF . Modern . Controls . MessageBox ;
2025-07-15 21:10:49 +08:00
using Point = System . Windows . Point ;
2025-12-20 13:56:46 +08:00
using VerticalAlignment = System . Windows . VerticalAlignment ;
2025-05-25 09:29:48 +08:00
2025-08-03 16:46:33 +08:00
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
2025-10-18 18:26:13 +08:00
[DllImport("UIAccessDLL_x86.dll", EntryPoint = "PrepareUIAccess", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 PrepareUIAccessX86 ( ) ;
[DllImport("UIAccessDLL_x64.dll", EntryPoint = "PrepareUIAccess", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 PrepareUIAccessX64 ( ) ;
2025-09-13 12:08:45 +08:00
// 每一页一个Canvas对象
2025-07-18 19:20:06 +08:00
private List < System . Windows . Controls . Canvas > whiteboardPages = new List < System . Windows . Controls . Canvas > ( ) ;
2025-07-28 14:40:44 +08:00
private int currentPageIndex ;
private System . Windows . Controls . Canvas currentCanvas ;
private AutoUpdateHelper . UpdateLineGroup AvailableLatestLineGroup ;
2025-08-31 11:43:52 +08:00
2025-08-23 21:39:00 +08:00
// 全局快捷键管理器
private GlobalHotkeyManager _globalHotkeyManager ;
2025-07-18 19:20:06 +08:00
2025-08-23 23:13:39 +08:00
// 墨迹渐隐管理器
private InkFadeManager _inkFadeManager ;
2025-09-13 15:34:50 +08:00
// 悬浮窗拦截管理器
private FloatingWindowInterceptorManager _floatingWindowInterceptorManager ;
2025-10-26 00:21:10 +08:00
2025-09-13 10:55:27 +08:00
// 设置面板相关状态
private bool userChangedNoFocusModeInSettings ;
2025-10-18 18:29:52 +08:00
private bool isTemporarilyDisablingNoFocusMode = false ;
2025-12-20 13:56:46 +08:00
2025-12-20 13:03:31 +08:00
// 全屏处理状态标志
public bool isFullScreenApplied = false ;
2025-09-13 10:55:27 +08:00
2025-07-25 18:12:46 +08:00
2025-07-20 21:01:15 +08:00
2025-05-25 09:29:48 +08:00
#region Window Initialization
2025-08-03 16:46:33 +08:00
public MainWindow ( )
{
2025-05-25 09:29:48 +08:00
/*
处于画板模式内: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-07-28 14:40:44 +08:00
var workingArea = Screen . PrimaryScreen . WorkingArea ;
2025-08-12 11:08:24 +08:00
// 考虑快捷调色盘的宽度,确保浮动栏有足够空间
double floatingBarWidth = 284 ; // 基础宽度
if ( Settings . Appearance . IsShowQuickColorPalette )
{
2025-08-12 12:19:55 +08:00
// 根据显示模式调整宽度
if ( Settings . Appearance . QuickColorPaletteDisplayMode = = 0 )
{
// 单行显示模式,自适应宽度,但需要足够空间显示6个颜色
floatingBarWidth = Math . Max ( floatingBarWidth , 120 ) ;
}
else
{
// 双行显示模式,宽度较大
floatingBarWidth = Math . Max ( floatingBarWidth , 820 ) ;
}
2025-08-12 11:08:24 +08:00
}
2025-06-06 16:09:28 +08:00
ViewboxFloatingBar . Margin = new Thickness (
2025-08-12 11:08:24 +08:00
( workingArea . Width - floatingBarWidth ) / 2 ,
2025-06-06 16:09:28 +08:00
workingArea . Bottom - 60 - workingArea . Top ,
- 2000 , - 200 ) ;
2025-08-31 09:59:49 +08:00
// 新增:只在屏幕模式下初始化浮动栏动画
if ( currentMode = = 0 )
{
ViewboxFloatingBarMarginAnimation ( 100 , true ) ;
}
2025-05-25 09:29:48 +08:00
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
if ( File . Exists ( "debug.ini" ) ) Label . Visibility = Visibility . Visible ;
}
2025-08-03 16:46:33 +08:00
catch ( Exception ex )
{
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile ( ex . ToString ( ) , LogHelper . LogType . Error ) ;
}
2025-08-03 16:46:33 +08:00
try
{
if ( File . Exists ( "Log.txt" ) )
{
2025-05-25 09:29:48 +08:00
var fileInfo = new FileInfo ( "Log.txt" ) ;
var fileSizeInKB = fileInfo . Length / 1024 ;
if ( fileSizeInKB > 512 )
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
File . Delete ( "Log.txt" ) ;
LogHelper . WriteLogToFile (
"The Log.txt file has been successfully deleted. Original file size: " + fileSizeInKB +
2025-07-28 14:40:44 +08:00
" KB" ) ;
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
catch ( Exception ex )
{
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile (
ex + " | Can not delete the Log.txt file. File size: " + fileSizeInKB + " KB" ,
LogHelper . LogType . Error ) ;
}
}
}
2025-08-03 16:46:33 +08:00
catch ( Exception ex )
{
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile ( ex . ToString ( ) , LogHelper . LogType . Error ) ;
}
InitTimers ( ) ;
timeMachine . OnRedoStateChanged + = TimeMachine_OnRedoStateChanged ;
timeMachine . OnUndoStateChanged + = TimeMachine_OnUndoStateChanged ;
inkCanvas . Strokes . StrokesChanged + = StrokesOnStrokesChanged ;
2025-07-28 14:40:44 +08:00
SystemEvents . UserPreferenceChanged + = SystemEvents_UserPreferenceChanged ;
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
if ( File . Exists ( "SpecialVersion.ini" ) ) SpecialVersionResetToSuggestion_Click ( ) ;
}
2025-08-03 16:46:33 +08:00
catch ( Exception ex )
{
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile ( ex . ToString ( ) , LogHelper . LogType . Error ) ;
}
CheckColorTheme ( true ) ;
CheckPenTypeUIState ( ) ;
2025-06-12 11:21:45 +08:00
2025-07-26 19:03:07 +08:00
// 初始化墨迹平滑管理器
2025-07-28 14:40:44 +08:00
_inkSmoothingManager = new InkSmoothingManager ( Dispatcher ) ;
2025-07-26 19:03:07 +08:00
2025-08-23 23:13:39 +08:00
// 初始化墨迹渐隐管理器
_inkFadeManager = new InkFadeManager ( this ) ;
2025-06-12 11:21:45 +08:00
// 注册输入事件
inkCanvas . PreviewMouseDown + = inkCanvas_PreviewMouseDown ;
inkCanvas . StylusDown + = inkCanvas_StylusDown ;
2025-07-30 19:56:22 +08:00
inkCanvas . MouseRightButtonUp + = InkCanvas_MouseRightButtonUp ;
2025-07-18 19:20:06 +08:00
// 初始化第一页Canvas
var firstCanvas = new System . Windows . Controls . Canvas ( ) ;
whiteboardPages . Add ( firstCanvas ) ;
InkCanvasGridForInkReplay . Children . Add ( firstCanvas ) ;
currentPageIndex = 0 ;
ShowPage ( currentPageIndex ) ;
2025-07-21 10:14:21 +08:00
// 手动实现触摸滑动
double leftTouchStartY = 0 ;
double leftScrollStartOffset = 0 ;
bool leftIsTouching = false ;
2025-08-03 16:46:33 +08:00
BlackBoardLeftSidePageListScrollViewer . TouchDown + = ( s , e ) = >
{
2025-07-21 10:14:21 +08:00
leftIsTouching = true ;
leftTouchStartY = e . GetTouchPoint ( BlackBoardLeftSidePageListScrollViewer ) . Position . Y ;
leftScrollStartOffset = BlackBoardLeftSidePageListScrollViewer . VerticalOffset ;
BlackBoardLeftSidePageListScrollViewer . CaptureTouch ( e . TouchDevice ) ;
e . Handled = true ;
} ;
2025-08-03 16:46:33 +08:00
BlackBoardLeftSidePageListScrollViewer . TouchMove + = ( s , e ) = >
{
if ( leftIsTouching )
{
2025-07-21 10:14:21 +08:00
double currentY = e . GetTouchPoint ( BlackBoardLeftSidePageListScrollViewer ) . Position . Y ;
double delta = leftTouchStartY - currentY ;
BlackBoardLeftSidePageListScrollViewer . ScrollToVerticalOffset ( leftScrollStartOffset + delta ) ;
e . Handled = true ;
}
} ;
2025-08-03 16:46:33 +08:00
BlackBoardLeftSidePageListScrollViewer . TouchUp + = ( s , e ) = >
{
2025-07-21 10:14:21 +08:00
leftIsTouching = false ;
BlackBoardLeftSidePageListScrollViewer . ReleaseTouchCapture ( e . TouchDevice ) ;
e . Handled = true ;
} ;
double rightTouchStartY = 0 ;
double rightScrollStartOffset = 0 ;
bool rightIsTouching = false ;
2025-08-03 16:46:33 +08:00
BlackBoardRightSidePageListScrollViewer . TouchDown + = ( s , e ) = >
{
2025-07-21 10:14:21 +08:00
rightIsTouching = true ;
rightTouchStartY = e . GetTouchPoint ( BlackBoardRightSidePageListScrollViewer ) . Position . Y ;
rightScrollStartOffset = BlackBoardRightSidePageListScrollViewer . VerticalOffset ;
BlackBoardRightSidePageListScrollViewer . CaptureTouch ( e . TouchDevice ) ;
e . Handled = true ;
} ;
2025-08-03 16:46:33 +08:00
BlackBoardRightSidePageListScrollViewer . TouchMove + = ( s , e ) = >
{
if ( rightIsTouching )
{
2025-07-21 10:14:21 +08:00
double currentY = e . GetTouchPoint ( BlackBoardRightSidePageListScrollViewer ) . Position . Y ;
double delta = rightTouchStartY - currentY ;
BlackBoardRightSidePageListScrollViewer . ScrollToVerticalOffset ( rightScrollStartOffset + delta ) ;
e . Handled = true ;
}
} ;
2025-08-03 16:46:33 +08:00
BlackBoardRightSidePageListScrollViewer . TouchUp + = ( s , e ) = >
{
2025-07-21 10:14:21 +08:00
rightIsTouching = false ;
BlackBoardRightSidePageListScrollViewer . ReleaseTouchCapture ( e . TouchDevice ) ;
e . Handled = true ;
} ;
2025-07-25 18:21:16 +08:00
// 初始化无焦点模式开关
ToggleSwitchNoFocusMode . IsOn = Settings . Advanced . IsNoFocusMode ;
2025-11-08 23:09:04 +08:00
ApplyNoFocusMode ( ) ;
2025-08-13 11:54:36 +08:00
// 初始化窗口置顶开关
ToggleSwitchAlwaysOnTop . IsOn = Settings . Advanced . IsAlwaysOnTop ;
2025-11-08 23:09:04 +08:00
ApplyAlwaysOnTop ( ) ;
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
// 添加窗口激活事件处理,确保置顶状态在窗口重新激活时得到保持
2025-08-30 19:40:14 +08:00
Activated + = Window_Activated ;
Deactivated + = Window_Deactivated ;
2025-08-31 11:43:52 +08:00
2025-08-30 18:10:55 +08:00
// 为浮动栏按钮添加触摸事件支持
AddTouchSupportToFloatingBarButtons ( ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
// 为滑块控件添加触摸事件支持
AddTouchSupportToSliders ( ) ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
// 初始化计时器控件事件
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
if ( TimerControl ! = null )
{
TimerControl . ShowMinimizedRequested + = TimerControl_ShowMinimizedRequested ;
TimerControl . HideMinimizedRequested + = TimerControl_HideMinimizedRequested ;
}
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
if ( MinimizedTimerControl ! = null & & TimerControl ! = null )
{
MinimizedTimerControl . SetParentControl ( TimerControl ) ;
}
} ) , DispatcherPriority . Loaded ) ;
}
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
private void TimerControl_ShowMinimizedRequested ( object sender , EventArgs e )
{
var timerContainer = FindName ( "TimerContainer" ) as FrameworkElement ;
var minimizedContainer = FindName ( "MinimizedTimerContainer" ) as FrameworkElement ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
if ( timerContainer ! = null & & minimizedContainer ! = null )
{
double x = 0 , y = 0 ;
2025-12-20 13:56:46 +08:00
if ( timerContainer . HorizontalAlignment = = HorizontalAlignment . Center & &
2025-11-29 16:27:35 +08:00
timerContainer . VerticalAlignment = = VerticalAlignment . Center )
{
var timerPoint = timerContainer . TransformToAncestor ( this ) . Transform ( new Point ( 0 , 0 ) ) ;
x = timerPoint . X ;
y = timerPoint . Y ;
}
else
{
var timerMargin = timerContainer . Margin ;
x = double . IsNaN ( timerMargin . Left ) ? 0 : timerMargin . Left ;
y = double . IsNaN ( timerMargin . Top ) ? 0 : timerMargin . Top ;
}
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
minimizedContainer . Margin = new Thickness ( x , y , 0 , 0 ) ;
minimizedContainer . HorizontalAlignment = HorizontalAlignment . Left ;
minimizedContainer . VerticalAlignment = VerticalAlignment . Top ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
timerContainer . Margin = new Thickness ( x , y , 0 , 0 ) ;
timerContainer . HorizontalAlignment = HorizontalAlignment . Left ;
timerContainer . VerticalAlignment = VerticalAlignment . Top ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
timerContainer . Visibility = Visibility . Collapsed ;
minimizedContainer . Visibility = Visibility . Visible ;
}
}
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
private void TimerControl_HideMinimizedRequested ( object sender , EventArgs e )
{
var timerContainer = FindName ( "TimerContainer" ) as FrameworkElement ;
var minimizedContainer = FindName ( "MinimizedTimerContainer" ) as FrameworkElement ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
if ( timerContainer ! = null & & minimizedContainer ! = null )
{
minimizedContainer . Visibility = Visibility . Collapsed ;
timerContainer . Visibility = Visibility . Visible ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
if ( TimerControl ! = null )
{
TimerControl . UpdateActivityTime ( ) ;
}
}
2025-05-25 09:29:48 +08:00
}
2025-07-25 18:12:46 +08:00
2025-07-20 21:03:42 +08:00
2025-05-25 09:29:48 +08:00
#endregion
#region Ink Canvas Functions
2025-07-28 14:40:44 +08:00
private Color Ink_DefaultColor = Colors . Red ;
2025-05-25 09:29:48 +08:00
private DrawingAttributes drawingAttributes ;
2025-07-28 14:40:44 +08:00
private InkSmoothingManager _inkSmoothingManager ;
2025-05-25 09:29:48 +08:00
2025-08-03 16:46:33 +08:00
private void loadPenCanvas ( )
{
try
{
2025-05-25 09:29:48 +08:00
//drawingAttributes = new DrawingAttributes();
drawingAttributes = inkCanvas . DefaultDrawingAttributes ;
drawingAttributes . Color = Ink_DefaultColor ;
drawingAttributes . Height = 2.5 ;
drawingAttributes . Width = 2.5 ;
drawingAttributes . IsHighlighter = false ;
2025-07-20 15:21:59 +08:00
// 默认使用高级贝塞尔曲线平滑,如果未启用则使用原来的FitToCurve
if ( Settings . Canvas . UseAdvancedBezierSmoothing )
{
drawingAttributes . FitToCurve = false ;
}
else
{
drawingAttributes . FitToCurve = Settings . Canvas . FitToCurve ;
}
2025-05-25 09:29:48 +08:00
inkCanvas . Gesture + = InkCanvas_Gesture ;
}
catch { }
}
//ApplicationGesture lastApplicationGesture = ApplicationGesture.AllGestures;
private DateTime lastGestureTime = DateTime . Now ;
2025-08-03 16:46:33 +08:00
private void InkCanvas_Gesture ( object sender , InkCanvasGestureEventArgs e )
{
2025-05-25 09:29:48 +08:00
var gestures = e . GetGestureRecognitionResults ( ) ;
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
foreach ( var gest in gestures )
//Trace.WriteLine(string.Format("Gesture: {0}, Confidence: {1}", gest.ApplicationGesture, gest.RecognitionConfidence));
2025-09-06 10:17:12 +08:00
// 只有在PPT放映模式下才响应翻页手势
2025-09-07 13:30:46 +08:00
if ( StackPanelPPTControls . Visibility = = Visibility . Visible & &
2025-09-06 10:17:12 +08:00
BtnPPTSlideShowEnd . Visibility = = Visibility . Visible & &
PPTManager ? . IsInSlideShow = = true )
2025-08-03 16:46:33 +08:00
{
2025-05-25 09:29:48 +08:00
if ( gest . ApplicationGesture = = ApplicationGesture . Left )
2025-08-24 11:08:13 +08:00
{
2025-11-15 18:04:22 +08:00
BtnPPTSlidesDown_Click ( null , null ) ; // 下一页
2025-08-24 11:08:13 +08:00
}
2025-05-25 09:29:48 +08:00
if ( gest . ApplicationGesture = = ApplicationGesture . Right )
2025-08-24 11:08:13 +08:00
{
2025-11-15 18:04:22 +08:00
BtnPPTSlidesUp_Click ( null , null ) ; // 上一页
2025-08-24 11:08:13 +08:00
}
2025-05-25 09:29:48 +08:00
}
}
catch { }
}
2025-08-03 16:46:33 +08:00
private void inkCanvas_EditingModeChanged ( object sender , RoutedEventArgs e )
{
2025-09-30 19:15:03 +08:00
var inkCanvas1 = sender as InkCanvas ;
if ( inkCanvas1 = = null ) return ;
2025-07-19 10:56:30 +08:00
2025-06-18 09:08:38 +08:00
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode ( inkCanvas1 ) ;
2025-08-03 16:46:33 +08:00
if ( Settings . Canvas . IsShowCursor )
{
2025-07-19 10:56:30 +08:00
if ( inkCanvas1 . EditingMode = = InkCanvasEditingMode . Ink | |
inkCanvas1 . EditingMode = = InkCanvasEditingMode . Select | |
drawingShapeMode ! = 0 )
2025-07-18 11:49:00 +08:00
inkCanvas1 . ForceCursor = true ;
else
inkCanvas1 . ForceCursor = false ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-07-23 22:16:43 +08:00
// 套索选择模式下始终强制显示光标,即使用户设置不显示光标
2025-08-03 16:46:33 +08:00
if ( inkCanvas1 . EditingMode = = InkCanvasEditingMode . Select )
{
2025-07-19 10:56:30 +08:00
inkCanvas1 . ForceCursor = true ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-07-19 10:56:30 +08:00
inkCanvas1 . ForceCursor = false ;
}
2025-07-18 11:49:00 +08:00
}
2025-07-23 22:16:43 +08:00
2025-05-25 09:29:48 +08:00
if ( inkCanvas1 . EditingMode = = InkCanvasEditingMode . Ink ) forcePointEraser = ! forcePointEraser ;
2025-08-03 16:46:33 +08:00
2025-09-27 17:01:33 +08:00
// 处理橡皮擦覆盖层的启用/禁用
2025-09-30 19:15:03 +08:00
var eraserOverlay = FindName ( "EraserOverlayCanvas" ) as Canvas ;
if ( eraserOverlay ! = null )
2025-08-03 16:46:33 +08:00
{
if ( inkCanvas1 . EditingMode = = InkCanvasEditingMode . EraseByPoint )
{
2025-07-23 22:16:43 +08:00
// 橡皮擦模式下启用覆盖层
2025-09-27 17:01:33 +08:00
EnableEraserOverlay ( ) ;
Trace . WriteLine ( "Eraser: Overlay enabled in eraser mode" ) ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-07-23 22:16:43 +08:00
// 其他模式下禁用覆盖层
2025-09-27 17:01:33 +08:00
DisableEraserOverlay ( ) ;
Trace . WriteLine ( "Eraser: Overlay disabled in non-eraser mode" ) ;
2025-07-23 22:16:43 +08:00
}
}
2025-05-25 09:29:48 +08:00
}
#endregion Ink Canvas
#region Definations and Loading
public static Settings Settings = new Settings ( ) ;
2025-09-13 21:10:36 +08:00
public static string settingsFileName = Path . Combine ( "Configs" , "Settings.json" ) ;
2025-07-28 14:40:44 +08:00
private bool isLoaded ;
private bool forcePointEraser ;
2025-05-25 09:29:48 +08:00
2025-08-03 16:46:33 +08:00
private void Window_Loaded ( object sender , RoutedEventArgs e )
{
2025-05-25 09:29:48 +08:00
loadPenCanvas ( ) ;
//加载设置
LoadSettings ( true ) ;
2025-10-01 00:01:35 +08:00
AutoBackupManager . Initialize ( Settings ) ;
2025-10-03 17:08:46 +08:00
2025-11-29 17:26:47 +08:00
// 初始化Dlass上传队列(恢复上次的上传队列)
DlassNoteUploader . InitializeQueue ( ) ;
2025-07-24 00:20:00 +08:00
// 检查保存路径是否可用,不可用则修正
try
{
string savePath = Settings . Automation . AutoSavedStrokesLocation ;
bool needFix = false ;
2025-07-28 14:40:44 +08:00
if ( string . IsNullOrWhiteSpace ( savePath ) | | ! Directory . Exists ( savePath ) )
2025-07-24 00:20:00 +08:00
{
needFix = true ;
}
else
{
// 检查是否可写
try
{
2025-07-28 14:40:44 +08:00
string testFile = Path . Combine ( savePath , "test.tmp" ) ;
File . WriteAllText ( testFile , "test" ) ;
File . Delete ( testFile ) ;
2025-07-24 00:20:00 +08:00
}
catch
{
needFix = true ;
}
}
if ( needFix )
{
2025-09-13 21:10:36 +08:00
string newPath = Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Saves" ) ;
2025-07-24 00:20:00 +08:00
Settings . Automation . AutoSavedStrokesLocation = newPath ;
2025-07-28 14:40:44 +08:00
if ( ! Directory . Exists ( newPath ) )
Directory . CreateDirectory ( newPath ) ;
2025-07-24 00:20:00 +08:00
SaveSettingsToFile ( ) ;
LogHelper . WriteLogToFile ( $"自动修正保存路径为: {newPath}" ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"检测或修正保存路径时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-08-03 16:46:33 +08:00
2025-07-16 09:16:36 +08:00
// 加载自定义背景颜色
LoadCustomBackgroundColor ( ) ;
2025-07-29 01:15:32 +08:00
2025-12-13 17:03:58 +08:00
// 设置窗口模式
SetWindowMode ( ) ;
2025-07-15 21:10:49 +08:00
// 注册设置面板滚动事件
if ( SettingsPanelScrollViewer ! = null )
{
SettingsPanelScrollViewer . ScrollChanged + = SettingsPanelScrollViewer_ScrollChanged ;
}
2025-07-29 01:15:32 +08:00
// 初始化PPT管理器
InitializePPTManagers ( ) ;
// 如果启用PPT支持,开始监控
if ( Settings . PowerPointSettings . PowerPointSupport )
{
StartPPTMonitoring ( ) ;
}
2025-08-03 16:46:33 +08:00
2025-09-06 12:09:06 +08:00
// 如果启用PowerPoint联动增强功能,启动进程守护
if ( Settings . PowerPointSettings . EnablePowerPointEnhancement )
{
StartPowerPointProcessMonitoring ( ) ;
}
2025-05-25 09:29:48 +08:00
// HasNewUpdateWindow hasNewUpdateWindow = new HasNewUpdateWindow();
if ( Environment . Is64BitProcess ) GroupBoxInkRecognition . Visibility = Visibility . Collapsed ;
2025-09-21 00:25:09 +08:00
// 根据设置应用主题
switch ( Settings . Appearance . Theme )
{
case 0 : // 浅色主题
ThemeManager . Current . ApplicationTheme = ApplicationTheme . Light ;
SetTheme ( "Light" ) ;
break ;
case 1 : // 深色主题
ThemeManager . Current . ApplicationTheme = ApplicationTheme . Dark ;
SetTheme ( "Dark" ) ;
break ;
case 2 : // 跟随系统
if ( IsSystemThemeLight ( ) )
{
ThemeManager . Current . ApplicationTheme = ApplicationTheme . Light ;
SetTheme ( "Light" ) ;
}
else
{
ThemeManager . Current . ApplicationTheme = ApplicationTheme . Dark ;
SetTheme ( "Dark" ) ;
}
break ;
}
2025-05-25 09:29:48 +08:00
//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 =
2025-07-28 14:40:44 +08:00
new SolidColorBrush ( Color . FromArgb ( 127 , 24 , 24 , 27 ) ) ;
2025-05-25 09:29:48 +08:00
BtnLeftWhiteBoardSwitchPreviousLabel . Opacity = 0.5 ;
BtnRightWhiteBoardSwitchPreviousGeometry . Brush =
2025-07-28 14:40:44 +08:00
new SolidColorBrush ( Color . FromArgb ( 127 , 24 , 24 , 27 ) ) ;
2025-05-25 09:29:48 +08:00
BtnRightWhiteBoardSwitchPreviousLabel . Opacity = 0.5 ;
2025-07-16 09:16:36 +08:00
// 应用颜色主题,这将考虑自定义背景色
CheckColorTheme ( true ) ;
2025-08-03 16:46:33 +08:00
2025-05-25 09:29:48 +08:00
BtnWhiteBoardSwitchPrevious . IsEnabled = CurrentWhiteboardIndex ! = 1 ;
BorderInkReplayToolBox . Visibility = Visibility . Collapsed ;
// 提前加载IA库,优化第一笔等待时间
2025-08-03 16:46:33 +08:00
if ( Settings . InkToShape . IsInkToShapeEnabled & & ! Environment . Is64BitProcess )
{
2025-05-25 09:29:48 +08:00
var strokeEmpty = new StrokeCollection ( ) ;
InkRecognizeHelper . RecognizeShape ( strokeEmpty ) ;
}
SystemEvents . DisplaySettingsChanged + = SystemEventsOnDisplaySettingsChanged ;
2025-10-02 15:30:51 +08:00
// 自动收纳到侧边栏(若通过 --board 进入白板模式或 --show 参数则跳过收纳)
if ( Settings . Startup . IsFoldAtStartup & & ! App . StartWithBoardMode & & ! App . StartWithShowMode )
2025-05-31 21:13:02 +08:00
{
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-20 15:42:11 +08:00
2025-10-26 00:21:10 +08:00
// 显示快抽悬浮按钮
ShowQuickDrawFloatingButton ( ) ;
2025-07-16 09:16:36 +08:00
// 如果当前不是黑板模式,则切换到黑板模式
if ( currentMode = = 0 )
{
// 延迟执行,确保UI已完全加载
2025-08-03 16:46:33 +08:00
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
2025-07-16 09:16:36 +08:00
// 重新加载自定义背景颜色
LoadCustomBackgroundColor ( ) ;
2025-08-03 16:46:33 +08:00
2025-07-16 09:16:36 +08:00
// 模拟点击切换按钮进入黑板模式
if ( GridTransparencyFakeBackground . Background ! = Brushes . Transparent )
{
BtnSwitch_Click ( BtnSwitch , null ) ;
}
2025-08-03 16:46:33 +08:00
2025-07-16 09:16:36 +08:00
// 确保背景颜色正确设置为黑板颜色
CheckColorTheme ( true ) ;
2025-07-28 14:40:44 +08:00
} ) , DispatcherPriority . Loaded ) ;
2025-07-16 09:16:36 +08:00
}
2025-07-16 13:35:17 +08:00
// 初始化插件系统
InitializePluginSystem ( ) ;
2025-07-25 18:21:16 +08:00
// 确保开关和设置同步
ToggleSwitchNoFocusMode . IsOn = Settings . Advanced . IsNoFocusMode ;
2025-11-08 23:09:04 +08:00
ApplyNoFocusMode ( ) ;
2025-08-13 11:54:36 +08:00
ToggleSwitchAlwaysOnTop . IsOn = Settings . Advanced . IsAlwaysOnTop ;
2025-11-08 23:09:04 +08:00
ApplyAlwaysOnTop ( ) ;
2025-10-03 17:08:46 +08:00
2025-10-18 18:26:13 +08:00
// 初始化UIA置顶开关
ToggleSwitchUIAccessTopMost . IsOn = Settings . Advanced . EnableUIAccessTopMost ;
UpdateUIAccessTopMostVisibility ( ) ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
App . IsUIAccessTopMostEnabled = Settings . Advanced . EnableUIAccessTopMost ;
2025-07-30 19:56:22 +08:00
// 初始化剪贴板监控
InitializeClipboardMonitoring ( ) ;
2025-08-31 11:43:52 +08:00
2025-09-13 15:34:50 +08:00
// 初始化悬浮窗拦截管理器
InitializeFloatingWindowInterceptor ( ) ;
2025-08-23 21:39:00 +08:00
// 初始化全局快捷键管理器
InitializeGlobalHotkeyManager ( ) ;
2025-08-23 23:13:39 +08:00
// 初始化墨迹渐隐管理器
InitializeInkFadeManager ( ) ;
2025-08-31 11:49:00 +08:00
// 处理命令行参数中的文件路径
HandleCommandLineFileOpen ( ) ;
2025-08-31 12:03:58 +08:00
// 初始化文件关联状态显示
InitializeFileAssociationStatus ( ) ;
2025-09-06 21:26:46 +08:00
// 检查模式设置并应用
CheckMainWindowVisibility ( ) ;
2025-09-13 14:59:47 +08:00
// 检查是否通过--board参数启动,如果是则自动切换到白板模式
if ( App . StartWithBoardMode )
{
LogHelper . WriteLogToFile ( "检测到--board参数,自动切换到白板模式" , LogHelper . LogType . Event ) ;
// 延迟执行,确保UI已完全加载
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
SwitchToBoardMode ( ) ;
} ) , DispatcherPriority . Loaded ) ;
}
2025-10-02 15:30:51 +08:00
// 检查是否通过--show参数启动,如果是则确保退出收纳模式并恢复浮动栏
if ( App . StartWithShowMode )
{
LogHelper . WriteLogToFile ( "检测到--show参数,退出收纳模式并恢复浮动栏" , LogHelper . LogType . Event ) ;
// 延迟执行,确保UI已完全加载
Dispatcher . BeginInvoke ( new Action ( async ( ) = >
{
// 如果当前处于收纳模式,则展开浮动栏
if ( isFloatingBarFolded )
{
await UnFoldFloatingBar ( new object ( ) ) ;
}
} ) , DispatcherPriority . Loaded ) ;
}
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
// 初始化计时器控件关联
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
if ( TimerControl ! = null & & MinimizedTimerControl ! = null )
{
MinimizedTimerControl . SetParentControl ( TimerControl ) ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
TimerControl . ShowMinimizedRequested + = ( s , args ) = >
{
if ( TimerContainer ! = null & & MinimizedTimerContainer ! = null & & MinimizedTimerControl ! = null )
{
TimerContainer . Visibility = Visibility . Collapsed ;
MinimizedTimerContainer . Visibility = Visibility . Visible ;
MinimizedTimerControl . Visibility = Visibility . Visible ;
}
} ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
TimerControl . HideMinimizedRequested + = ( s , args ) = >
{
if ( MinimizedTimerContainer ! = null & & MinimizedTimerControl ! = null )
{
MinimizedTimerContainer . Visibility = Visibility . Collapsed ;
MinimizedTimerControl . Visibility = Visibility . Collapsed ;
}
} ;
}
} ) , DispatcherPriority . Loaded ) ;
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
private void SystemEventsOnDisplaySettingsChanged ( object sender , EventArgs e )
{
2025-05-25 09:29:48 +08:00
if ( ! Settings . Advanced . IsEnableResolutionChangeDetection ) return ;
2025-07-28 14:40:44 +08:00
ShowNotification ( $"检测到显示器信息变化,变为{Screen.PrimaryScreen.Bounds.Width}x{Screen.PrimaryScreen.Bounds.Height}) " ) ;
2025-08-03 16:46:33 +08:00
new Thread ( ( ) = >
{
2025-05-25 09:29:48 +08:00
var isFloatingBarOutsideScreen = false ;
var isInPPTPresentationMode = false ;
2025-08-03 16:46:33 +08:00
Dispatcher . Invoke ( ( ) = >
{
2025-05-25 09:29:48 +08:00
isFloatingBarOutsideScreen = IsOutsideOfScreenHelper . IsOutsideOfScreen ( ViewboxFloatingBar ) ;
isInPPTPresentationMode = BtnPPTSlideShowEnd . Visibility = = Visibility . Visible ;
} ) ;
2025-08-03 16:46:33 +08:00
if ( isFloatingBarOutsideScreen ) dpiChangedDelayAction . DebounceAction ( 3000 , null , ( ) = >
{
2025-05-25 09:29:48 +08:00
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}" ) ;
2025-08-03 16:46:33 +08:00
new Thread ( ( ) = >
{
2025-05-25 09:29:48 +08:00
var isFloatingBarOutsideScreen = false ;
var isInPPTPresentationMode = false ;
2025-08-03 16:46:33 +08:00
Dispatcher . Invoke ( ( ) = >
{
2025-05-25 09:29:48 +08:00
isFloatingBarOutsideScreen = IsOutsideOfScreenHelper . IsOutsideOfScreen ( ViewboxFloatingBar ) ;
isInPPTPresentationMode = BtnPPTSlideShowEnd . Visibility = = Visibility . Visible ;
} ) ;
2025-08-03 16:46:33 +08:00
if ( isFloatingBarOutsideScreen ) dpiChangedDelayAction . DebounceAction ( 3000 , null , ( ) = >
{
2025-05-25 09:29:48 +08:00
if ( ! isFloatingBarFolded )
{
if ( isInPPTPresentationMode ) ViewboxFloatingBarMarginAnimation ( 60 ) ;
else ViewboxFloatingBarMarginAnimation ( 100 , true ) ;
}
} ) ;
} ) . Start ( ) ;
}
}
2025-12-13 17:03:58 +08:00
private void SetWindowMode ( )
{
if ( Settings . Advanced . WindowMode )
{
WindowState = WindowState . Normal ;
Left = 0.0 ;
Top = 0.0 ;
Height = SystemParameters . PrimaryScreenHeight ;
Width = SystemParameters . PrimaryScreenWidth ;
}
else // 全屏
{
WindowState = WindowState . Maximized ;
}
}
2025-08-03 16:46:33 +08:00
private void Window_Closing ( object sender , CancelEventArgs e )
{
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile ( "Ink Canvas closing" , LogHelper . LogType . Event ) ;
2025-10-26 00:21:10 +08:00
try
{
2025-11-08 22:07:38 +08:00
// 快抽按钮现在集成在主窗口中,不需要单独关闭
2025-10-26 00:21:10 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"关闭快抽悬浮按钮时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-12-20 13:56:46 +08:00
2025-08-03 16:46:33 +08:00
if ( ! CloseIsFromButton & & Settings . Advanced . IsSecondConfirmWhenShutdownApp )
{
2025-07-31 10:03:29 +08:00
// 第一个确认对话框
var result1 = MessageBox . Show ( "是否继续关闭 InkCanvasForClass,这将丢失当前未保存的墨迹。" , "InkCanvasForClass" ,
MessageBoxButton . OKCancel , MessageBoxImage . Warning ) ;
2025-08-03 16:46:33 +08:00
if ( result1 = = MessageBoxResult . Cancel )
{
2025-07-31 10:03:29 +08:00
e . Cancel = true ;
LogHelper . WriteLogToFile ( "Ink Canvas closing cancelled at first confirmation" , LogHelper . LogType . Event ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-31 10:03:29 +08:00
// 第二个确认对话框
2025-08-03 16:46:33 +08:00
var result2 = MessageBox . Show ( "真的狠心关闭 InkCanvasForClass吗?" , "InkCanvasForClass" ,
2025-07-31 10:03:29 +08:00
MessageBoxButton . OKCancel , MessageBoxImage . Error ) ;
2025-08-03 16:46:33 +08:00
if ( result2 = = MessageBoxResult . Cancel )
{
2025-07-31 10:03:29 +08:00
e . Cancel = true ;
LogHelper . WriteLogToFile ( "Ink Canvas closing cancelled at second confirmation" , LogHelper . LogType . Event ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-31 10:03:29 +08:00
// 第三个最终确认对话框
2025-08-03 16:46:33 +08:00
var result3 = MessageBox . Show ( "最后确认:确定要关闭 InkCanvasForClass 吗?" , "InkCanvasForClass" ,
2025-07-31 10:03:29 +08:00
MessageBoxButton . OKCancel , MessageBoxImage . Question ) ;
2025-08-03 16:46:33 +08:00
if ( result3 = = MessageBoxResult . Cancel )
{
2025-07-31 10:03:29 +08:00
e . Cancel = true ;
LogHelper . WriteLogToFile ( "Ink Canvas closing cancelled at final confirmation" , LogHelper . LogType . Event ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-31 10:03:29 +08:00
// 所有确认都通过,允许关闭
e . Cancel = false ;
LogHelper . WriteLogToFile ( "Ink Canvas closing confirmed by user" , LogHelper . LogType . Event ) ;
2025-05-25 09:29:48 +08:00
}
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-08-03 16:46:33 +08:00
private void MainWindow_OnSizeChanged ( object sender , SizeChangedEventArgs e )
{
if ( Settings . Advanced . IsEnableForceFullScreen )
{
2025-05-25 09:29:48 +08:00
if ( isLoaded ) ShowNotification (
2025-07-28 14:40:44 +08:00
$"检测到窗口大小变化,已自动恢复到全屏:{Screen.PrimaryScreen.Bounds.Width}x{Screen.PrimaryScreen.Bounds.Height}(缩放比例为{Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth}x{Screen.PrimaryScreen.Bounds.Height / SystemParameters.PrimaryScreenHeight}) " ) ;
2025-05-25 09:29:48 +08:00
WindowState = WindowState . Maximized ;
MoveWindow ( new WindowInteropHelper ( this ) . Handle , 0 , 0 ,
2025-07-28 14:40:44 +08:00
Screen . PrimaryScreen . Bounds . Width ,
Screen . PrimaryScreen . Bounds . Height , true ) ;
2025-05-25 09:29:48 +08:00
}
}
2025-06-06 16:09:28 +08:00
2025-08-03 16:46:33 +08:00
private void Window_Closed ( object sender , EventArgs e )
{
2025-05-25 09:29:48 +08:00
SystemEvents . DisplaySettingsChanged - = SystemEventsOnDisplaySettingsChanged ;
2025-07-29 01:15:32 +08:00
// 释放PPT管理器资源
DisposePPTManagers ( ) ;
2025-07-30 19:56:22 +08:00
// 清理剪贴板监控
CleanupClipboardMonitoring ( ) ;
ClipboardNotification . Stop ( ) ;
2025-08-31 11:43:52 +08:00
2025-08-23 21:39:00 +08:00
// 清理全局快捷键管理器
if ( _globalHotkeyManager ! = null )
{
_globalHotkeyManager . Dispose ( ) ;
_globalHotkeyManager = null ;
}
2025-07-30 19:56:22 +08:00
2025-08-23 23:13:39 +08:00
// 清理墨迹渐隐管理器
if ( _inkFadeManager ! = null )
{
_inkFadeManager . ClearAllFadingStrokes ( ) ;
_inkFadeManager = null ;
}
2025-10-02 11:50:00 +08:00
// 清理悬浮窗拦截管理器
if ( _floatingWindowInterceptorManager ! = null )
{
_floatingWindowInterceptorManager . Dispose ( ) ;
_floatingWindowInterceptorManager = null ;
}
2025-08-13 12:52:56 +08:00
// 停止置顶维护定时器
StopTopmostMaintenance ( ) ;
2025-11-15 20:24:28 +08:00
UninstallKeyboardHook ( ) ;
2025-09-06 12:38:57 +08:00
// 从Z-Order管理器中移除主窗口
WindowZOrderManager . UnregisterWindow ( this ) ;
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile ( "Ink Canvas closed" , LogHelper . LogType . Event ) ;
2025-07-29 01:15:32 +08:00
2025-06-18 18:16:48 +08:00
// 检查是否有待安装的更新
CheckPendingUpdates ( ) ;
}
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
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" ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
if ( File . Exists ( statusFilePath ) & & File . ReadAllText ( statusFilePath ) . Trim ( ) . ToLower ( ) = = "true" )
{
LogHelper . WriteLogToFile ( $"AutoUpdate | Installing pending update v{AvailableLatestVersion} on application close" ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 设置为用户主动退出,避免被看门狗判定为崩溃
App . IsAppExitByUser = true ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 创建批处理脚本并启动,软件关闭后会执行更新操作
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
}
2025-10-06 14:11:32 +08:00
// 使用多线路组下载更新
2025-07-19 16:03:45 +08:00
private async Task < bool > DownloadUpdateWithFallback ( string version , AutoUpdateHelper . UpdateLineGroup primaryGroup , UpdateChannel channel )
{
try
{
// 如果主要线路组可用,直接使用
if ( primaryGroup ! = null )
{
LogHelper . WriteLogToFile ( $"AutoUpdate | 使用主要线路组下载: {primaryGroup.GroupName}" ) ;
return await AutoUpdateHelper . DownloadSetupFile ( version , primaryGroup ) ;
}
2025-08-03 16:46:33 +08:00
2025-07-19 16:03:45 +08:00
// 如果主要线路组不可用,获取所有可用线路组
LogHelper . WriteLogToFile ( "AutoUpdate | 主要线路组不可用,获取所有可用线路组" ) ;
var availableGroups = await AutoUpdateHelper . GetAvailableLineGroupsOrdered ( channel ) ;
if ( availableGroups . Count = = 0 )
{
LogHelper . WriteLogToFile ( "AutoUpdate | 没有可用的线路组" , LogHelper . LogType . Error ) ;
return false ;
}
2025-08-03 16:46:33 +08:00
2025-07-19 16:03:45 +08:00
LogHelper . WriteLogToFile ( $"AutoUpdate | 使用 {availableGroups.Count} 个可用线路组进行下载" ) ;
return await AutoUpdateHelper . DownloadSetupFileWithFallback ( version , availableGroups ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"AutoUpdate | 下载更新时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
return false ;
}
}
2025-08-03 16:46:33 +08:00
private async void AutoUpdate ( )
{
2025-12-20 17:45:35 +08:00
if ( ! string . IsNullOrEmpty ( Settings . Startup . AutoUpdatePauseUntilDate ) )
{
if ( DateTime . TryParse ( Settings . Startup . AutoUpdatePauseUntilDate , out DateTime pauseUntilDate ) )
{
if ( DateTime . Now < pauseUntilDate )
{
LogHelper . WriteLogToFile ( $"AutoUpdate | 自动更新已暂停,直到 {pauseUntilDate:yyyy-MM-dd}" ) ;
return ;
}
else
{
LogHelper . WriteLogToFile ( $"AutoUpdate | 暂停期已过,恢复自动更新检查" ) ;
Settings . Startup . AutoUpdatePauseUntilDate = "" ;
SaveSettingsToFile ( ) ;
}
}
}
2025-07-19 16:03:45 +08:00
// 清除之前的更新状态,确保使用新通道重新检查
AvailableLatestVersion = null ;
AvailableLatestLineGroup = null ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 使用当前选择的更新通道检查更新
2025-07-22 18:02:29 +08:00
var ( remoteVersion , lineGroup , apiReleaseNotes ) = await AutoUpdateHelper . CheckForUpdates ( Settings . Startup . UpdateChannel ) ;
2025-07-19 15:36:05 +08:00
AvailableLatestVersion = remoteVersion ;
AvailableLatestLineGroup = lineGroup ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 声明下载状态变量,用于整个方法
bool isDownloadSuccessful = false ;
2025-05-25 09:29:48 +08:00
2025-10-06 22:41:53 +08:00
bool hasValidLineGroup = lineGroup ! = null ;
2025-08-03 16:46:33 +08:00
if ( AvailableLatestVersion ! = null )
{
2025-10-04 14:47:53 +08:00
// 检测到新版本,停止重试定时器
timerCheckAutoUpdateRetry . Stop ( ) ;
updateCheckRetryCount = 0 ;
2025-10-06 18:29:12 +08:00
2025-06-29 11:56:38 +08:00
// 检测到新版本
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( $"AutoUpdate | New version available: {AvailableLatestVersion}" ) ;
2025-08-03 16:46:33 +08:00
2025-12-20 19:16:39 +08:00
// 通过 Windows 系统通知提示有新版本
WindowsNotificationHelper . ShowNewVersionToast ( AvailableLatestVersion ) ;
2025-06-29 11:56:38 +08:00
// 检查是否是用户选择跳过的版本
2025-08-03 16:46:33 +08:00
if ( ! string . IsNullOrEmpty ( Settings . Startup . SkippedVersion ) & &
Settings . Startup . SkippedVersion = = AvailableLatestVersion )
{
2025-06-29 11:56:38 +08:00
LogHelper . WriteLogToFile ( $"AutoUpdate | Version {AvailableLatestVersion} was marked to be skipped by the user" ) ;
return ; // 跳过此版本,不执行更新操作
}
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 如果检测到的版本与跳过的版本不同,则清除跳过版本记录
// 这确保用户只能跳过当前最新版本,而不是永久跳过所有更新
2025-08-03 16:46:33 +08:00
if ( ! string . IsNullOrEmpty ( Settings . Startup . SkippedVersion ) & &
Settings . Startup . SkippedVersion ! = AvailableLatestVersion )
{
2025-06-29 11:56:38 +08:00
LogHelper . WriteLogToFile ( $"AutoUpdate | Detected new version {AvailableLatestVersion} different from skipped version {Settings.Startup.SkippedVersion}, clearing skip record" ) ;
Settings . Startup . SkippedVersion = "" ;
SaveSettingsToFile ( ) ;
}
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 获取当前版本
2025-06-18 18:16:48 +08:00
string currentVersion = Assembly . GetExecutingAssembly ( ) . GetName ( ) . Version . ToString ( ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 如果启用了静默更新,则自动下载更新而不显示提示
2025-08-03 16:46:33 +08:00
if ( Settings . Startup . IsAutoUpdateWithSilence )
{
2025-06-29 11:56:38 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Silent update enabled, downloading update automatically without notification" ) ;
2025-08-03 16:46:33 +08:00
2025-07-19 16:03:45 +08:00
// 静默下载更新,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback ( AvailableLatestVersion , AvailableLatestLineGroup , Settings . Startup . UpdateChannel ) ;
2025-08-03 16:46:33 +08:00
if ( isDownloadSuccessful )
{
2025-06-29 11:56:38 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Update downloaded successfully, will install when conditions are met" ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 启动检查定时器,定期检查是否可以安装
timerCheckAutoUpdateWithSilence . Start ( ) ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-06-29 11:56:38 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Silent update download failed" , LogHelper . LogType . Error ) ;
}
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
return ;
}
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 如果没有启用静默更新,则显示常规更新窗口
2025-06-18 18:16:48 +08:00
string releaseDate = DateTime . Now . ToString ( "yyyy年MM月dd日" ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 从服务器获取更新日志
string releaseNotes = await AutoUpdateHelper . GetUpdateLog ( Settings . Startup . UpdateChannel ) ;
2025-08-03 16:46:33 +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-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 创建并显示更新窗口
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow ( currentVersion , AvailableLatestVersion , releaseDate , releaseNotes ) ;
2025-08-30 16:25:10 +08:00
updateWindow . Owner = this ;
2025-06-18 18:16:48 +08:00
bool? dialogResult = updateWindow . ShowDialog ( ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 如果窗口被关闭但没有点击按钮,则不执行任何操作
2025-08-03 16:46:33 +08:00
if ( dialogResult ! = true )
{
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Update dialog closed without selection" ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 不再从更新窗口获取自动更新设置
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 根据用户选择处理更新
2025-08-03 16:46:33 +08:00
switch ( updateWindow . Result )
{
2025-06-18 18:16:48 +08:00
case HasNewUpdateWindow . UpdateResult . UpdateNow :
// 立即更新:显示下载进度,下载完成后立即安装
LogHelper . WriteLogToFile ( "AutoUpdate | User chose to update now" ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 显示下载进度提示
MessageBox . Show ( "开始下载更新,请稍候..." , "正在更新" , MessageBoxButton . OK , MessageBoxImage . Information ) ;
2025-08-03 16:46:33 +08:00
2025-07-19 16:03:45 +08:00
// 下载更新文件,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback ( AvailableLatestVersion , AvailableLatestLineGroup , Settings . Startup . UpdateChannel ) ;
2025-08-03 16:46:33 +08:00
if ( isDownloadSuccessful )
{
2025-06-18 18:16:48 +08:00
// 下载成功,提示用户准备安装
MessageBoxResult result = MessageBox . Show ( "更新已下载完成,点击确定后将关闭软件并安装新版本!" , "安装更新" , MessageBoxButton . OKCancel , MessageBoxImage . Information ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 只有当用户点击确定按钮后才关闭软件
2025-08-03 16:46:33 +08:00
if ( result = = MessageBoxResult . OK )
{
2025-06-18 18:16:48 +08:00
// 设置为用户主动退出,避免被看门狗判定为崩溃
App . IsAppExitByUser = true ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 准备批处理脚本
2025-08-30 14:56:59 +08:00
AutoUpdateHelper . InstallNewVersionApp ( AvailableLatestVersion , true ) ; // 修改为静默模式,避免重复启动进程
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 关闭软件,让安装程序接管
Application . Current . Shutdown ( ) ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | User cancelled update installation" ) ;
}
2025-08-03 16:46:33 +08:00
}
else
{
2025-06-18 18:16:48 +08:00
// 下载失败
MessageBox . Show ( "更新下载失败,请检查网络连接后重试。" , "下载失败" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
}
break ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
case HasNewUpdateWindow . UpdateResult . UpdateLater :
// 稍后更新:静默下载,在软件关闭时自动安装
LogHelper . WriteLogToFile ( "AutoUpdate | User chose to update later" ) ;
2025-08-03 16:46:33 +08:00
2025-07-19 16:03:45 +08:00
// 不管设置如何,都进行下载,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback ( AvailableLatestVersion , AvailableLatestLineGroup , Settings . Startup . UpdateChannel ) ;
2025-08-03 16:46:33 +08:00
if ( isDownloadSuccessful )
{
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Update downloaded successfully, will install when application closes" ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 设置标志,在应用程序关闭时安装
Settings . Startup . IsAutoUpdate = true ;
Settings . Startup . IsAutoUpdateWithSilence = true ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 启动检查定时器
timerCheckAutoUpdateWithSilence . Start ( ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 通知用户
MessageBox . Show ( "更新已下载完成,将在软件关闭时自动安装。" , "更新已准备就绪" , MessageBoxButton . OK , MessageBoxImage . Information ) ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Update download failed" , LogHelper . LogType . Error ) ;
MessageBox . Show ( "更新下载失败,请检查网络连接后重试。" , "下载失败" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
}
break ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
case HasNewUpdateWindow . UpdateResult . SkipVersion :
// 跳过该版本:记录到设置中
LogHelper . WriteLogToFile ( $"AutoUpdate | User chose to skip version {AvailableLatestVersion}" ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 记录要跳过的版本号
Settings . Startup . SkippedVersion = AvailableLatestVersion ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 保存设置到文件
SaveSettingsToFile ( ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 通知用户
2025-08-03 16:46:33 +08:00
MessageBox . Show ( $"已设置跳过版本 {AvailableLatestVersion},在下次发布新版本之前不会再提示更新。" ,
"已跳过此版本" ,
MessageBoxButton . OK ,
2025-06-29 11:56:38 +08:00
MessageBoxImage . Information ) ;
2025-06-18 18:16:48 +08:00
break ;
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
}
2025-10-06 22:41:53 +08:00
else if ( hasValidLineGroup )
{
LogHelper . WriteLogToFile ( "AutoUpdate | Current version is already the latest, no retry needed" ) ;
// 停止重试定时器
timerCheckAutoUpdateRetry . Stop ( ) ;
updateCheckRetryCount = 0 ;
}
2025-08-03 16:46:33 +08:00
else
{
2025-10-04 14:47:53 +08:00
// 检查更新失败,启动重试定时器
LogHelper . WriteLogToFile ( "AutoUpdate | Update check failed, starting retry timer" ) ;
2025-10-06 18:29:12 +08:00
2025-10-04 14:47:53 +08:00
// 重置重试计数
updateCheckRetryCount = 0 ;
2025-10-06 18:29:12 +08:00
2025-10-04 14:47:53 +08:00
// 启动重试定时器,10分钟后重新检查
timerCheckAutoUpdateRetry . Start ( ) ;
2025-10-06 18:29:12 +08:00
2025-10-04 14:47:53 +08:00
// 清理更新文件夹
2025-05-25 09:29:48 +08:00
AutoUpdateHelper . DeleteUpdatesFolder ( ) ;
}
}
2025-10-06 22:41:53 +08:00
// 新增:崩溃后操作设置按钮事件
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
// 添加一个辅助方法,根据当前编辑模式设置光标
2025-07-18 16:12:04 +08:00
public void SetCursorBasedOnEditingMode ( InkCanvas canvas )
2025-06-18 09:08:38 +08:00
{
2025-07-19 10:56:30 +08:00
// 套索选择模式下光标始终显示,无论用户设置如何
2025-08-03 16:46:33 +08:00
if ( canvas . EditingMode = = InkCanvasEditingMode . Select )
{
2025-07-19 10:18:16 +08:00
canvas . UseCustomCursor = true ;
canvas . ForceCursor = true ;
2025-07-19 10:56:30 +08:00
canvas . Cursor = Cursors . Cross ;
2025-07-19 10:18:16 +08:00
System . Windows . Forms . Cursor . Show ( ) ;
return ;
}
2025-07-19 10:56:30 +08:00
// 其他模式按照用户设置处理
2025-08-03 16:46:33 +08:00
if ( Settings . Canvas . IsShowCursor )
{
2025-06-18 09:08:38 +08:00
canvas . UseCustomCursor = true ;
canvas . ForceCursor = true ;
2025-07-19 10:56:30 +08:00
2025-06-18 09:08:38 +08:00
// 根据编辑模式设置不同的光标
2025-08-03 16:46:33 +08:00
if ( canvas . EditingMode = = InkCanvasEditingMode . EraseByPoint )
{
2025-06-18 09:08:38 +08:00
canvas . Cursor = Cursors . Cross ;
2025-08-03 16:46:33 +08:00
}
else if ( canvas . EditingMode = = InkCanvasEditingMode . Ink )
{
2025-06-18 09:08:38 +08:00
var sri = Application . GetResourceStream ( new Uri ( "Resources/Cursors/Pen.cur" , UriKind . Relative ) ) ;
if ( sri ! = null )
canvas . Cursor = new Cursor ( sri . Stream ) ;
}
2025-07-19 10:56:30 +08:00
2025-06-19 11:25:15 +08:00
// 确保光标可见,无论是鼠标、触控还是手写笔
2025-06-18 09:08:38 +08:00
System . Windows . Forms . Cursor . Show ( ) ;
2025-07-19 10:56:30 +08:00
2025-06-19 11:25:15 +08:00
// 确保手写笔模式下也能显示光标
2025-08-03 16:46:33 +08:00
if ( Tablet . TabletDevices . Count > 0 )
{
foreach ( TabletDevice device in Tablet . TabletDevices )
{
if ( device . Type = = TabletDeviceType . Stylus )
{
2025-06-19 11:25:15 +08:00
// 手写笔设备存在,强制显示光标
System . Windows . Forms . Cursor . Show ( ) ;
break ;
}
}
}
2025-08-03 16:46:33 +08:00
}
else
{
2025-06-18 09:08:38 +08:00
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
// 使用辅助方法设置光标
2025-07-19 10:56:30 +08:00
SetCursorBasedOnEditingMode ( sender as InkCanvas ) ;
2025-07-30 14:12:10 +08:00
2025-08-31 08:04:46 +08:00
// 检查是否点击了空白区域或其他非图片元素
var hitTest = e . OriginalSource ;
if ( ! ( hitTest is Image ) & & ! ( hitTest is MediaElement ) )
2025-07-30 14:12:10 +08:00
{
2025-08-31 08:04:46 +08:00
// 如果当前有选中的元素,取消选中状态
if ( currentSelectedElement ! = null )
2025-07-30 14:12:10 +08:00
{
2025-09-07 00:49:10 +08:00
// 取消选中元素
2025-08-31 08:04:46 +08:00
UnselectElement ( currentSelectedElement ) ;
currentSelectedElement = null ;
2025-09-07 13:30:46 +08:00
2025-09-07 00:49:10 +08:00
// 重置为选择模式,确保用户可以继续选择其他元素
SetCurrentToolMode ( InkCanvasEditingMode . Select ) ;
// 更新模式缓存
UpdateCurrentToolMode ( "select" ) ;
// 刷新浮动栏高光显示
SetFloatingBarHighlightPosition ( "select" ) ;
2025-07-30 14:12:10 +08:00
}
}
2025-06-12 11:21:45 +08:00
}
// 手写笔输入
private void inkCanvas_StylusDown ( object sender , StylusDownEventArgs e )
{
2025-06-18 09:08:38 +08:00
// 使用辅助方法设置光标
2025-07-19 10:56:30 +08:00
SetCursorBasedOnEditingMode ( sender as InkCanvas ) ;
2025-06-12 11:21:45 +08:00
}
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" ) ;
2025-07-26 14:29:24 +08:00
// 刷新设备信息
RefreshDeviceInfo ( ) ;
2025-06-18 09:08:38 +08:00
}
2025-10-06 14:11:32 +08:00
// 个性化设置
2025-06-18 09:08:38 +08:00
private void NavTheme_Click ( object sender , RoutedEventArgs e )
{
// 切换到个性化设置页面
ShowSettingsSection ( "theme" ) ;
}
2025-10-06 14:11:32 +08:00
// 快捷键设置
2025-06-18 09:08:38 +08:00
private void NavShortcuts_Click ( object sender , RoutedEventArgs e )
{
2025-08-23 21:39:00 +08:00
OpenHotkeySettingsWindow ( ) ;
2025-06-18 09:08:38 +08:00
}
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
}
2025-07-26 14:29:24 +08:00
/// <summary>
/// 刷新设备信息按钮点击事件
/// </summary>
private void RefreshDeviceInfo_Click ( object sender , RoutedEventArgs e )
{
RefreshDeviceInfo ( ) ;
}
/// <summary>
/// 刷新设备信息显示
/// </summary>
private void RefreshDeviceInfo ( )
{
try
{
// 获取设备ID
string deviceId = DeviceIdentifier . GetDeviceId ( ) ;
DeviceIdTextBlock . Text = deviceId ;
// 获取使用频率
var usageFrequency = DeviceIdentifier . GetUsageFrequency ( ) ;
string frequencyText ;
switch ( usageFrequency )
{
case DeviceIdentifier . UsageFrequency . High :
frequencyText = "高频用户" ;
break ;
case DeviceIdentifier . UsageFrequency . Medium :
frequencyText = "中频用户" ;
break ;
case DeviceIdentifier . UsageFrequency . Low :
frequencyText = "低频用户" ;
break ;
default :
frequencyText = "未知" ;
break ;
}
UsageFrequencyTextBlock . Text = frequencyText ;
// 获取更新优先级
var updatePriority = DeviceIdentifier . GetUpdatePriority ( ) ;
string priorityText ;
switch ( updatePriority )
{
case DeviceIdentifier . UpdatePriority . High :
priorityText = "高优先级(优先推送更新)" ;
break ;
case DeviceIdentifier . UpdatePriority . Medium :
priorityText = "中优先级(正常推送更新)" ;
break ;
case DeviceIdentifier . UpdatePriority . Low :
priorityText = "低优先级(延迟推送更新)" ;
break ;
default :
priorityText = "未知" ;
break ;
}
UpdatePriorityTextBlock . Text = priorityText ;
2025-07-28 11:35:47 +08:00
// 获取使用统计(秒级精度)
var ( launchCount , totalSeconds , avgSessionSeconds , _ ) = DeviceIdentifier . GetUsageStats ( ) ;
2025-07-26 14:29:24 +08:00
LaunchCountTextBlock . Text = launchCount . ToString ( ) ;
2025-07-28 11:35:47 +08:00
// 使用新的格式化方法显示秒级精度的使用时长
string totalUsageText = DeviceIdentifier . FormatDuration ( totalSeconds ) ;
2025-07-26 14:29:24 +08:00
TotalUsageTextBlock . Text = totalUsageText ;
LogHelper . WriteLogToFile ( $"MainWindow | 设备信息已刷新 - ID: {deviceId}, 频率: {frequencyText}, 优先级: {priorityText}" ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"MainWindow | 刷新设备信息失败: {ex.Message}" , LogHelper . LogType . Error ) ;
2025-08-03 16:46:33 +08:00
2025-07-26 14:29:24 +08:00
// 显示错误信息
DeviceIdTextBlock . Text = "获取失败" ;
UsageFrequencyTextBlock . Text = "获取失败" ;
UpdatePriorityTextBlock . Text = "获取失败" ;
LaunchCountTextBlock . Text = "获取失败" ;
TotalUsageTextBlock . Text = "获取失败" ;
}
}
2025-10-06 14:11:32 +08:00
// 折叠侧边栏
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-08-03 16:46:33 +08:00
2025-10-06 14:11:32 +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-10-06 14:11:32 +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-07-28 14:40:44 +08:00
BorderSettingsMask . Background = new SolidColorBrush ( Color . FromArgb ( 1 , 0 , 0 , 0 ) ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 09:08:38 +08:00
// 获取SettingsPanelScrollViewer中的所有GroupBox
2025-09-30 19:15:03 +08:00
var stackPanel = SettingsPanelScrollViewer . Content as StackPanel ;
if ( stackPanel = = null ) return ;
2025-08-03 16:46:33 +08:00
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-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 确保UI完全更新
2025-08-03 16:46:33 +08:00
await Dispatcher . InvokeAsync ( ( ) = > { } , DispatcherPriority . Render ) ;
2025-07-15 21:10:49 +08:00
// 根据传入的sectionTag滚动到相应的设置部分
GroupBox targetGroupBox = null ;
2025-08-03 16:46:33 +08:00
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 ;
2025-07-16 13:35:17 +08:00
case "plugins" :
targetGroupBox = GroupBoxPlugins ;
break ;
2025-06-18 09:08:38 +08:00
default :
2025-07-15 21:10:49 +08:00
// 默认滚动到顶部
SettingsPanelScrollViewer . ScrollToTop ( ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 如果找到目标GroupBox,则滚动到它的位置
if ( targetGroupBox ! = null )
{
// 使用动画平滑滚动到目标位置
ScrollToElement ( targetGroupBox ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 高亮显示当前选中的导航项
UpdateNavigationButtonState ( sectionTag ) ;
}
else
{
// 如果没有找到目标GroupBox,则滚动到顶部
SettingsPanelScrollViewer . ScrollToTop ( ) ;
}
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 根据Header文本查找GroupBox
private GroupBox FindGroupBoxByHeader ( StackPanel parent , string headerText )
{
foreach ( var child in parent . Children )
{
if ( child is GroupBox groupBox )
{
// 查找GroupBox的Header
2025-08-03 16:46:33 +08:00
if ( groupBox . Header is TextBlock headerTextBlock & &
headerTextBlock . Text ! = null & &
2025-07-15 21:10:49 +08:00
headerTextBlock . Text . Contains ( headerText ) )
{
return groupBox ;
}
}
}
return null ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 平滑滚动到指定元素
private async void ScrollToElement ( FrameworkElement element )
{
if ( element = = null | | SettingsPanelScrollViewer = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
try
{
// 暂时禁用滚动事件处理
SettingsPanelScrollViewer . ScrollChanged - = SettingsPanelScrollViewer_ScrollChanged ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 记录当前滚动位置
double originalOffset = SettingsPanelScrollViewer . VerticalOffset ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 将ScrollViewer内部的位置信息重置到顶部(不会触发视觉更新)
SettingsPanelScrollViewer . ScrollToHome ( ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 使用Dispatcher进行延迟处理,确保布局更新
2025-08-03 16:46:33 +08:00
await Dispatcher . InvokeAsync ( ( ) = >
{
2025-07-15 21:10:49 +08:00
try
{
// 强制更新布局
SettingsPanelScrollViewer . UpdateLayout ( ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 获取元素相对于顶部的准确位置
Point elementPosition = element . TransformToAncestor ( SettingsPanelScrollViewer ) . Transform ( new Point ( 0 , 0 ) ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 计算目标位置,减去一些偏移,使元素不会贴在顶部
double targetPosition = elementPosition . Y - 20 ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 确保目标位置不小于0
targetPosition = Math . Max ( 0 , targetPosition ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 直接设置滚动位置,不使用动画
SettingsPanelScrollViewer . ScrollToVerticalOffset ( targetPosition ) ;
}
2025-08-31 01:47:00 +08:00
catch ( Exception )
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 ;
}
2025-07-28 14:40:44 +08:00
} , DispatcherPriority . Render ) ;
2025-07-15 21:10:49 +08:00
}
catch ( Exception )
{
// 确保在异常情况下也重新启用滚动事件处理
SettingsPanelScrollViewer . ScrollChanged + = SettingsPanelScrollViewer_ScrollChanged ;
}
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 滚动条变化事件处理
2025-07-28 14:40:44 +08:00
private void SettingsPanelScrollViewer_ScrollChanged ( object sender , ScrollChangedEventArgs e )
2025-07-15 21:10:49 +08:00
{
// 可以在这里添加滚动事件的处理逻辑,如果需要的话
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 更新导航按钮状态
private void UpdateNavigationButtonState ( string activeTag )
{
// 清除所有导航按钮的Tag属性
ClearAllNavButtonTags ( ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 设置当前活动按钮的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 ;
2025-07-16 13:35:17 +08:00
case "plugins" :
SetNavButtonTag ( "plugins" ) ;
break ;
2025-07-15 21:10:49 +08:00
}
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 清除所有导航按钮的Tag属性
private void ClearAllNavButtonTags ( )
{
2025-09-30 19:15:03 +08:00
var grid = BorderSettings . Child as Grid ;
if ( grid = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var navSidebar = grid . Children [ 0 ] as Border ;
if ( navSidebar = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var navGrid = navSidebar . Child as Grid ;
if ( navGrid = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var scrollViewer = navGrid . Children [ 1 ] as ScrollViewer ;
if ( scrollViewer = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var stackPanel = scrollViewer . Content as StackPanel ;
if ( stackPanel = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
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
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 设置导航按钮的Tag属性
private void SetNavButtonTag ( string tag )
{
2025-09-30 19:15:03 +08:00
var grid = BorderSettings . Child as Grid ;
if ( grid = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var navSidebar = grid . Children [ 0 ] as Border ;
if ( navSidebar = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var navGrid = navSidebar . Child as Grid ;
if ( navGrid = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var scrollViewer = navGrid . Children [ 1 ] as ScrollViewer ;
if ( scrollViewer = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var stackPanel = scrollViewer . Content as StackPanel ;
if ( stackPanel = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
foreach ( var child in stackPanel . Children )
{
if ( child is Button button )
{
// 检查按钮的ToolTip属性,根据tag设置对应的按钮
2025-09-30 19:15:03 +08:00
string buttonTag = button . Tag as string ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 如果按钮的Tag与要设置的tag匹配,则设置Tag
2025-09-30 19:15:03 +08:00
if ( buttonTag ! = null & & buttonTag . ToLower ( ) = = tag . ToLower ( ) )
2025-07-15 21:10:49 +08:00
{
button . Tag = tag ;
return ;
}
}
}
2025-06-18 09:08:38 +08:00
}
2025-08-03 16:46:33 +08:00
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
2025-08-03 16:46:33 +08:00
if ( groupBox . Header is TextBlock headerTextBlock & &
headerTextBlock . Text ! = null & &
2025-06-18 09:08:38 +08:00
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-07-16 13:35:17 +08:00
2025-08-09 13:16:22 +08:00
#region 插 件 ? ? ?
2025-07-16 13:35:17 +08:00
// 添加插件系统初始化方法
private void InitializePluginSystem ( )
{
try
{
// 初始化插件管理器
2025-07-28 14:40:44 +08:00
PluginManager . Instance . Initialize ( ) ;
LogHelper . WriteLogToFile ( "插件系统已初始化" ) ;
2025-07-16 13:35:17 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"初始化插件系统时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
// 添加插件管理导航点击事件处理
private void NavPlugins_Click ( object sender , RoutedEventArgs e )
{
ShowSettingsSection ( "plugins" ) ;
}
// 添加打开插件管理器按钮点击事件
private void BtnOpenPluginManager_Click ( object sender , RoutedEventArgs e )
{
try
{
// 暂时隐藏设置面板
BorderSettings . Visibility = Visibility . Hidden ;
BorderSettingsMask . Visibility = Visibility . Hidden ;
2025-08-03 16:46:33 +08:00
2025-07-16 13:35:17 +08:00
// 创建并显示插件设置窗口
2025-07-28 14:40:44 +08:00
PluginSettingsWindow pluginSettingsWindow = new PluginSettingsWindow ( ) ;
2025-08-03 16:46:33 +08:00
2025-07-16 13:35:17 +08:00
// 设置窗口关闭事件,用于在插件管理窗口关闭后恢复设置面板
pluginSettingsWindow . Closed + = ( s , args ) = >
{
// 恢复设置面板显示
BorderSettings . Visibility = Visibility . Visible ;
BorderSettingsMask . Visibility = Visibility . Visible ;
} ;
2025-08-03 16:46:33 +08:00
2025-07-16 13:35:17 +08:00
// 显示插件设置窗口
pluginSettingsWindow . ShowDialog ( ) ;
}
catch ( Exception ex )
{
// 确保在发生错误时也恢复设置面板显示
BorderSettings . Visibility = Visibility . Visible ;
BorderSettingsMask . Visibility = Visibility . Visible ;
2025-08-03 16:46:33 +08:00
2025-07-16 13:35:17 +08:00
LogHelper . WriteLogToFile ( $"打开插件管理器时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
MessageBox . Show ( $"打开插件管理器时出错: {ex.Message}" , "错误" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
}
}
2025-08-09 13:16:22 +08:00
#endregion 插 件 ? ? ?
2025-07-18 16:12:04 +08:00
2025-08-31 15:35:28 +08:00
#region 新 设 置 窗 口
// 添加打开新设置窗口按钮点击事件
private void BtnOpenNewSettings_Click ( object sender , RoutedEventArgs e )
{
if ( isOpeningOrHidingSettingsPane ) return ;
HideSubPanels ( ) ;
{
var settingsWindow = new SettingsWindow ( ) ;
settingsWindow . Owner = this ;
settingsWindow . ShowDialog ( ) ;
}
}
#endregion 新 设 置 窗 口
2025-07-18 16:12:04 +08:00
// 在MainWindow类中添加:
private void ApplyCurrentEraserShape ( )
{
double k = 1 ;
switch ( Settings . Canvas . EraserSize )
{
case 0 :
k = Settings . Canvas . EraserShapeType = = 0 ? 0.5 : 0.7 ;
break ;
case 1 :
k = Settings . Canvas . EraserShapeType = = 0 ? 0.8 : 0.9 ;
break ;
case 3 :
k = Settings . Canvas . EraserShapeType = = 0 ? 1.25 : 1.2 ;
break ;
case 4 :
k = Settings . Canvas . EraserShapeType = = 0 ? 1.5 : 1.3 ;
break ;
}
if ( Settings . Canvas . EraserShapeType = = 0 )
{
inkCanvas . EraserShape = new EllipseStylusShape ( k * 90 , k * 90 ) ;
}
else if ( Settings . Canvas . EraserShapeType = = 1 )
{
inkCanvas . EraserShape = new RectangleStylusShape ( k * 90 * 0.6 , k * 90 ) ;
}
}
2025-07-18 19:20:06 +08:00
// 显示指定页
private void ShowPage ( int index )
{
if ( index < 0 | | index > = whiteboardPages . Count ) return ;
// 只切换可见性
for ( int i = 0 ; i < whiteboardPages . Count ; i + + )
{
2025-07-28 14:40:44 +08:00
whiteboardPages [ i ] . Visibility = ( i = = index ) ? Visibility . Visible : Visibility . Collapsed ;
2025-07-18 19:20:06 +08:00
}
currentCanvas = whiteboardPages [ index ] ;
currentPageIndex = index ;
}
// 新建页面
private void AddNewPage ( )
{
var newCanvas = new System . Windows . Controls . Canvas ( ) ;
whiteboardPages . Add ( newCanvas ) ;
InkCanvasGridForInkReplay . Children . Add ( newCanvas ) ;
ShowPage ( whiteboardPages . Count - 1 ) ;
}
// 删除当前页面
private void DeleteCurrentPage ( )
{
if ( whiteboardPages . Count < = 1 ) return ;
InkCanvasGridForInkReplay . Children . Remove ( currentCanvas ) ;
whiteboardPages . RemoveAt ( currentPageIndex ) ;
if ( currentPageIndex > = whiteboardPages . Count )
currentPageIndex = whiteboardPages . Count - 1 ;
ShowPage ( currentPageIndex ) ;
}
2025-07-21 16:43:29 +08:00
// 快速面板退出PPT放映按钮事件
private void ExitPPTSlideShow_MouseUp ( object sender , MouseButtonEventArgs e )
{
// 直接调用PPT放映结束按钮的逻辑
BtnPPTSlideShowEnd_Click ( BtnPPTSlideShowEnd , null ) ;
}
2025-07-22 17:36:02 +08:00
private void HistoryRollbackButton_Click ( object sender , RoutedEventArgs e )
{
// 收起设置面板(与插件面板一致)
BorderSettings . Visibility = Visibility . Hidden ;
BorderSettingsMask . Visibility = Visibility . Hidden ;
var win = new HistoryRollbackWindow ( Settings . Startup . UpdateChannel ) ;
win . ShowDialog ( ) ;
// 可选:回滚窗口关闭后恢复设置面板显示
BorderSettings . Visibility = Visibility . Visible ;
BorderSettingsMask . Visibility = Visibility . Visible ;
}
2025-07-25 18:21:16 +08:00
[DllImport("user32.dll")]
private static extern int SetWindowLong ( IntPtr hWnd , int nIndex , int dwNewLong ) ;
[DllImport("user32.dll")]
private static extern int GetWindowLong ( IntPtr hWnd , int nIndex ) ;
2025-08-13 11:54:36 +08:00
[DllImport("user32.dll")]
private static extern bool SetWindowPos ( IntPtr hWnd , IntPtr hWndInsertAfter , int X , int Y , int cx , int cy , uint uFlags ) ;
2025-08-13 12:52:56 +08:00
[DllImport("user32.dll")]
private static extern bool BringWindowToTop ( IntPtr hWnd ) ;
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow ( IntPtr hWnd ) ;
2025-08-24 11:08:13 +08:00
[DllImport("user32.dll")]
private static extern bool PostMessage ( IntPtr hWnd , uint Msg , IntPtr wParam , IntPtr lParam ) ;
2025-08-30 16:25:10 +08:00
[DllImport("kernel32.dll")]
private static extern uint GetCurrentProcessId ( ) ;
2025-12-20 13:56:46 +08:00
2025-11-15 20:24:28 +08:00
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx ( int idHook , LowLevelKeyboardProc lpfn , IntPtr hMod , uint dwThreadId ) ;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx ( IntPtr hhk ) ;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx ( IntPtr hhk , int nCode , IntPtr wParam , IntPtr lParam ) ;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle ( string lpModuleName ) ;
private const int WH_KEYBOARD_LL = 13 ;
private const int WM_KEYDOWN = 0x0100 ;
private const int WM_KEYUP = 0x0101 ;
private const int WM_SYSKEYDOWN = 0x0104 ;
private const int WM_SYSKEYUP = 0x0105 ;
private delegate IntPtr LowLevelKeyboardProc ( int nCode , IntPtr wParam , IntPtr lParam ) ;
[StructLayout(LayoutKind.Sequential)]
private struct KBDLLHOOKSTRUCT
{
public uint vkCode ;
public uint scanCode ;
public uint flags ;
public uint time ;
public IntPtr dwExtraInfo ;
}
2025-08-13 12:52:56 +08:00
2025-11-15 20:24:28 +08:00
private LowLevelKeyboardProc _keyboardProc ;
private IntPtr _keyboardHookId = IntPtr . Zero ;
2025-08-31 11:43:52 +08:00
2025-07-25 18:21:16 +08:00
private const int GWL_EXSTYLE = - 20 ;
private const int WS_EX_NOACTIVATE = 0x08000000 ;
2025-08-13 12:52:56 +08:00
private const int WS_EX_TOPMOST = 0x00000008 ;
2025-08-13 11:54:36 +08:00
private static readonly IntPtr HWND_TOPMOST = new IntPtr ( - 1 ) ;
private static readonly IntPtr HWND_NOTOPMOST = new IntPtr ( - 2 ) ;
private const uint SWP_NOMOVE = 0x0002 ;
private const uint SWP_NOSIZE = 0x0001 ;
private const uint SWP_NOACTIVATE = 0x0010 ;
2025-08-13 12:52:56 +08:00
private const uint SWP_SHOWWINDOW = 0x0040 ;
private const uint SWP_NOOWNERZORDER = 0x0200 ;
// 添加定时器来维护置顶状态
private DispatcherTimer topmostMaintenanceTimer ;
2025-11-01 20:42:18 +08:00
private DispatcherTimer autoSaveStrokesTimer ;
2025-08-31 09:54:13 +08:00
private bool isTopmostMaintenanceEnabled ;
2025-07-25 18:21:16 +08:00
2025-11-15 20:24:28 +08:00
private IntPtr KeyboardHookProc ( int nCode , IntPtr wParam , IntPtr lParam )
{
if ( nCode > = 0 )
{
2025-12-20 13:56:46 +08:00
if ( Settings . Advanced . IsNoFocusMode & &
BtnPPTSlideShowEnd . Visibility = = Visibility . Visible & &
2025-11-15 20:24:28 +08:00
currentMode = = 0 )
{
KBDLLHOOKSTRUCT hookStruct = ( KBDLLHOOKSTRUCT ) Marshal . PtrToStructure ( lParam , typeof ( KBDLLHOOKSTRUCT ) ) ;
uint vkCode = hookStruct . vkCode ;
2025-12-20 13:56:46 +08:00
2025-11-15 20:24:28 +08:00
if ( wParam = = ( IntPtr ) WM_KEYDOWN | | wParam = = ( IntPtr ) WM_SYSKEYDOWN )
{
2025-12-20 13:56:46 +08:00
if ( vkCode = = 0x22 | | vkCode = = 0x28 | | vkCode = = 0x27 | |
2025-11-15 20:24:28 +08:00
vkCode = = 0x4E | | vkCode = = 0x20 )
{
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
BtnPPTSlidesDown_Click ( null , null ) ;
} ) , DispatcherPriority . Normal ) ;
return ( IntPtr ) 1 ;
}
2025-12-20 13:56:46 +08:00
else if ( vkCode = = 0x21 | | vkCode = = 0x26 | | vkCode = = 0x25 | |
2025-11-15 20:24:28 +08:00
vkCode = = 0x50 )
{
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
BtnPPTSlidesUp_Click ( null , null ) ;
} ) , DispatcherPriority . Normal ) ;
return ( IntPtr ) 1 ;
}
}
}
}
return CallNextHookEx ( _keyboardHookId , nCode , wParam , lParam ) ;
}
private void InstallKeyboardHook ( )
{
if ( _keyboardHookId = = IntPtr . Zero )
{
_keyboardProc = KeyboardHookProc ;
_keyboardHookId = SetWindowsHookEx ( WH_KEYBOARD_LL , _keyboardProc ,
GetModuleHandle ( null ) , 0 ) ;
if ( _keyboardHookId = = IntPtr . Zero )
{
LogHelper . WriteLogToFile ( "安装低级键盘钩子失败" , LogHelper . LogType . Error ) ;
}
}
}
private void UninstallKeyboardHook ( )
{
if ( _keyboardHookId ! = IntPtr . Zero )
{
UnhookWindowsHookEx ( _keyboardHookId ) ;
_keyboardHookId = IntPtr . Zero ;
_keyboardProc = null ;
}
}
2025-07-25 18:21:16 +08:00
private void ApplyNoFocusMode ( )
{
var hwnd = new WindowInteropHelper ( this ) . Handle ;
int exStyle = GetWindowLong ( hwnd , GWL_EXSTYLE ) ;
2025-12-20 13:56:46 +08:00
bool shouldBeNoFocus = isTemporarilyDisablingNoFocusMode ?
2025-10-18 18:29:52 +08:00
false : Settings . Advanced . IsNoFocusMode ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:29:52 +08:00
if ( shouldBeNoFocus )
2025-07-25 18:21:16 +08:00
{
SetWindowLong ( hwnd , GWL_EXSTYLE , exStyle | WS_EX_NOACTIVATE ) ;
2025-11-15 20:24:28 +08:00
InstallKeyboardHook ( ) ;
2025-07-25 18:21:16 +08:00
}
else
{
SetWindowLong ( hwnd , GWL_EXSTYLE , exStyle & ~ WS_EX_NOACTIVATE ) ;
2025-11-15 20:24:28 +08:00
UninstallKeyboardHook ( ) ;
2025-07-25 18:21:16 +08:00
}
}
2025-08-13 11:54:36 +08:00
private void ApplyAlwaysOnTop ( )
{
2025-08-13 12:52:56 +08:00
try
2025-08-13 11:54:36 +08:00
{
2025-09-06 13:48:44 +08:00
var hwnd = new WindowInteropHelper ( this ) . Handle ;
2025-08-13 12:52:56 +08:00
if ( Settings . Advanced . IsAlwaysOnTop )
{
Topmost = true ;
2025-08-31 11:43:52 +08:00
2025-09-06 13:48:44 +08:00
// 1. 设置窗口样式为置顶
int exStyle = GetWindowLong ( hwnd , GWL_EXSTYLE ) ;
SetWindowLong ( hwnd , GWL_EXSTYLE , exStyle | WS_EX_TOPMOST ) ;
// 2. 使用SetWindowPos确保窗口在最顶层
SetWindowPos ( hwnd , HWND_TOPMOST , 0 , 0 , 0 , 0 ,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER ) ;
2025-09-06 13:16:10 +08:00
2025-10-18 20:41:09 +08:00
// 3. 如果启用了无焦点模式且未启用UIA置顶,需要特殊处理
if ( Settings . Advanced . IsNoFocusMode & & ! Settings . Advanced . EnableUIAccessTopMost )
2025-08-13 12:52:56 +08:00
{
// 启动置顶维护定时器
StartTopmostMaintenance ( ) ;
}
else
{
// 停止置顶维护定时器
StopTopmostMaintenance ( ) ;
}
}
else
{
// 取消置顶时
2025-09-06 13:48:44 +08:00
// 1. 先使用Win32 API取消置顶
SetWindowPos ( hwnd , HWND_NOTOPMOST , 0 , 0 , 0 , 0 ,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER ) ;
// 2. 移除置顶窗口样式
int exStyle = GetWindowLong ( hwnd , GWL_EXSTYLE ) ;
SetWindowLong ( hwnd , GWL_EXSTYLE , exStyle & ~ WS_EX_TOPMOST ) ;
// 3. 停止置顶维护定时器
2025-08-13 12:52:56 +08:00
StopTopmostMaintenance ( ) ;
}
2025-08-13 11:54:36 +08:00
}
2025-08-13 12:52:56 +08:00
catch ( Exception ex )
2025-08-13 11:54:36 +08:00
{
2025-08-13 12:52:56 +08:00
LogHelper . WriteLogToFile ( $"应用窗口置顶失败: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 启动置顶维护定时器
/// </summary>
private void StartTopmostMaintenance ( )
{
2025-10-18 20:41:09 +08:00
if ( Settings . Advanced . EnableUIAccessTopMost )
{
return ;
}
2025-08-13 12:52:56 +08:00
if ( isTopmostMaintenanceEnabled ) return ;
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
if ( topmostMaintenanceTimer = = null )
{
topmostMaintenanceTimer = new DispatcherTimer ( ) ;
topmostMaintenanceTimer . Interval = TimeSpan . FromMilliseconds ( 500 ) ; // 每500ms检查一次
topmostMaintenanceTimer . Tick + = TopmostMaintenanceTimer_Tick ;
}
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
topmostMaintenanceTimer . Start ( ) ;
isTopmostMaintenanceEnabled = true ;
LogHelper . WriteLogToFile ( "启动置顶维护定时器" , LogHelper . LogType . Trace ) ;
}
/// <summary>
/// 停止置顶维护定时器
/// </summary>
private void StopTopmostMaintenance ( )
{
if ( topmostMaintenanceTimer ! = null & & isTopmostMaintenanceEnabled )
{
topmostMaintenanceTimer . Stop ( ) ;
isTopmostMaintenanceEnabled = false ;
LogHelper . WriteLogToFile ( "停止置顶维护定时器" , LogHelper . LogType . Trace ) ;
}
}
2025-11-15 18:37:23 +08:00
public void PauseTopmostMaintenance ( )
{
if ( topmostMaintenanceTimer ! = null & & isTopmostMaintenanceEnabled )
{
topmostMaintenanceTimer . Stop ( ) ;
}
}
public void ResumeTopmostMaintenance ( )
{
2025-12-20 13:56:46 +08:00
if ( Settings . Advanced . IsAlwaysOnTop & &
Settings . Advanced . IsNoFocusMode & &
2025-11-15 18:37:23 +08:00
! Settings . Advanced . EnableUIAccessTopMost )
{
if ( topmostMaintenanceTimer ! = null & & ! isTopmostMaintenanceEnabled )
{
topmostMaintenanceTimer . Start ( ) ;
isTopmostMaintenanceEnabled = true ;
}
}
}
2025-08-13 12:52:56 +08:00
/// <summary>
/// 置顶维护定时器事件
/// </summary>
private void TopmostMaintenanceTimer_Tick ( object sender , EventArgs e )
{
try
{
2025-10-18 20:41:09 +08:00
if ( Settings . Advanced . EnableUIAccessTopMost )
{
StopTopmostMaintenance ( ) ;
return ;
}
2025-08-13 12:52:56 +08:00
if ( ! Settings . Advanced . IsAlwaysOnTop | | ! Settings . Advanced . IsNoFocusMode )
{
StopTopmostMaintenance ( ) ;
return ;
}
var hwnd = new WindowInteropHelper ( this ) . Handle ;
if ( hwnd = = IntPtr . Zero ) return ;
// 检查窗口是否仍然可见且不是最小化状态
if ( ! IsWindow ( hwnd ) | | ! IsWindowVisible ( hwnd ) | | IsIconic ( hwnd ) )
{
return ;
}
2025-09-06 12:38:57 +08:00
// 检查是否有子窗口在前景
2025-09-06 13:48:44 +08:00
var foregroundWindow = GetForegroundWindow ( ) ;
if ( foregroundWindow ! = hwnd )
2025-09-06 12:38:57 +08:00
{
2025-09-06 13:48:44 +08:00
// 检查前景窗口是否是当前应用程序的子窗口
var foregroundWindowProcessId = GetWindowThreadProcessId ( foregroundWindow , out uint processId ) ;
var currentProcessId = GetCurrentProcessId ( ) ;
if ( processId = = currentProcessId )
{
// 如果有子窗口在前景,暂停置顶维护
return ;
}
// 如果窗口不在最顶层且没有子窗口,重新设置置顶
SetWindowPos ( hwnd , HWND_TOPMOST , 0 , 0 , 0 , 0 ,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER ) ;
// 确保窗口样式正确
int exStyle = GetWindowLong ( hwnd , GWL_EXSTYLE ) ;
if ( ( exStyle & WS_EX_TOPMOST ) = = 0 )
{
SetWindowLong ( hwnd , GWL_EXSTYLE , exStyle | WS_EX_TOPMOST ) ;
}
2025-08-13 12:52:56 +08:00
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"置顶维护定时器出错: {ex.Message}" , LogHelper . LogType . Error ) ;
2025-08-13 11:54:36 +08:00
}
}
/// <summary>
/// 根据窗口置顶设置和当前模式设置窗口的Topmost属性
/// </summary>
/// <param name="shouldBeTopmost">当前模式是否需要窗口置顶</param>
public void SetTopmostBasedOnSettings ( bool shouldBeTopmost )
{
if ( Settings . Advanced . IsAlwaysOnTop )
{
// 如果启用了窗口置顶设置,则始终置顶
Topmost = true ;
ApplyAlwaysOnTop ( ) ;
}
else
{
// 如果未启用窗口置顶设置,则根据当前模式决定
Topmost = shouldBeTopmost ;
if ( ! shouldBeTopmost )
{
ApplyAlwaysOnTop ( ) ; // 确保取消置顶
}
}
}
2025-07-25 18:21:16 +08:00
private void ToggleSwitchNoFocusMode_Toggled ( object sender , RoutedEventArgs e )
{
if ( ! isLoaded ) return ;
var toggle = sender as ToggleSwitch ;
Settings . Advanced . IsNoFocusMode = toggle ! = null & & toggle . IsOn ;
SaveSettingsToFile ( ) ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:29:52 +08:00
if ( isTemporarilyDisablingNoFocusMode )
{
isTemporarilyDisablingNoFocusMode = false ;
}
2025-12-20 13:56:46 +08:00
2025-07-25 18:21:16 +08:00
ApplyNoFocusMode ( ) ;
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
// 如果启用了窗口置顶,需要重新应用置顶设置以处理无焦点模式的变化
if ( Settings . Advanced . IsAlwaysOnTop )
{
ApplyAlwaysOnTop ( ) ;
}
2025-09-13 10:55:27 +08:00
// 如果当前在设置面板中,标记用户已修改无焦点模式设置
if ( BorderSettings . Visibility = = Visibility . Visible )
{
userChangedNoFocusModeInSettings = true ;
}
2025-07-25 18:21:16 +08:00
}
2025-07-28 19:02:45 +08:00
2025-08-13 11:54:36 +08:00
private void ToggleSwitchAlwaysOnTop_Toggled ( object sender , RoutedEventArgs e )
{
if ( ! isLoaded ) return ;
var toggle = sender as ToggleSwitch ;
Settings . Advanced . IsAlwaysOnTop = toggle ! = null & & toggle . IsOn ;
SaveSettingsToFile ( ) ;
ApplyAlwaysOnTop ( ) ;
2025-10-18 18:26:13 +08:00
UpdateUIAccessTopMostVisibility ( ) ;
}
private void ToggleSwitchUIAccessTopMost_Toggled ( object sender , RoutedEventArgs e )
{
if ( ! isLoaded ) return ;
var toggle = sender as ToggleSwitch ;
bool newValue = toggle ! = null & & toggle . IsOn ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
Settings . Advanced . EnableUIAccessTopMost = newValue ;
SaveSettingsToFile ( ) ;
ApplyUIAccessTopMost ( ) ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
App . IsUIAccessTopMostEnabled = newValue ;
2025-12-20 13:56:46 +08:00
2025-08-13 11:54:36 +08:00
}
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
private void Window_Activated ( object sender , EventArgs e )
{
// 窗口激活时,如果启用了置顶功能,重新应用置顶设置
if ( Settings . Advanced . IsAlwaysOnTop )
{
// 使用Dispatcher.BeginInvoke确保在UI线程上执行
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
ApplyAlwaysOnTop ( ) ;
2025-08-30 19:40:14 +08:00
} ) , DispatcherPriority . Loaded ) ;
2025-08-13 12:52:56 +08:00
}
}
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
/// <summary>
/// 窗口失去焦点时的处理
/// </summary>
private void Window_Deactivated ( object sender , EventArgs e )
{
// 窗口失去焦点时,如果启用了置顶功能且处于无焦点模式,重新应用置顶设置
if ( Settings . Advanced . IsAlwaysOnTop & & Settings . Advanced . IsNoFocusMode )
{
// 使用Dispatcher.BeginInvoke确保在UI线程上执行
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
ApplyAlwaysOnTop ( ) ;
2025-08-30 19:40:14 +08:00
} ) , DispatcherPriority . Loaded ) ;
2025-08-13 12:52:56 +08:00
}
}
2025-08-13 11:54:36 +08:00
2025-07-31 13:23:33 +08:00
2025-08-23 21:39:00 +08:00
#region 全 局 快 捷 键 管 理
2025-08-23 23:13:39 +08:00
/// <summary>
/// 初始化墨迹渐隐管理器
/// </summary>
private void InitializeInkFadeManager ( )
{
try
{
// 确保墨迹渐隐管理器已初始化
if ( _inkFadeManager = = null )
{
_inkFadeManager = new InkFadeManager ( this ) ;
}
// 同步设置状态
_inkFadeManager . IsEnabled = Settings . Canvas . EnableInkFade ;
_inkFadeManager . UpdateFadeTime ( Settings . Canvas . InkFadeTime ) ;
LogHelper . WriteLogToFile ( "墨迹渐隐管理器已初始化" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"初始化墨迹渐隐管理器时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-08-23 21:39:00 +08:00
/// <summary>
/// 初始化全局快捷键管理器
/// </summary>
private void InitializeGlobalHotkeyManager ( )
{
try
{
_globalHotkeyManager = new GlobalHotkeyManager ( this ) ;
2025-08-30 11:29:16 +08:00
// 启动时加载快捷键,但默认为鼠标模式,禁用快捷键以放行键盘操作
2025-08-30 11:20:53 +08:00
_globalHotkeyManager . EnableHotkeyRegistration ( ) ;
2025-08-30 11:29:16 +08:00
// 启动时默认为鼠标模式,禁用快捷键
_globalHotkeyManager . UpdateHotkeyStateForToolMode ( true ) ;
LogHelper . WriteLogToFile ( "全局快捷键管理器已初始化,启动时默认为鼠标模式并禁用快捷键" , LogHelper . LogType . Event ) ;
2025-08-23 21:39:00 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"初始化全局快捷键管理器时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 打开快捷键设置窗口
/// </summary>
private void OpenHotkeySettingsWindow ( )
{
try
{
if ( _globalHotkeyManager = = null )
{
MessageBox . Show ( "快捷键管理器尚未初始化,请稍后重试。" , "错误" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
return ;
}
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 暂时隐藏设置面板
BorderSettings . Visibility = Visibility . Hidden ;
BorderSettingsMask . Visibility = Visibility . Hidden ;
2025-09-07 13:30:46 +08:00
2025-09-06 15:07:26 +08:00
// 创建快捷键设置窗口
2025-08-23 21:39:00 +08:00
var hotkeySettingsWindow = new HotkeySettingsWindow ( this , _globalHotkeyManager ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 设置窗口关闭事件,用于在快捷键设置窗口关闭后恢复设置面板
2025-09-06 15:07:26 +08:00
hotkeySettingsWindow . Closed + = ( s , e ) = >
{
2025-09-06 15:50:16 +08:00
// 恢复设置面板显示
BorderSettings . Visibility = Visibility . Visible ;
BorderSettingsMask . Visibility = Visibility . Visible ;
2025-09-06 15:07:26 +08:00
} ;
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 显示快捷键设置窗口
2025-08-23 21:39:00 +08:00
hotkeySettingsWindow . ShowDialog ( ) ;
}
catch ( Exception ex )
{
2025-09-06 15:50:16 +08:00
// 确保在发生错误时也恢复设置面板显示
BorderSettings . Visibility = Visibility . Visible ;
BorderSettingsMask . Visibility = Visibility . Visible ;
2025-09-07 13:30:46 +08:00
2025-08-23 21:39:00 +08:00
LogHelper . WriteLogToFile ( $"打开快捷键设置窗口时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
MessageBox . Show ( $"打开快捷键设置窗口时出错: {ex.Message}" , "错误" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
}
}
#endregion
2025-08-23 23:13:39 +08:00
#region 墨 迹 渐 隐 功 能
/// <summary>
/// 墨迹渐隐开关切换事件处理
/// </summary>
private void ToggleSwitchEnableInkFade_Toggled ( object sender , RoutedEventArgs e )
{
try
{
Settings . Canvas . EnableInkFade = ToggleSwitchEnableInkFade . IsOn ;
_inkFadeManager . IsEnabled = Settings . Canvas . EnableInkFade ;
2025-08-31 11:43:52 +08:00
2025-08-23 23:13:39 +08:00
// 同步批注子面板中的开关状态
if ( ToggleSwitchInkFadeInPanel ! = null )
{
ToggleSwitchInkFadeInPanel . IsOn = Settings . Canvas . EnableInkFade ;
}
2025-08-24 09:09:48 +08:00
// 同步普通画笔面板中的开关状态
if ( ToggleSwitchInkFadeInPanel2 ! = null )
{
ToggleSwitchInkFadeInPanel2 . IsOn = Settings . Canvas . EnableInkFade ;
}
2025-08-31 11:43:52 +08:00
2025-08-23 23:13:39 +08:00
LogHelper . WriteLogToFile ( $"墨迹渐隐功能已{(Settings.Canvas.EnableInkFade ? " 启 用 " : " 禁 用 ")}" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换墨迹渐隐功能时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 墨迹渐隐时间滑块值改变事件处理
/// </summary>
private void InkFadeTimeSlider_ValueChanged ( object sender , RoutedPropertyChangedEventArgs < double > e )
{
try
{
Settings . Canvas . InkFadeTime = ( int ) e . NewValue ;
2025-08-31 01:00:59 +08:00
if ( _inkFadeManager ! = null )
{
_inkFadeManager . UpdateFadeTime ( Settings . Canvas . InkFadeTime ) ;
}
2025-08-23 23:13:39 +08:00
LogHelper . WriteLogToFile ( $"墨迹渐隐时间已更新为 {Settings.Canvas.InkFadeTime}ms" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新墨迹渐隐时间时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 批注子面板中墨迹渐隐开关切换事件处理
/// </summary>
private void ToggleSwitchInkFadeInPanel_Toggled ( object sender , RoutedEventArgs e )
{
try
{
Settings . Canvas . EnableInkFade = ToggleSwitchInkFadeInPanel . IsOn ;
_inkFadeManager . IsEnabled = Settings . Canvas . EnableInkFade ;
2025-08-31 11:43:52 +08:00
2025-08-23 23:13:39 +08:00
// 同步设置面板中的开关状态
if ( ToggleSwitchEnableInkFade ! = null )
{
ToggleSwitchEnableInkFade . IsOn = Settings . Canvas . EnableInkFade ;
}
2025-08-24 09:09:48 +08:00
// 同步普通画笔面板中的开关状态
if ( ToggleSwitchInkFadeInPanel2 ! = null )
{
ToggleSwitchInkFadeInPanel2 . IsOn = Settings . Canvas . EnableInkFade ;
}
2025-08-31 11:43:52 +08:00
2025-08-23 23:13:39 +08:00
LogHelper . WriteLogToFile ( $"批注子面板中墨迹渐隐功能已{(Settings.Canvas.EnableInkFade ? " 启 用 " : " 禁 用 ")}" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"批注子面板中切换墨迹渐隐功能时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-09-06 19:27:17 +08:00
/// <summary>
/// PPT放映模式显示手势按钮开关切换事件处理
/// </summary>
private void ToggleSwitchShowGestureButtonInSlideShow_Toggled ( object sender , RoutedEventArgs e )
{
try
{
if ( ! isLoaded ) return ;
var toggle = sender as ToggleSwitch ;
Settings . PowerPointSettings . ShowGestureButtonInSlideShow = toggle ! = null & & toggle . IsOn ;
SaveSettingsToFile ( ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 19:27:17 +08:00
// 如果当前在PPT放映模式,需要立即更新手势按钮的显示状态
if ( BtnPPTSlideShowEnd . Visibility = = Visibility . Visible )
{
UpdateGestureButtonVisibilityInPPTMode ( ) ;
}
2025-09-07 13:30:46 +08:00
2025-09-06 19:27:17 +08:00
LogHelper . WriteLogToFile ( $"PPT放映模式显示手势按钮已{(Settings.PowerPointSettings.ShowGestureButtonInSlideShow ? " 启 用 " : " 禁 用 ")}" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换PPT放映模式显示手势按钮时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 更新PPT模式下手势按钮的显示状态
/// </summary>
private void UpdateGestureButtonVisibilityInPPTMode ( )
{
try
{
if ( Settings . PowerPointSettings . ShowGestureButtonInSlideShow )
{
2025-09-07 00:20:48 +08:00
// 如果启用了PPT放映模式显示手势按钮,则检查是否在批注模式下显示手势按钮
2025-09-06 19:27:17 +08:00
CheckEnableTwoFingerGestureBtnVisibility ( true ) ;
}
else
{
// 如果禁用了PPT放映模式显示手势按钮,则隐藏手势按钮
EnableTwoFingerGestureBorder . Visibility = Visibility . Collapsed ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新PPT模式下手势按钮显示状态时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-08-23 23:13:39 +08:00
#endregion
2025-08-24 11:08:13 +08:00
2025-08-30 11:20:53 +08:00
2025-08-31 12:03:58 +08:00
/// <summary>
/// 初始化文件关联状态显示
/// </summary>
private void InitializeFileAssociationStatus ( )
{
try
{
bool isRegistered = FileAssociationManager . IsFileAssociationRegistered ( ) ;
if ( isRegistered )
{
TextBlockFileAssociationStatus . Text = "✓ .icstk文件关联已注册" ;
TextBlockFileAssociationStatus . Foreground = new SolidColorBrush ( Colors . LightGreen ) ;
}
else
{
TextBlockFileAssociationStatus . Text = "✗ .icstk文件关联未注册" ;
TextBlockFileAssociationStatus . Foreground = new SolidColorBrush ( Colors . LightCoral ) ;
}
}
catch ( Exception ex )
{
TextBlockFileAssociationStatus . Text = "✗ 检查文件关联状态时出错" ;
TextBlockFileAssociationStatus . Foreground = new SolidColorBrush ( Colors . LightCoral ) ;
LogHelper . WriteLogToFile ( $"初始化文件关联状态显示时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-08-31 11:49:00 +08:00
/// <summary>
/// 处理命令行参数中的文件路径
/// </summary>
private void HandleCommandLineFileOpen ( )
{
try
{
// 检查启动参数中是否有.icstk文件
string icstkFile = FileAssociationManager . GetIcstkFileFromArgs ( App . StartArgs ) ;
2025-09-07 13:30:46 +08:00
2025-08-31 11:49:00 +08:00
if ( ! string . IsNullOrEmpty ( icstkFile ) )
{
LogHelper . WriteLogToFile ( $"检测到命令行参数中的.icstk文件: {icstkFile}" , LogHelper . LogType . Event ) ;
2025-09-07 13:30:46 +08:00
2025-08-31 11:49:00 +08:00
// 延迟执行,确保UI已完全加载
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
try
{
// 打开文件
OpenSingleStrokeFile ( icstkFile ) ;
ShowNotification ( $"已加载墨迹文件: {Path.GetFileName(icstkFile)}" ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"打开命令行参数中的文件失败: {ex.Message}" , LogHelper . LogType . Error ) ;
ShowNotification ( "打开墨迹文件失败" ) ;
}
} ) , DispatcherPriority . Loaded ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"处理命令行文件打开时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-08-30 11:20:53 +08:00
/// <summary>
/// 集中管理工具模式切换和快捷键状态更新
/// 避免在每个工具按钮点击时重复刷新快捷键状态
/// </summary>
/// <param name="newMode">新的编辑模式</param>
/// <param name="additionalActions">可选的额外操作委托</param>
internal void SetCurrentToolMode ( InkCanvasEditingMode newMode , Action additionalActions = null )
{
try
{
// 执行模式切换
inkCanvas . EditingMode = newMode ;
2025-08-31 11:43:52 +08:00
2025-08-30 11:20:53 +08:00
// 根据模式确定是否为鼠标模式(无工具模式)
bool isMouseMode = newMode = = InkCanvasEditingMode . None ;
2025-08-31 11:43:52 +08:00
2025-08-30 11:20:53 +08:00
// 更新快捷键状态
if ( _globalHotkeyManager ! = null )
{
_globalHotkeyManager . UpdateHotkeyStateForToolMode ( isMouseMode ) ;
}
2025-09-07 00:20:48 +08:00
// 在PPT放映模式下,工具模式切换时需要更新手势按钮的显示状态
if ( BtnPPTSlideShowEnd . Visibility = = Visibility . Visible )
{
UpdateGestureButtonVisibilityInPPTMode ( ) ;
}
2025-08-31 11:43:52 +08:00
2025-08-30 11:20:53 +08:00
// 执行额外的操作(如果有)
additionalActions ? . Invoke ( ) ;
2025-08-31 11:43:52 +08:00
2025-08-30 11:20:53 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"设置工具模式时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-09-06 13:16:10 +08:00
#region 滑 块 触 摸 支 持
/// <summary>
/// 为所有滑块控件添加触摸和手写笔事件支持
/// </summary>
private void AddTouchSupportToSliders ( )
{
try
{
// 获取所有滑块控件并添加触摸支持
var sliders = new List < Slider >
{
InkFadeTimeSlider ,
AutoStraightenLineThresholdSlider ,
LineStraightenSensitivitySlider ,
LineEndpointSnappingThresholdSlider ,
ViewboxFloatingBarScaleTransformValueSlider ,
ViewboxFloatingBarOpacityValueSlider ,
ViewboxFloatingBarOpacityInPPTValueSlider ,
PPTButtonLeftPositionValueSlider ,
PPTButtonRightPositionValueSlider ,
PPTButtonLBPositionValueSlider ,
PPTButtonRBPositionValueSlider ,
2025-12-20 22:56:13 +08:00
PPTLSButtonOpacityValueSlider ,
PPTRSButtonOpacityValueSlider ,
PPTLBButtonOpacityValueSlider ,
PPTRBButtonOpacityValueSlider ,
2025-09-06 13:16:10 +08:00
TouchMultiplierSlider ,
NibModeBoundsWidthSlider ,
FingerModeBoundsWidthSlider ,
SideControlMinimumAutomationSlider ,
RandWindowOnceCloseLatencySlider ,
RandWindowOnceMaxStudentsSlider ,
2025-10-06 20:44:09 +08:00
TimerVolumeSlider ,
2025-10-12 17:44:44 +08:00
ProgressiveReminderVolumeSlider ,
2025-09-06 13:16:10 +08:00
BoardInkWidthSlider ,
BoardInkAlphaSlider ,
BoardHighlighterWidthSlider ,
InkWidthSlider ,
InkAlphaSlider ,
2025-10-26 00:21:10 +08:00
HighlighterWidthSlider ,
MLAvoidanceHistorySlider ,
MLAvoidanceWeightSlider
2025-09-06 13:16:10 +08:00
} ;
foreach ( var slider in sliders )
{
if ( slider ! = null )
{
AddTouchSupportToSlider ( slider ) ;
}
}
LogHelper . WriteLogToFile ( "已为所有滑块控件添加触摸支持" , LogHelper . LogType . Trace ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"添加滑块触摸支持时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 为单个滑块控件添加触摸和手写笔事件支持
/// </summary>
/// <param name="slider">要添加触摸支持的滑块控件</param>
private void AddTouchSupportToSlider ( Slider slider )
{
if ( slider = = null ) return ;
// 启用触摸和手写笔支持
slider . IsManipulationEnabled = true ;
2025-09-06 13:30:32 +08:00
// 添加触摸事件 - 使用更简单直接的方法
slider . TouchDown + = ( s , e ) = > HandleSliderTouch ( s , e , slider ) ;
slider . TouchMove + = ( s , e ) = > HandleSliderTouch ( s , e , slider ) ;
slider . TouchUp + = ( s , e ) = > HandleSliderTouchEnd ( s , e , slider ) ;
2025-09-06 13:16:10 +08:00
// 添加手写笔事件
2025-09-06 13:30:32 +08:00
slider . StylusDown + = ( s , e ) = > HandleSliderStylus ( s , e , slider ) ;
slider . StylusMove + = ( s , e ) = > HandleSliderStylus ( s , e , slider ) ;
slider . StylusUp + = ( s , e ) = > HandleSliderStylusEnd ( s , e , slider ) ;
2025-09-06 13:16:10 +08:00
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 处理滑块触摸事件(按下和移动)
2025-09-06 13:16:10 +08:00
/// </summary>
2025-09-06 13:30:32 +08:00
private void HandleSliderTouch ( object sender , TouchEventArgs e , Slider slider )
2025-09-06 13:16:10 +08:00
{
if ( slider = = null ) return ;
// 捕获触摸设备
2025-09-07 00:11:22 +08:00
if ( e . RoutedEvent = = TouchDownEvent )
2025-09-06 13:30:32 +08:00
{
slider . CaptureTouch ( e . TouchDevice ) ;
}
2025-09-06 13:16:10 +08:00
// 计算触摸位置对应的滑块值
var touchPoint = e . GetTouchPoint ( slider ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:30:32 +08:00
// 使用更精确的位置计算方法
UpdateSliderValueFromPositionImproved ( slider , touchPoint . Position ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
e . Handled = true ;
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 处理滑块触摸结束事件
2025-09-06 13:16:10 +08:00
/// </summary>
2025-09-06 13:30:32 +08:00
private void HandleSliderTouchEnd ( object sender , TouchEventArgs e , Slider slider )
2025-09-06 13:16:10 +08:00
{
if ( slider = = null ) return ;
// 释放触摸捕获
slider . ReleaseTouchCapture ( e . TouchDevice ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
e . Handled = true ;
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 处理滑块手写笔事件(按下和移动)
2025-09-06 13:16:10 +08:00
/// </summary>
2025-09-06 13:30:32 +08:00
private void HandleSliderStylus ( object sender , StylusEventArgs e , Slider slider )
2025-09-06 13:16:10 +08:00
{
if ( slider = = null ) return ;
// 捕获手写笔设备
2025-09-07 00:11:22 +08:00
if ( e . RoutedEvent = = StylusDownEvent )
2025-09-06 13:16:10 +08:00
{
2025-09-06 13:30:32 +08:00
slider . CaptureStylus ( ) ;
2025-09-06 13:16:10 +08:00
}
// 计算手写笔位置对应的滑块值
var stylusPoint = e . GetStylusPoints ( slider ) ;
if ( stylusPoint . Count > 0 )
{
2025-09-06 13:30:32 +08:00
UpdateSliderValueFromPositionImproved ( slider , stylusPoint [ 0 ] . ToPoint ( ) ) ;
2025-09-06 13:16:10 +08:00
}
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
e . Handled = true ;
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 处理滑块手写笔结束事件
2025-09-06 13:16:10 +08:00
/// </summary>
2025-09-06 13:30:32 +08:00
private void HandleSliderStylusEnd ( object sender , StylusEventArgs e , Slider slider )
2025-09-06 13:16:10 +08:00
{
if ( slider = = null ) return ;
// 释放手写笔捕获
slider . ReleaseStylusCapture ( ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
e . Handled = true ;
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 根据触摸/手写笔位置更新滑块值(改进版本)
2025-09-06 13:16:10 +08:00
/// </summary>
2025-09-06 13:30:32 +08:00
/// <param name="slider">滑块控件</param>
/// <param name="position">触摸/手写笔位置</param>
private void UpdateSliderValueFromPositionImproved ( Slider slider , Point position )
2025-09-06 13:16:10 +08:00
{
if ( slider = = null ) return ;
2025-09-06 13:30:32 +08:00
try
{
// 获取滑块的轨道元素
2025-09-30 19:15:03 +08:00
var track = slider . Template . FindName ( "PART_Track" , slider ) as Track ;
if ( track = = null )
2025-09-06 13:30:32 +08:00
{
// 如果找不到轨道,使用简单方法
UpdateSliderValueFromPosition ( slider , position ) ;
return ;
}
2025-09-06 13:16:10 +08:00
2025-09-06 13:30:32 +08:00
// 获取轨道的实际边界
var trackBounds = track . TransformToAncestor ( slider ) . TransformBounds ( new Rect ( 0 , 0 , track . ActualWidth , track . ActualHeight ) ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:30:32 +08:00
double relativePosition = 0 ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:30:32 +08:00
if ( slider . Orientation = = System . Windows . Controls . Orientation . Horizontal )
{
// 水平滑块
if ( trackBounds . Width > 0 )
{
// 计算相对于轨道的相对位置
var relativeX = position . X - trackBounds . X ;
relativePosition = Math . Max ( 0 , Math . Min ( 1 , relativeX / trackBounds . Width ) ) ;
}
}
else
{
// 垂直滑块
if ( trackBounds . Height > 0 )
{
// 计算相对于轨道的相对位置
var relativeY = position . Y - trackBounds . Y ;
relativePosition = Math . Max ( 0 , Math . Min ( 1 , relativeY / trackBounds . Height ) ) ;
}
}
2025-09-06 13:16:10 +08:00
2025-09-06 13:30:32 +08:00
// 计算新的滑块值
var newValue = slider . Minimum + relativePosition * ( slider . Maximum - slider . Minimum ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:30:32 +08:00
// 如果启用了吸附到刻度,则调整到最近的刻度
if ( slider . IsSnapToTickEnabled & & slider . TickFrequency > 0 )
{
var tickCount = ( int ) ( ( slider . Maximum - slider . Minimum ) / slider . TickFrequency ) ;
var tickIndex = ( int ) Math . Round ( relativePosition * tickCount ) ;
newValue = slider . Minimum + tickIndex * slider . TickFrequency ;
}
2025-09-06 13:16:10 +08:00
2025-09-06 13:30:32 +08:00
// 更新滑块值
slider . Value = Math . Max ( slider . Minimum , Math . Min ( slider . Maximum , newValue ) ) ;
}
catch ( Exception ex )
{
// 如果改进方法失败,回退到简单方法
UpdateSliderValueFromPosition ( slider , position ) ;
LogHelper . WriteLogToFile ( $"更新滑块值时出错,使用回退方法: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-09-06 13:16:10 +08:00
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 根据触摸/手写笔位置更新滑块值(简单版本)
2025-09-06 13:16:10 +08:00
/// </summary>
/// <param name="slider">滑块控件</param>
/// <param name="position">触摸/手写笔位置</param>
private void UpdateSliderValueFromPosition ( Slider slider , Point position )
{
if ( slider = = null ) return ;
try
{
2025-09-06 13:30:32 +08:00
// 使用更简单直接的方法计算滑块值
2025-09-06 13:16:10 +08:00
double relativePosition = 0 ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
if ( slider . Orientation = = System . Windows . Controls . Orientation . Horizontal )
{
2025-09-06 13:30:32 +08:00
// 水平滑块 - 使用滑块的实际宽度
var sliderWidth = slider . ActualWidth ;
if ( sliderWidth > 0 )
2025-09-06 13:16:10 +08:00
{
2025-09-06 13:30:32 +08:00
// 考虑滑块的边距和拇指大小
var thumbSize = 20 ; // 假设拇指大小约为20像素
var effectiveWidth = sliderWidth - thumbSize ;
var adjustedX = position . X - thumbSize / 2 ;
relativePosition = Math . Max ( 0 , Math . Min ( 1 , adjustedX / effectiveWidth ) ) ;
2025-09-06 13:16:10 +08:00
}
}
else
{
2025-09-06 13:30:32 +08:00
// 垂直滑块 - 使用滑块的实际高度
var sliderHeight = slider . ActualHeight ;
if ( sliderHeight > 0 )
2025-09-06 13:16:10 +08:00
{
2025-09-06 13:30:32 +08:00
// 考虑滑块的边距和拇指大小
var thumbSize = 20 ; // 假设拇指大小约为20像素
var effectiveHeight = sliderHeight - thumbSize ;
var adjustedY = position . Y - thumbSize / 2 ;
relativePosition = Math . Max ( 0 , Math . Min ( 1 , adjustedY / effectiveHeight ) ) ;
2025-09-06 13:16:10 +08:00
}
}
// 计算新的滑块值
var newValue = slider . Minimum + relativePosition * ( slider . Maximum - slider . Minimum ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
// 如果启用了吸附到刻度,则调整到最近的刻度
if ( slider . IsSnapToTickEnabled & & slider . TickFrequency > 0 )
{
var tickCount = ( int ) ( ( slider . Maximum - slider . Minimum ) / slider . TickFrequency ) ;
var tickIndex = ( int ) Math . Round ( relativePosition * tickCount ) ;
newValue = slider . Minimum + tickIndex * slider . TickFrequency ;
}
// 更新滑块值
slider . Value = Math . Max ( slider . Minimum , Math . Min ( slider . Maximum , newValue ) ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新滑块值时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
#endregion
2025-09-06 21:26:46 +08:00
#region 模 式 切 换 相 关
/// <summary>
/// 模式切换开关事件处理
/// </summary>
private void ToggleSwitchMode_Toggled ( object sender , RoutedEventArgs e )
{
try
{
2025-09-30 19:15:03 +08:00
var toggle = sender as ToggleSwitch ;
if ( toggle ! = null )
2025-09-06 21:26:46 +08:00
{
Settings . ModeSettings . IsPPTOnlyMode = toggle . IsOn ;
2025-10-03 17:08:46 +08:00
2025-09-13 10:51:08 +08:00
// 保存设置到文件
SaveSettingsToFile ( ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 21:26:46 +08:00
// 如果切换到仅PPT模式,立即隐藏主窗口
if ( Settings . ModeSettings . IsPPTOnlyMode )
{
Hide ( ) ;
LogHelper . WriteLogToFile ( "已切换到仅PPT模式,主窗口已隐藏" , LogHelper . LogType . Event ) ;
}
else
{
// 如果切换到正常模式,显示主窗口
Show ( ) ;
LogHelper . WriteLogToFile ( "已切换到正常模式,主窗口已显示" , LogHelper . LogType . Event ) ;
}
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换模式时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 检查是否应该显示主窗口(基于PPT模式和PPT放映状态)
/// </summary>
private void CheckMainWindowVisibility ( )
{
try
{
if ( Settings . ModeSettings . IsPPTOnlyMode )
{
// 仅PPT模式下,只有在PPT放映时才显示
bool isInSlideShow = BtnPPTSlideShowEnd . Visibility = = Visibility . Visible ;
if ( isInSlideShow & & ! IsVisible )
{
Show ( ) ;
LogHelper . WriteLogToFile ( "PPT放映开始,显示主窗口(仅PPT模式)" , LogHelper . LogType . Trace ) ;
}
else if ( ! isInSlideShow & & IsVisible )
{
Hide ( ) ;
}
}
else
{
// 正常模式下,确保主窗口可见
if ( ! IsVisible )
{
Show ( ) ;
}
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"检查主窗口可见性时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-09-13 14:59:47 +08:00
/// <summary>
/// 切换到白板模式(用于--board参数和IPC命令)
/// 调用浮动栏上的白板功能
/// </summary>
public void SwitchToBoardMode ( )
{
try
{
LogHelper . WriteLogToFile ( "开始切换到白板模式" , LogHelper . LogType . Event ) ;
// 调用浮动栏上的白板功能
ImageBlackboard_MouseUp ( null , null ) ;
LogHelper . WriteLogToFile ( "已成功切换到白板模式" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换到白板模式时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-09-06 21:26:46 +08:00
#endregion
2025-09-21 00:25:09 +08:00
#region Theme Toggle
/// <summary>
/// 主题下拉框选择变化事件
/// </summary>
private void ComboBoxTheme_SelectionChanged ( object sender , SelectionChangedEventArgs e )
{
if ( ! isLoaded ) return ;
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
try
{
2025-09-30 19:15:03 +08:00
System . Windows . Controls . ComboBox comboBox = sender as System . Windows . Controls . ComboBox ;
if ( comboBox ! = null )
2025-09-21 00:25:09 +08:00
{
Settings . Appearance . Theme = comboBox . SelectedIndex ;
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
// 应用新主题
ApplyTheme ( comboBox . SelectedIndex ) ;
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
// 保存设置
SaveSettingsToFile ( ) ;
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
// 显示通知
string themeName ;
switch ( comboBox . SelectedIndex )
{
case 0 :
themeName = "浅色主题" ;
break ;
case 1 :
themeName = "深色主题" ;
break ;
case 2 :
themeName = "跟随系统" ;
break ;
default :
themeName = "未知主题" ;
break ;
}
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
ShowNotification ( $"已切换到{themeName}" ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换主题时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
ShowNotification ( "主题切换失败" ) ;
}
}
/// <summary>
/// 应用指定主题
/// </summary>
/// <param name="themeIndex">主题索引:0-浅色,1-深色,2-跟随系统</param>
private void ApplyTheme ( int themeIndex )
{
try
{
switch ( themeIndex )
{
case 0 : // 浅色主题
2025-10-04 17:14:14 +08:00
SetTheme ( "Light" , true ) ;
2025-09-21 00:25:09 +08:00
// 浅色主题下设置浮动栏为完全不透明
ViewboxFloatingBar . Opacity = 1.0 ;
break ;
case 1 : // 深色主题
2025-10-04 17:14:14 +08:00
SetTheme ( "Dark" , true ) ;
2025-09-21 00:25:09 +08:00
// 深色主题下设置浮动栏为完全不透明
ViewboxFloatingBar . Opacity = 1.0 ;
break ;
case 2 : // 跟随系统
if ( IsSystemThemeLight ( ) )
{
2025-10-04 17:14:14 +08:00
SetTheme ( "Light" , true ) ;
2025-09-21 00:25:09 +08:00
ViewboxFloatingBar . Opacity = 1.0 ;
}
else
{
2025-10-04 17:14:14 +08:00
SetTheme ( "Dark" , true ) ;
2025-09-21 00:25:09 +08:00
ViewboxFloatingBar . Opacity = 1.0 ;
}
break ;
}
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
// 强制刷新通知框的颜色资源
RefreshNotificationColors ( ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"应用主题时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
/// <summary>
/// 刷新通知框的颜色资源
/// </summary>
private void RefreshNotificationColors ( )
{
try
{
// 强制刷新通知框的背景和前景色
var border = GridNotifications . Children . OfType < Border > ( ) . FirstOrDefault ( ) ;
if ( border ! = null )
{
border . Background = ( Brush ) Application . Current . FindResource ( "SettingsPageBackground" ) ;
border . BorderBrush = new SolidColorBrush ( Color . FromRgb ( 185 , 28 , 28 ) ) ; // 保持红色边框
}
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
TextBlockNotice . Foreground = ( Brush ) Application . Current . FindResource ( "SettingsPageForeground" ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"刷新通知框颜色时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
#endregion
2025-10-18 18:26:13 +08:00
#region UIA置顶功能
/// <summary>
/// 更新UIA置顶开关的可见性
/// </summary>
private void UpdateUIAccessTopMostVisibility ( )
{
try
{
var visibility = Settings . Advanced . IsAlwaysOnTop ? Visibility . Visible : Visibility . Collapsed ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
if ( UIAccessTopMostPanel ! = null )
{
UIAccessTopMostPanel . Visibility = visibility ;
}
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
if ( UIAccessTopMostDescription ! = null )
{
UIAccessTopMostDescription . Visibility = visibility ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新UIA置顶开关可见性时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 应用UIA置顶功能
/// </summary>
private void ApplyUIAccessTopMost ( )
{
try
{
if ( Settings . Advanced . EnableUIAccessTopMost & & Settings . Advanced . IsAlwaysOnTop )
{
// 检查是否以管理员权限运行
var identity = WindowsIdentity . GetCurrent ( ) ;
var principal = new WindowsPrincipal ( identity ) ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
if ( principal . IsInRole ( WindowsBuiltInRole . Administrator ) )
{
try
{
2025-10-25 19:57:56 +08:00
timerKillProcess . Stop ( ) ;
if ( App . watchdogProcess ! = null & & ! App . watchdogProcess . HasExited )
{
App . watchdogProcess . Kill ( ) ;
App . watchdogProcess = null ;
}
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
// 调用UIAccess DLL
if ( Environment . Is64BitProcess )
{
PrepareUIAccessX64 ( ) ;
}
else
{
PrepareUIAccessX86 ( ) ;
}
2025-12-20 13:56:46 +08:00
2025-10-25 19:57:56 +08:00
App . StartWatchdogIfNeeded ( ) ;
timerKillProcess . Start ( ) ;
2025-10-18 18:26:13 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"启用UIA置顶功能时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
else
{
LogHelper . WriteLogToFile ( "UIA置顶功能需要管理员权限" , LogHelper . LogType . Warning ) ;
}
}
else
{
LogHelper . WriteLogToFile ( "UIA置顶功能已禁用" , LogHelper . LogType . Trace ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"应用UIA置顶功能时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-10-26 00:21:10 +08:00
/// <summary>
/// 显示快抽悬浮按钮
/// </summary>
private void ShowQuickDrawFloatingButton ( )
{
try
{
2025-11-08 22:17:52 +08:00
var quickDrawButton = FindName ( "QuickDrawFloatingButton" ) as Controls . QuickDrawFloatingButtonControl ;
2025-11-08 22:07:38 +08:00
if ( quickDrawButton = = null ) return ;
2025-10-26 00:21:10 +08:00
// 检查设置是否启用快抽功能
2025-11-08 22:07:38 +08:00
if ( Settings ? . RandSettings ? . EnableQuickDraw = = true )
2025-10-26 00:21:10 +08:00
{
2025-11-08 22:07:38 +08:00
quickDrawButton . Visibility = Visibility . Visible ;
2025-10-26 00:21:10 +08:00
}
2025-11-08 22:07:38 +08:00
else
2025-10-26 00:21:10 +08:00
{
2025-11-08 22:07:38 +08:00
quickDrawButton . Visibility = Visibility . Collapsed ;
2025-10-26 00:21:10 +08:00
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"显示快抽悬浮按钮失败: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-10-18 18:26:13 +08:00
#endregion
2025-05-25 09:29:48 +08:00
}
2025-08-13 21:59:35 +08:00
}