From 097d414d0d6a6b72a13cc5f5dd4418fd2d208579 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 18:33:57 +0800
Subject: [PATCH 01/23] =?UTF-8?q?fix:=E9=9A=90=E8=97=8F=E6=8C=89=E9=92=AE?=
=?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=9A=84=E9=AB=98=E5=85=89=E9=94=99=E4=BD=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../MainWindow_cs/MW_FloatingBarIcons.cs | 68 +++++++++++++++++--
Ink Canvas/MainWindow_cs/MW_Settings.cs | 19 ++++++
2 files changed, 80 insertions(+), 7 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
index af76a843..33f42f87 100644
--- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
+++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
@@ -2971,13 +2971,13 @@ namespace Ink_Canvas
quickColorPaletteMode = "single";
}
- // 获取实际按钮宽度,如果获取不到则使用默认值
- double cursorWidth = Cursor_Icon?.ActualWidth > 0 ? Cursor_Icon.ActualWidth : buttonWidth;
- double penWidth = Pen_Icon?.ActualWidth > 0 ? Pen_Icon.ActualWidth : buttonWidth;
- double deleteWidth = SymbolIconDelete?.ActualWidth > 0 ? SymbolIconDelete.ActualWidth : buttonWidth;
- double eraserWidth = Eraser_Icon?.ActualWidth > 0 ? Eraser_Icon.ActualWidth : buttonWidth;
- double eraserByStrokesWidth = EraserByStrokes_Icon?.ActualWidth > 0 ? EraserByStrokes_Icon.ActualWidth : buttonWidth;
- double selectWidth = SymbolIconSelect?.ActualWidth > 0 ? SymbolIconSelect.ActualWidth : buttonWidth;
+ // 获取实际按钮宽度,如果获取不到则使用默认值,同时考虑按钮的可见性
+ double cursorWidth = (Cursor_Icon?.Visibility == Visibility.Visible && Cursor_Icon?.ActualWidth > 0) ? Cursor_Icon.ActualWidth : 0;
+ double penWidth = (Pen_Icon?.Visibility == Visibility.Visible && Pen_Icon?.ActualWidth > 0) ? Pen_Icon.ActualWidth : 0;
+ double deleteWidth = (SymbolIconDelete?.Visibility == Visibility.Visible && SymbolIconDelete?.ActualWidth > 0) ? SymbolIconDelete.ActualWidth : 0;
+ double eraserWidth = (Eraser_Icon?.Visibility == Visibility.Visible && Eraser_Icon?.ActualWidth > 0) ? Eraser_Icon.ActualWidth : 0;
+ double eraserByStrokesWidth = (EraserByStrokes_Icon?.Visibility == Visibility.Visible && EraserByStrokes_Icon?.ActualWidth > 0) ? EraserByStrokes_Icon.ActualWidth : 0;
+ double selectWidth = (SymbolIconSelect?.Visibility == Visibility.Visible && SymbolIconSelect?.ActualWidth > 0) ? SymbolIconSelect.ActualWidth : 0;
// 获取高光的实际宽度
double actualHighlightWidth = FloatingbarSelectionBG.ActualWidth > 0 ? FloatingbarSelectionBG.ActualWidth : highlightWidth;
@@ -3074,6 +3074,60 @@ namespace Ink_Canvas
}
}
+ ///
+ /// 获取当前选中的模式
+ ///
+ /// 当前选中的模式名称
+ public string GetCurrentSelectedMode()
+ {
+ try
+ {
+ // 检查当前编辑模式
+ if (inkCanvas.EditingMode == InkCanvasEditingMode.Select)
+ {
+ return "select";
+ }
+ else if (inkCanvas.EditingMode == InkCanvasEditingMode.Ink)
+ {
+ // 检查是否是荧光笔模式
+ if (drawingAttributes != null && drawingAttributes.IsHighlighter)
+ {
+ return "color";
+ }
+ else
+ {
+ return "pen";
+ }
+ }
+ else if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
+ {
+ // 检查是面积擦还是线擦
+ if (Eraser_Icon != null && Eraser_Icon.Visibility == Visibility.Visible)
+ {
+ return "eraser";
+ }
+ else if (EraserByStrokes_Icon != null && EraserByStrokes_Icon.Visibility == Visibility.Visible)
+ {
+ return "eraserByStrokes";
+ }
+ }
+ else if (inkCanvas.EditingMode == InkCanvasEditingMode.None)
+ {
+ return "cursor";
+ }
+ else if (drawingShapeMode != 0)
+ {
+ return "shape";
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"获取当前选中模式失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+
+ return string.Empty;
+ }
+
#endregion
#region 触摸事件支持
diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs
index 9d7ca62a..220abb99 100644
--- a/Ink Canvas/MainWindow_cs/MW_Settings.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs
@@ -2675,6 +2675,25 @@ namespace Ink_Canvas
break;
}
}
+
+ // 在按钮可见性更新后,重新计算当前高光位置
+ // 延迟执行以确保UI更新完成
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ try
+ {
+ // 获取当前选中的模式并重新设置高光位置
+ string currentMode = GetCurrentSelectedMode();
+ if (!string.IsNullOrEmpty(currentMode))
+ {
+ SetFloatingBarHighlightPosition(currentMode);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"重新计算高光位置失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }), System.Windows.Threading.DispatcherPriority.Loaded);
}
catch (Exception ex)
{
From 5ee2ad6f3d6bcfb2c4bbc9745fd8ea57c118cf30 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 18:36:20 +0800
Subject: [PATCH 02/23] =?UTF-8?q?improve:=E6=B5=AE=E5=8A=A8=E6=A0=8F?=
=?UTF-8?q?=E5=8A=A8=E6=80=81=E8=B0=83=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_Settings.cs | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs
index 220abb99..2765e976 100644
--- a/Ink Canvas/MainWindow_cs/MW_Settings.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs
@@ -2688,10 +2688,23 @@ namespace Ink_Canvas
{
SetFloatingBarHighlightPosition(currentMode);
}
+
+ // 重新计算浮动栏位置,因为按钮可见性变化会影响浮动栏宽度
+ if (!isFloatingBarFolded)
+ {
+ if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible)
+ {
+ ViewboxFloatingBarMarginAnimation(60);
+ }
+ else
+ {
+ ViewboxFloatingBarMarginAnimation(100, true);
+ }
+ }
}
catch (Exception ex)
{
- LogHelper.WriteLogToFile($"重新计算高光位置失败: {ex.Message}", LogHelper.LogType.Error);
+ LogHelper.WriteLogToFile($"重新计算高光位置和浮动栏位置失败: {ex.Message}", LogHelper.LogType.Error);
}
}), System.Windows.Threading.DispatcherPriority.Loaded);
}
From 52d318054c8649249e5d2281870c22a3ee12d683 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 18:48:46 +0800
Subject: [PATCH 03/23] =?UTF-8?q?fix:=E9=83=A8=E5=88=86=E6=83=85=E5=86=B5?=
=?UTF-8?q?=E9=AB=98=E5=85=89=E9=94=99=E4=BD=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_Settings.cs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs
index 2765e976..723f056e 100644
--- a/Ink Canvas/MainWindow_cs/MW_Settings.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs
@@ -2678,10 +2678,13 @@ namespace Ink_Canvas
// 在按钮可见性更新后,重新计算当前高光位置
// 延迟执行以确保UI更新完成
- Dispatcher.BeginInvoke(new Action(() =>
+ Dispatcher.BeginInvoke(new Action(async () =>
{
try
{
+ // 等待UI完全更新
+ await Task.Delay(100);
+
// 获取当前选中的模式并重新设置高光位置
string currentMode = GetCurrentSelectedMode();
if (!string.IsNullOrEmpty(currentMode))
From 7beb2a3cc5b277bbb810c96d7c9e6d1df869cefc Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 18:49:31 +0800
Subject: [PATCH 04/23] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E5=8F=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/AssemblyInfo.cs | 4 ++--
Ink Canvas/Properties/AssemblyInfo.cs | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Ink Canvas/AssemblyInfo.cs b/Ink Canvas/AssemblyInfo.cs
index 68f1de06..36053414 100644
--- a/Ink Canvas/AssemblyInfo.cs
+++ b/Ink Canvas/AssemblyInfo.cs
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.7.8.3")]
-[assembly: AssemblyFileVersion("1.7.8.3")]
+[assembly: AssemblyVersion("1.7.8.4")]
+[assembly: AssemblyFileVersion("1.7.8.4")]
diff --git a/Ink Canvas/Properties/AssemblyInfo.cs b/Ink Canvas/Properties/AssemblyInfo.cs
index 68f1de06..36053414 100644
--- a/Ink Canvas/Properties/AssemblyInfo.cs
+++ b/Ink Canvas/Properties/AssemblyInfo.cs
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.7.8.3")]
-[assembly: AssemblyFileVersion("1.7.8.3")]
+[assembly: AssemblyVersion("1.7.8.4")]
+[assembly: AssemblyFileVersion("1.7.8.4")]
From 636769c0ef7452ef2122628476658abfe8aa2f88 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 18:59:32 +0800
Subject: [PATCH 05/23] =?UTF-8?q?fix:=E6=8F=92=E5=85=A5=E5=9B=BE=E7=89=87?=
=?UTF-8?q?=E5=AD=90=E9=9D=A2=E6=9D=BF=E6=8A=98=E5=8F=A0=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
index 33f42f87..439ca3a5 100644
--- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
+++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
@@ -2776,17 +2776,20 @@ namespace Ink_Canvas
private void InsertImageOptions_MouseUp(object sender, MouseButtonEventArgs e)
{
- // Hide other sub-panels first
- HideSubPanelsImmediately();
-
- // Show the image options panel
- if (BoardImageOptionsPanel.Visibility == Visibility.Collapsed)
+ // Check if the image options panel is currently visible
+ bool isImagePanelVisible = BoardImageOptionsPanel.Visibility == Visibility.Visible;
+
+ // Toggle the image options panel
+ if (isImagePanelVisible)
{
- AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardImageOptionsPanel);
+ // Panel was visible, so hide it with animation
+ AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
}
else
{
- AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
+ // Panel was hidden, so hide other panels and show this one
+ HideSubPanelsImmediately();
+ AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardImageOptionsPanel);
}
}
From 96c51cd7ffeef0a745e1974436e2f63165934e5e Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 19:40:14 +0800
Subject: [PATCH 06/23] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/App.xaml.cs | 4 +-
Ink Canvas/Helpers/AutoUpdateHelper.cs | 1 -
Ink Canvas/Helpers/GlobalHotkeyManager.cs | 4 +-
Ink Canvas/Helpers/InkFadeManager.cs | 13 +-
Ink Canvas/Helpers/PPTManager.cs | 4 +-
.../Helpers/Plugins/EnhancedPluginBase.cs | 1 -
Ink Canvas/Helpers/Plugins/IEnhancedPlugin.cs | 1 -
Ink Canvas/Helpers/Plugins/IPluginService.cs | 1 -
.../Plugins/PluginConfigurationManager.cs | 1 -
.../Helpers/Plugins/PluginServiceManager.cs | 3 -
Ink Canvas/MainWindow.xaml.cs | 8 +-
.../MainWindow_cs/MW_FloatingBarIcons.cs | 5 +-
Ink Canvas/MainWindow_cs/MW_Hotkeys.cs | 4 -
Ink Canvas/MainWindow_cs/MW_ImageInsert.cs | 331 ++++++++++++++++++
Ink Canvas/MainWindow_cs/MW_Screenshot.cs | 328 -----------------
Ink Canvas/MainWindow_cs/MW_TouchEvents.cs | 1 -
.../Windows/HotkeySettingsWindow.xaml.cs | 12 +-
.../Windows/ScreenshotSelectorWindow.xaml.cs | 26 +-
18 files changed, 368 insertions(+), 380 deletions(-)
create mode 100644 Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs
index 25f5f817..8bcce6b3 100644
--- a/Ink Canvas/App.xaml.cs
+++ b/Ink Canvas/App.xaml.cs
@@ -484,7 +484,7 @@ namespace Ink_Canvas
// 在应用启动时自动释放IACore相关DLL
try
{
- Helpers.IACoreDllExtractor.ExtractIACoreDlls();
+ IACoreDllExtractor.ExtractIACoreDlls();
}
catch (Exception ex)
{
@@ -508,7 +508,7 @@ namespace Ink_Canvas
}
// 检查是否存在更新标记文件
- string updateMarkerFile = Path.Combine(App.RootPath, "update_in_progress.tmp");
+ string updateMarkerFile = Path.Combine(RootPath, "update_in_progress.tmp");
bool isUpdateInProgress = false;
// 检查是否以更新模式启动
diff --git a/Ink Canvas/Helpers/AutoUpdateHelper.cs b/Ink Canvas/Helpers/AutoUpdateHelper.cs
index e24c1df3..1cd7b7d1 100644
--- a/Ink Canvas/Helpers/AutoUpdateHelper.cs
+++ b/Ink Canvas/Helpers/AutoUpdateHelper.cs
@@ -12,7 +12,6 @@ using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
-using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
diff --git a/Ink Canvas/Helpers/GlobalHotkeyManager.cs b/Ink Canvas/Helpers/GlobalHotkeyManager.cs
index 467adec2..07d62a59 100644
--- a/Ink Canvas/Helpers/GlobalHotkeyManager.cs
+++ b/Ink Canvas/Helpers/GlobalHotkeyManager.cs
@@ -472,7 +472,7 @@ namespace Ink_Canvas.Helpers
{
// 通过反射访问主窗口的penType字段
var penTypeField = _mainWindow.GetType().GetField("penType",
- System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ BindingFlags.NonPublic | BindingFlags.Instance);
if (penTypeField != null)
{
@@ -480,7 +480,7 @@ namespace Ink_Canvas.Helpers
// 调用CheckPenTypeUIState方法更新UI状态
var checkPenTypeMethod = _mainWindow.GetType().GetMethod("CheckPenTypeUIState",
- System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ BindingFlags.NonPublic | BindingFlags.Instance);
if (checkPenTypeMethod != null)
{
diff --git a/Ink Canvas/Helpers/InkFadeManager.cs b/Ink Canvas/Helpers/InkFadeManager.cs
index 1a1e6b49..1bd3d9c2 100644
--- a/Ink Canvas/Helpers/InkFadeManager.cs
+++ b/Ink Canvas/Helpers/InkFadeManager.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Windows;
using System.Windows.Media.Animation;
using System.Windows.Threading;
@@ -107,7 +106,7 @@ namespace Ink_Canvas.Helpers
{
// 将墨迹添加到 inkCanvas 的父容器中,而不是 inkCanvas.Children
// 这样可以避免坐标系统问题
- var parent = _mainWindow.inkCanvas.Parent as System.Windows.Controls.Panel;
+ var parent = _mainWindow.inkCanvas.Parent as Panel;
if (parent != null)
{
parent.Children.Add(strokeVisual);
@@ -154,7 +153,7 @@ namespace Ink_Canvas.Helpers
try
{
// 从父容器中移除墨迹
- var parent = _mainWindow.inkCanvas?.Parent as System.Windows.Controls.Panel;
+ var parent = _mainWindow.inkCanvas?.Parent as Panel;
if (parent != null && parent.Children.Contains(visual))
{
parent.Children.Remove(visual);
@@ -202,7 +201,7 @@ namespace Ink_Canvas.Helpers
{
if (_mainWindow.inkCanvas != null)
{
- var parent = _mainWindow.inkCanvas.Parent as System.Windows.Controls.Panel;
+ var parent = _mainWindow.inkCanvas.Parent as Panel;
foreach (var visual in _strokeVisuals.Values)
{
if (parent != null && parent.Children.Contains(visual))
@@ -454,7 +453,7 @@ namespace Ink_Canvas.Helpers
originalVisual.Visibility = Visibility.Hidden;
var segments = new List();
- var parent = _mainWindow.inkCanvas?.Parent as System.Windows.Controls.Panel;
+ var parent = _mainWindow.inkCanvas?.Parent as Panel;
if (parent == null)
{
// 如果父容器不是Panel,直接使用InkCanvas
@@ -676,7 +675,7 @@ namespace Ink_Canvas.Helpers
try
{
// 移除所有分段
- var parent = _mainWindow.inkCanvas?.Parent as System.Windows.Controls.Panel;
+ var parent = _mainWindow.inkCanvas?.Parent as Panel;
foreach (var segment in segments)
{
@@ -811,7 +810,7 @@ namespace Ink_Canvas.Helpers
try
{
// 从父容器中移除墨迹
- var parent = _mainWindow.inkCanvas?.Parent as System.Windows.Controls.Panel;
+ var parent = _mainWindow.inkCanvas?.Parent as Panel;
if (parent != null && parent.Children.Contains(visual))
{
parent.Children.Remove(visual);
diff --git a/Ink Canvas/Helpers/PPTManager.cs b/Ink Canvas/Helpers/PPTManager.cs
index c51fa589..76c3f4e9 100644
--- a/Ink Canvas/Helpers/PPTManager.cs
+++ b/Ink Canvas/Helpers/PPTManager.cs
@@ -340,7 +340,7 @@ namespace Ink_Canvas.Helpers
LogHelper.WriteLogToFile($"PPT事件注册失败: {ex}", LogHelper.LogType.Error);
throw; // 重新抛出异常,让外层处理
}
- }, DispatcherPriority.Normal, System.Threading.CancellationToken.None, TimeSpan.FromSeconds(2));
+ }, DispatcherPriority.Normal, CancellationToken.None, TimeSpan.FromSeconds(2));
// 获取当前演示文稿信息
UpdateCurrentPresentationInfo();
@@ -407,7 +407,7 @@ namespace Ink_Canvas.Helpers
// COM对象类型转换失败,通常是因为对象已经被释放
LogHelper.WriteLogToFile("PPT COM对象已被释放,跳过事件注册取消", LogHelper.LogType.Trace);
}
- }, DispatcherPriority.Normal, System.Threading.CancellationToken.None, TimeSpan.FromSeconds(1));
+ }, DispatcherPriority.Normal, CancellationToken.None, TimeSpan.FromSeconds(1));
}
}
catch (Exception ex)
diff --git a/Ink Canvas/Helpers/Plugins/EnhancedPluginBase.cs b/Ink Canvas/Helpers/Plugins/EnhancedPluginBase.cs
index cf3c0b47..3b62137f 100644
--- a/Ink Canvas/Helpers/Plugins/EnhancedPluginBase.cs
+++ b/Ink Canvas/Helpers/Plugins/EnhancedPluginBase.cs
@@ -1,4 +1,3 @@
-using System;
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
diff --git a/Ink Canvas/Helpers/Plugins/IEnhancedPlugin.cs b/Ink Canvas/Helpers/Plugins/IEnhancedPlugin.cs
index 270064db..6ee5e9d5 100644
--- a/Ink Canvas/Helpers/Plugins/IEnhancedPlugin.cs
+++ b/Ink Canvas/Helpers/Plugins/IEnhancedPlugin.cs
@@ -1,4 +1,3 @@
-using System;
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
diff --git a/Ink Canvas/Helpers/Plugins/IPluginService.cs b/Ink Canvas/Helpers/Plugins/IPluginService.cs
index 3063f85f..2df665f5 100644
--- a/Ink Canvas/Helpers/Plugins/IPluginService.cs
+++ b/Ink Canvas/Helpers/Plugins/IPluginService.cs
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
-using System.Windows.Ink;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
diff --git a/Ink Canvas/Helpers/Plugins/PluginConfigurationManager.cs b/Ink Canvas/Helpers/Plugins/PluginConfigurationManager.cs
index c328b642..dfa342b2 100644
--- a/Ink Canvas/Helpers/Plugins/PluginConfigurationManager.cs
+++ b/Ink Canvas/Helpers/Plugins/PluginConfigurationManager.cs
@@ -1,4 +1,3 @@
-using Ink_Canvas.Helpers;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
diff --git a/Ink Canvas/Helpers/Plugins/PluginServiceManager.cs b/Ink Canvas/Helpers/Plugins/PluginServiceManager.cs
index 1748fc1e..e3e2d3e5 100644
--- a/Ink Canvas/Helpers/Plugins/PluginServiceManager.cs
+++ b/Ink Canvas/Helpers/Plugins/PluginServiceManager.cs
@@ -1,10 +1,7 @@
-using Ink_Canvas.Helpers;
-using Ink_Canvas.Windows;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
-using System.Windows.Ink;
using System.Windows.Media;
using System.Linq;
diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs
index 043d046f..33c4260e 100644
--- a/Ink Canvas/MainWindow.xaml.cs
+++ b/Ink Canvas/MainWindow.xaml.cs
@@ -237,8 +237,8 @@ namespace Ink_Canvas
ApplyAlwaysOnTop();
// 添加窗口激活事件处理,确保置顶状态在窗口重新激活时得到保持
- this.Activated += Window_Activated;
- this.Deactivated += Window_Deactivated;
+ Activated += Window_Activated;
+ Deactivated += Window_Deactivated;
// 为浮动栏按钮添加触摸事件支持
AddTouchSupportToFloatingBarButtons();
@@ -1911,7 +1911,7 @@ namespace Ink_Canvas
Dispatcher.BeginInvoke(new Action(() =>
{
ApplyAlwaysOnTop();
- }), System.Windows.Threading.DispatcherPriority.Loaded);
+ }), DispatcherPriority.Loaded);
}
}
@@ -1927,7 +1927,7 @@ namespace Ink_Canvas
Dispatcher.BeginInvoke(new Action(() =>
{
ApplyAlwaysOnTop();
- }), System.Windows.Threading.DispatcherPriority.Loaded);
+ }), DispatcherPriority.Loaded);
}
}
diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
index 439ca3a5..ae4e514e 100644
--- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
+++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
@@ -13,7 +13,6 @@ using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
-using System.Windows.Threading;
using Application = System.Windows.Application;
using Button = System.Windows.Controls.Button;
using HorizontalAlignment = System.Windows.HorizontalAlignment;
@@ -2806,8 +2805,8 @@ namespace Ink_Canvas
// Wait a bit for the panel to hide
await Task.Delay(100);
- // Capture screenshot and copy to clipboard
- await CaptureScreenshotToClipboard();
+ // Capture screenshot and insert to canvas
+ await CaptureScreenshotAndInsert();
}
private async void ImageOptionSelectFile_MouseUp(object sender, MouseButtonEventArgs e)
diff --git a/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs b/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs
index a0cbc550..dc19ff5f 100644
--- a/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs
@@ -1,9 +1,5 @@
using System.Windows;
using System.Windows.Input;
-using System.Diagnostics;
-using System;
-using System.Linq;
-using System.Runtime.InteropServices;
namespace Ink_Canvas
{
diff --git a/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs b/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
new file mode 100644
index 00000000..9a0b08d3
--- /dev/null
+++ b/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
@@ -0,0 +1,331 @@
+using Ink_Canvas.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Forms;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using Application = System.Windows.Application;
+using PixelFormat = System.Drawing.Imaging.PixelFormat;
+using Size = System.Drawing.Size;
+
+namespace Ink_Canvas
+{
+ // 截图结果结构体
+ public struct ScreenshotResult
+ {
+ public Rectangle Area;
+ public List Path;
+
+ public ScreenshotResult(Rectangle area, List path = null)
+ {
+ Area = area;
+ Path = path;
+ }
+ }
+
+ public partial class MainWindow : Window
+ {
+ // 截图并插入到画布
+ private async Task CaptureScreenshotAndInsert()
+ {
+ try
+ {
+ // 隐藏主窗口以避免截图包含窗口本身
+ var originalVisibility = Visibility;
+ Visibility = Visibility.Hidden;
+
+ // 等待窗口隐藏
+ await Task.Delay(200);
+
+ // 启动区域选择截图
+ var screenshotResult = await ShowScreenshotSelector();
+
+ // 恢复窗口显示
+ Visibility = originalVisibility;
+
+ if (screenshotResult.HasValue && screenshotResult.Value.Area.Width > 0 && screenshotResult.Value.Area.Height > 0)
+ {
+ // 截取选定区域
+ using (var originalBitmap = CaptureScreenArea(screenshotResult.Value.Area))
+ {
+ if (originalBitmap != null)
+ {
+ Bitmap finalBitmap = originalBitmap;
+ bool needDisposeFinalBitmap = false;
+
+ try
+ {
+ // 如果有路径信息,应用形状遮罩
+ if (screenshotResult.Value.Path != null && screenshotResult.Value.Path.Count > 0)
+ {
+ finalBitmap = ApplyShapeMask(originalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
+ needDisposeFinalBitmap = true; // 标记需要释放新创建的位图
+ }
+
+ // 将截图转换为WPF Image并插入到画布
+ await InsertScreenshotToCanvas(finalBitmap);
+ }
+ finally
+ {
+ // 如果创建了新的位图,需要释放它
+ if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
+ {
+ finalBitmap.Dispose();
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ ShowNotification("截图已取消");
+ }
+ }
+ catch (Exception ex)
+ {
+ ShowNotification($"截图失败: {ex.Message}");
+ Visibility = Visibility.Visible;
+ }
+ }
+
+ // 显示截图区域选择器
+ private async Task ShowScreenshotSelector()
+ {
+ ScreenshotResult? result = null;
+
+ try
+ {
+ await Application.Current.Dispatcher.InvokeAsync(() =>
+ {
+ var selectorWindow = new ScreenshotSelectorWindow();
+ if (selectorWindow.ShowDialog() == true)
+ {
+ result = new ScreenshotResult(
+ selectorWindow.SelectedArea.Value,
+ selectorWindow.SelectedPath
+ );
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"显示截图选择器失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+
+ return result;
+ }
+
+ // 截取指定屏幕区域
+ private Bitmap CaptureScreenArea(Rectangle area)
+ {
+ try
+ {
+ // 确保区域在有效范围内
+ var virtualScreen = SystemInformation.VirtualScreen;
+
+ // 调整区域边界,确保不超出屏幕范围
+ int x = Math.Max(area.X, virtualScreen.X);
+ int y = Math.Max(area.Y, virtualScreen.Y);
+ int right = Math.Min(area.Right, virtualScreen.Right);
+ int bottom = Math.Min(area.Bottom, virtualScreen.Bottom);
+
+ int width = Math.Max(1, right - x);
+ int height = Math.Max(1, bottom - y);
+
+ // 创建支持透明度的位图
+ var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
+ using (var graphics = Graphics.FromImage(bitmap))
+ {
+ // 设置高质量渲染
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.CompositingMode = CompositingMode.SourceOver;
+
+ // 截取屏幕区域
+ graphics.CopyFromScreen(x, y, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
+ }
+
+ LogHelper.WriteLogToFile($"成功截取区域: X={x}, Y={y}, Width={width}, Height={height}");
+ return bitmap;
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"截取屏幕区域失败: {ex.Message}", LogHelper.LogType.Error);
+ return null;
+ }
+ }
+
+ // 将截图插入到画布
+ private async Task InsertScreenshotToCanvas(Bitmap bitmap)
+ {
+ try
+ {
+ // 将Bitmap转换为WPF BitmapSource
+ var bitmapSource = ConvertBitmapToBitmapSource(bitmap);
+
+ // 创建WPF Image控件
+ var image = new System.Windows.Controls.Image
+ {
+ Source = bitmapSource,
+ Stretch = Stretch.Uniform
+ };
+ RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
+
+ // 生成唯一名称
+ string timestamp = "screenshot_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
+ image.Name = timestamp;
+
+ // 居中并缩放图片
+ CenterAndScaleElement(image);
+
+ // 添加到画布
+ inkCanvas.Children.Add(image);
+
+ // 添加鼠标事件处理,使图片可以被选择
+ image.MouseDown += UIElement_MouseDown;
+ image.IsManipulationEnabled = true;
+
+ // 提交历史记录
+ timeMachine.CommitElementInsertHistory(image);
+
+ ShowNotification("截图已插入到画布");
+ }
+ catch (Exception ex)
+ {
+ ShowNotification($"插入截图失败: {ex.Message}");
+ LogHelper.WriteLogToFile($"插入截图失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 应用形状遮罩到截图
+ private Bitmap ApplyShapeMask(Bitmap bitmap, List path, Rectangle area)
+ {
+ try
+ {
+ // 验证路径参数
+ if (path == null || path.Count < 3)
+ {
+ LogHelper.WriteLogToFile("路径点数不足,无法应用形状遮罩", LogHelper.LogType.Warning);
+ return bitmap;
+ }
+
+ // 获取DPI缩放比例
+ var dpiScale = GetDpiScale();
+ var virtualScreen = SystemInformation.VirtualScreen;
+
+ // 创建结果位图,确保支持透明度
+ var resultBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb);
+
+ // 首先将整个位图设置为透明
+ using (var resultGraphics = Graphics.FromImage(resultBitmap))
+ {
+ // 清除位图,设置为完全透明
+ resultGraphics.Clear(System.Drawing.Color.Transparent);
+
+ // 设置高质量渲染
+ resultGraphics.SmoothingMode = SmoothingMode.AntiAlias;
+ resultGraphics.CompositingQuality = CompositingQuality.HighQuality;
+ resultGraphics.CompositingMode = CompositingMode.SourceOver;
+
+ // 创建路径
+ using (var pathGraphics = new GraphicsPath())
+ {
+ // 转换WPF坐标到GDI+坐标,考虑DPI缩放和屏幕偏移
+ var points = new PointF[path.Count];
+ for (int i = 0; i < path.Count; i++)
+ {
+ // 将WPF坐标转换为实际屏幕坐标,然后相对于截图区域计算偏移
+ double screenX = (path[i].X * dpiScale) + virtualScreen.Left;
+ double screenY = (path[i].Y * dpiScale) + virtualScreen.Top;
+
+ // 计算相对于截图区域的坐标
+ float relativeX = (float)(screenX - area.X);
+ float relativeY = (float)(screenY - area.Y);
+
+ // 确保坐标在有效范围内
+ relativeX = Math.Max(0, Math.Min(relativeX, bitmap.Width - 1));
+ relativeY = Math.Max(0, Math.Min(relativeY, bitmap.Height - 1));
+
+ points[i] = new PointF(relativeX, relativeY);
+ }
+
+ // 添加路径 - 使用FillMode.Winding确保路径正确填充
+ pathGraphics.FillMode = FillMode.Winding;
+ pathGraphics.AddPolygon(points);
+
+ // 验证路径是否有效
+ if (!pathGraphics.IsVisible(0, 0) && pathGraphics.GetBounds().Width > 0 && pathGraphics.GetBounds().Height > 0)
+ {
+ // 设置裁剪区域为路径内部
+ resultGraphics.SetClip(pathGraphics);
+
+ // 在裁剪区域内绘制原始图像
+ resultGraphics.DrawImage(bitmap, 0, 0);
+
+ // 重置裁剪区域,确保后续操作不受影响
+ resultGraphics.ResetClip();
+ }
+ else
+ {
+ LogHelper.WriteLogToFile("生成的路径无效,返回透明图像", LogHelper.LogType.Warning);
+ // 如果路径无效,返回透明图像
+ return resultBitmap;
+ }
+ }
+ }
+
+ return resultBitmap;
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"应用形状遮罩失败: {ex.Message}", LogHelper.LogType.Error);
+ return bitmap;
+ }
+ }
+
+ // 将System.Drawing.Bitmap转换为WPF BitmapSource
+ private BitmapSource ConvertBitmapToBitmapSource(Bitmap bitmap)
+ {
+ try
+ {
+ using (var memory = new MemoryStream())
+ {
+ bitmap.Save(memory, ImageFormat.Png);
+ memory.Position = 0;
+
+ var bitmapImage = new BitmapImage();
+ bitmapImage.BeginInit();
+ bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
+ bitmapImage.StreamSource = memory;
+ bitmapImage.EndInit();
+ bitmapImage.Freeze();
+
+ return bitmapImage;
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"转换位图失败: {ex.Message}", LogHelper.LogType.Error);
+ throw;
+ }
+ }
+
+ // 获取DPI缩放比例
+ private double GetDpiScale()
+ {
+ var source = PresentationSource.FromVisual(this);
+ if (source?.CompositionTarget != null)
+ {
+ return source.CompositionTarget.TransformToDevice.M11;
+ }
+ return 1.0; // 默认DPI
+ }
+ }
+}
\ No newline at end of file
diff --git a/Ink Canvas/MainWindow_cs/MW_Screenshot.cs b/Ink Canvas/MainWindow_cs/MW_Screenshot.cs
index 00e537fd..05b5cf89 100644
--- a/Ink Canvas/MainWindow_cs/MW_Screenshot.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Screenshot.cs
@@ -1,33 +1,12 @@
-using Ink_Canvas.Helpers;
using System;
-using System.Collections.Generic;
using System.Drawing;
-using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
-using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
-using System.Windows.Media.Imaging;
-using Application = System.Windows.Application;
-using Clipboard = System.Windows.Clipboard;
-using Size = System.Drawing.Size;
namespace Ink_Canvas
{
- // 截图结果结构体
- public struct ScreenshotResult
- {
- public System.Drawing.Rectangle Area;
- public List Path;
-
- public ScreenshotResult(System.Drawing.Rectangle area, List path = null)
- {
- Area = area;
- Path = path;
- }
- }
-
public partial class MainWindow : Window
{
private void SaveScreenShot(bool isHideNotification, string fileName = null)
@@ -120,312 +99,5 @@ namespace Ink_Canvas
screenshotsFolder,
$"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png");
}
-
- // 截图并复制到剪贴板
- private async Task CaptureScreenshotToClipboard()
- {
- try
- {
- // 隐藏主窗口以避免截图包含窗口本身
- var originalVisibility = this.Visibility;
- this.Visibility = Visibility.Hidden;
-
- // 等待窗口隐藏
- await Task.Delay(200);
-
- // 启动区域选择截图
- var screenshotResult = await ShowScreenshotSelector();
-
- // 恢复窗口显示
- this.Visibility = originalVisibility;
-
- if (screenshotResult.HasValue && screenshotResult.Value.Area.Width > 0 && screenshotResult.Value.Area.Height > 0)
- {
- // 截取选定区域
- using (var originalBitmap = CaptureScreenArea(screenshotResult.Value.Area))
- {
- if (originalBitmap != null)
- {
- Bitmap finalBitmap = originalBitmap;
- bool needDisposeFinalBitmap = false;
-
- try
- {
- // 如果有路径信息,应用形状遮罩
- if (screenshotResult.Value.Path != null && screenshotResult.Value.Path.Count > 0)
- {
- finalBitmap = ApplyShapeMask(originalBitmap, screenshotResult.Value.Path, screenshotResult.Value.Area);
- needDisposeFinalBitmap = true; // 标记需要释放新创建的位图
- }
-
- // 将截图复制到剪贴板
- CopyBitmapToClipboard(finalBitmap);
-
- // 等待窗口完全显示后自动粘贴
- await Task.Delay(100);
- await AutoPasteScreenshot();
- }
- finally
- {
- // 如果创建了新的位图,需要释放它
- if (needDisposeFinalBitmap && finalBitmap != originalBitmap)
- {
- finalBitmap.Dispose();
- }
- }
- }
- }
- }
- else
- {
- ShowNotification("截图已取消");
- }
- }
- catch (Exception ex)
- {
- ShowNotification($"截图失败: {ex.Message}");
- this.Visibility = Visibility.Visible;
- }
- }
-
- // 显示截图区域选择器
- private async Task ShowScreenshotSelector()
- {
- ScreenshotResult? result = null;
-
- try
- {
- await Application.Current.Dispatcher.InvokeAsync(() =>
- {
- var selectorWindow = new ScreenshotSelectorWindow();
- if (selectorWindow.ShowDialog() == true)
- {
- result = new ScreenshotResult(
- selectorWindow.SelectedArea.Value,
- selectorWindow.SelectedPath
- );
- }
- });
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"显示截图选择器失败: {ex.Message}", LogHelper.LogType.Error);
- }
-
- return result;
- }
-
- // 截取指定屏幕区域
- private Bitmap CaptureScreenArea(System.Drawing.Rectangle area)
- {
- try
- {
- // 确保区域在有效范围内
- var virtualScreen = SystemInformation.VirtualScreen;
-
- // 调整区域边界,确保不超出屏幕范围
- int x = Math.Max(area.X, virtualScreen.X);
- int y = Math.Max(area.Y, virtualScreen.Y);
- int right = Math.Min(area.Right, virtualScreen.Right);
- int bottom = Math.Min(area.Bottom, virtualScreen.Bottom);
-
- int width = Math.Max(1, right - x);
- int height = Math.Max(1, bottom - y);
-
- // 创建支持透明度的位图
- var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
- using (var graphics = Graphics.FromImage(bitmap))
- {
- // 设置高质量渲染
- graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
- graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
- graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
- graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
-
- // 截取屏幕区域
- graphics.CopyFromScreen(x, y, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
- }
-
- LogHelper.WriteLogToFile($"成功截取区域: X={x}, Y={y}, Width={width}, Height={height}", LogHelper.LogType.Info);
- return bitmap;
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"截取屏幕区域失败: {ex.Message}", LogHelper.LogType.Error);
- return null;
- }
- }
-
- // 自动粘贴截图到画布
- private async Task AutoPasteScreenshot()
- {
- try
- {
- // 只在白板模式下自动粘贴
- if (currentMode == 1)
- {
- await PasteImageFromClipboard();
- ShowNotification("截图已自动插入到画布");
- }
- else
- {
- ShowNotification("截图已复制到剪贴板,可在白板模式下粘贴");
- }
- }
- catch (Exception ex)
- {
- ShowNotification($"自动粘贴截图失败: {ex.Message}");
- LogHelper.WriteLogToFile($"自动粘贴截图失败: {ex.Message}", LogHelper.LogType.Error);
- }
- }
-
- // 将Bitmap复制到剪贴板
- private void CopyBitmapToClipboard(Bitmap bitmap)
- {
- try
- {
- // 将System.Drawing.Bitmap转换为WPF BitmapSource
- var bitmapSource = ConvertBitmapToBitmapSource(bitmap);
-
- // 复制到剪贴板
- Clipboard.SetImage(bitmapSource);
- }
- catch (Exception ex)
- {
- ShowNotification($"复制到剪贴板失败: {ex.Message}");
- }
- }
-
- // 应用形状遮罩到截图
- private Bitmap ApplyShapeMask(Bitmap bitmap, List path, System.Drawing.Rectangle area)
- {
- try
- {
- // 验证路径参数
- if (path == null || path.Count < 3)
- {
- LogHelper.WriteLogToFile("路径点数不足,无法应用形状遮罩", LogHelper.LogType.Warning);
- return bitmap;
- }
-
- // 获取DPI缩放比例
- var dpiScale = GetDpiScale();
- var virtualScreen = SystemInformation.VirtualScreen;
-
- // 创建结果位图,确保支持透明度
- var resultBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb);
-
- // 首先将整个位图设置为透明
- using (var resultGraphics = Graphics.FromImage(resultBitmap))
- {
- // 清除位图,设置为完全透明
- resultGraphics.Clear(System.Drawing.Color.Transparent);
-
- // 设置高质量渲染
- resultGraphics.SmoothingMode = SmoothingMode.AntiAlias;
- resultGraphics.CompositingQuality = CompositingQuality.HighQuality;
- resultGraphics.CompositingMode = CompositingMode.SourceOver;
-
- // 创建路径
- using (var pathGraphics = new GraphicsPath())
- {
- // 转换WPF坐标到GDI+坐标,考虑DPI缩放和屏幕偏移
- var points = new PointF[path.Count];
- for (int i = 0; i < path.Count; i++)
- {
- // 将WPF坐标转换为实际屏幕坐标,然后相对于截图区域计算偏移
- double screenX = (path[i].X * dpiScale) + virtualScreen.Left;
- double screenY = (path[i].Y * dpiScale) + virtualScreen.Top;
-
- // 计算相对于截图区域的坐标
- float relativeX = (float)(screenX - area.X);
- float relativeY = (float)(screenY - area.Y);
-
- // 确保坐标在有效范围内
- relativeX = Math.Max(0, Math.Min(relativeX, bitmap.Width - 1));
- relativeY = Math.Max(0, Math.Min(relativeY, bitmap.Height - 1));
-
- points[i] = new PointF(relativeX, relativeY);
- }
-
- // 添加路径 - 使用FillMode.Winding确保路径正确填充
- pathGraphics.FillMode = FillMode.Winding;
- pathGraphics.AddPolygon(points);
-
- // 验证路径是否有效
- if (!pathGraphics.IsVisible(0, 0) && pathGraphics.GetBounds().Width > 0 && pathGraphics.GetBounds().Height > 0)
- {
- // 设置裁剪区域为路径内部
- resultGraphics.SetClip(pathGraphics);
-
- // 在裁剪区域内绘制原始图像
- resultGraphics.DrawImage(bitmap, 0, 0);
-
- // 重置裁剪区域,确保后续操作不受影响
- resultGraphics.ResetClip();
- }
- else
- {
- LogHelper.WriteLogToFile("生成的路径无效,返回原始图像", LogHelper.LogType.Warning);
- // 如果路径无效,返回透明图像
- return resultBitmap;
- }
- }
- }
-
- LogHelper.WriteLogToFile($"成功应用形状遮罩,路径点数: {path.Count}", LogHelper.LogType.Info);
- return resultBitmap;
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"应用形状遮罩失败: {ex.Message}", LogHelper.LogType.Error);
- // 返回完全透明的图像而不是原始图像
- var transparentBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb);
- using (var g = Graphics.FromImage(transparentBitmap))
- {
- g.Clear(System.Drawing.Color.Transparent);
- }
- return transparentBitmap;
- }
- }
-
- // 获取DPI缩放比例
- private double GetDpiScale()
- {
- var source = PresentationSource.FromVisual(this);
- if (source?.CompositionTarget != null)
- {
- return source.CompositionTarget.TransformToDevice.M11;
- }
- return 1.0; // 默认DPI
- }
-
- // 将System.Drawing.Bitmap转换为WPF BitmapSource
- private BitmapSource ConvertBitmapToBitmapSource(Bitmap bitmap)
- {
- try
- {
- using (var memory = new MemoryStream())
- {
- // 使用PNG格式保存,确保透明度信息不丢失
- bitmap.Save(memory, ImageFormat.Png);
- memory.Position = 0;
-
- var bitmapImage = new BitmapImage();
- bitmapImage.BeginInit();
- bitmapImage.StreamSource = memory;
- bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
- bitmapImage.EndInit();
- bitmapImage.Freeze();
-
- return bitmapImage;
- }
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"转换位图失败: {ex.Message}", LogHelper.LogType.Error);
- throw;
- }
- }
}
}
diff --git a/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs b/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs
index d24d6f42..9c1a8393 100644
--- a/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs
+++ b/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs
@@ -1,7 +1,6 @@
using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
diff --git a/Ink Canvas/Windows/HotkeySettingsWindow.xaml.cs b/Ink Canvas/Windows/HotkeySettingsWindow.xaml.cs
index 31289032..5539d553 100644
--- a/Ink Canvas/Windows/HotkeySettingsWindow.xaml.cs
+++ b/Ink Canvas/Windows/HotkeySettingsWindow.xaml.cs
@@ -30,7 +30,7 @@ namespace Ink_Canvas.Windows
InitializeHotkeyItems();
// 延迟加载快捷键,确保快捷键管理器已完全初始化
- this.Loaded += (s, e) =>
+ Loaded += (s, e) =>
{
try
{
@@ -48,7 +48,7 @@ namespace Ink_Canvas.Windows
};
// 注册窗口关闭事件
- this.Closed += HotkeySettingsWindow_Closed;
+ Closed += HotkeySettingsWindow_Closed;
}
#endregion
@@ -440,7 +440,7 @@ namespace Ink_Canvas.Windows
if (settingsBorder != null)
{
- settingsBorder.Visibility = System.Windows.Visibility.Collapsed;
+ settingsBorder.Visibility = Visibility.Collapsed;
}
// 隐藏设置蒙版
@@ -449,7 +449,7 @@ namespace Ink_Canvas.Windows
if (settingsMask != null)
{
- settingsMask.Visibility = System.Windows.Visibility.Collapsed;
+ settingsMask.Visibility = Visibility.Collapsed;
}
}
catch (Exception ex)
@@ -471,7 +471,7 @@ namespace Ink_Canvas.Windows
if (settingsBorder != null)
{
- settingsBorder.Visibility = System.Windows.Visibility.Visible;
+ settingsBorder.Visibility = Visibility.Visible;
}
// 显示设置蒙版
@@ -480,7 +480,7 @@ namespace Ink_Canvas.Windows
if (settingsMask != null)
{
- settingsMask.Visibility = System.Windows.Visibility.Visible;
+ settingsMask.Visibility = Visibility.Visible;
}
}
catch (Exception ex)
diff --git a/Ink Canvas/Windows/ScreenshotSelectorWindow.xaml.cs b/Ink Canvas/Windows/ScreenshotSelectorWindow.xaml.cs
index ee5aa49e..205f7fce 100644
--- a/Ink Canvas/Windows/ScreenshotSelectorWindow.xaml.cs
+++ b/Ink Canvas/Windows/ScreenshotSelectorWindow.xaml.cs
@@ -16,13 +16,13 @@ namespace Ink_Canvas
{
private bool _isSelecting = false;
private bool _isFreehandMode = false;
- private System.Windows.Point _startPoint;
- private System.Windows.Point _currentPoint;
- private List _freehandPoints;
+ private Point _startPoint;
+ private Point _currentPoint;
+ private List _freehandPoints;
private Polyline _freehandPolyline;
public DrawingRectangle? SelectedArea { get; private set; }
- public List SelectedPath { get; private set; }
+ public List SelectedPath { get; private set; }
public ScreenshotSelectorWindow()
{
@@ -47,7 +47,7 @@ namespace Ink_Canvas
private void InitializeFreehandMode()
{
- _freehandPoints = new List();
+ _freehandPoints = new List();
_freehandPolyline = new Polyline
{
Stroke = Brushes.Red,
@@ -65,10 +65,10 @@ namespace Ink_Canvas
// 转换为WPF坐标系统
var dpiScale = GetDpiScale();
- this.Left = virtualScreen.Left / dpiScale;
- this.Top = virtualScreen.Top / dpiScale;
- this.Width = virtualScreen.Width / dpiScale;
- this.Height = virtualScreen.Height / dpiScale;
+ Left = virtualScreen.Left / dpiScale;
+ Top = virtualScreen.Top / dpiScale;
+ Width = virtualScreen.Width / dpiScale;
+ Height = virtualScreen.Height / dpiScale;
}
private double GetDpiScale()
@@ -186,7 +186,7 @@ namespace Ink_Canvas
if (_freehandPoints.Count > 3) // 至少需要3个点形成有效路径
{
// 创建路径的副本,避免修改原始列表
- var pathPoints = new List(_freehandPoints);
+ var pathPoints = new List(_freehandPoints);
// 确保路径闭合(如果最后一个点不是起始点,则添加起始点)
if (pathPoints.Count > 0 &&
@@ -284,7 +284,7 @@ namespace Ink_Canvas
return new Rect(x, y, width, height);
}
- private Rect CalculatePathBounds(List points)
+ private Rect CalculatePathBounds(List points)
{
if (points == null || points.Count == 0)
return new Rect();
@@ -306,12 +306,12 @@ namespace Ink_Canvas
}
// 优化路径:移除重复点和过于接近的点,提高路径质量
- private List OptimizePath(List originalPath)
+ private List OptimizePath(List originalPath)
{
if (originalPath == null || originalPath.Count < 3)
return originalPath;
- var optimizedPath = new List();
+ var optimizedPath = new List();
const double minDistance = 2.0; // 最小距离阈值
// 添加第一个点
From a9cdc36967e18db7232ff2350a033bd5737dc821 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 20:59:50 +0800
Subject: [PATCH 07/23] =?UTF-8?q?improve:=E9=95=BF=E6=8C=89=E7=BF=BB?=
=?UTF-8?q?=E9=A1=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_PPT.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_PPT.cs b/Ink Canvas/MainWindow_cs/MW_PPT.cs
index 3efc6e72..f3c05cab 100644
--- a/Ink Canvas/MainWindow_cs/MW_PPT.cs
+++ b/Ink Canvas/MainWindow_cs/MW_PPT.cs
@@ -86,8 +86,8 @@ namespace Ink_Canvas
private DispatcherTimer _longPressTimer;
private bool _isLongPressActive = false;
private bool _isLongPressNext = true; // true为下一页,false为上一页
- private const int LongPressDelay = 50; // 长按延迟时间(毫秒)
- private const int LongPressInterval = 50; // 长按翻页间隔(毫秒)
+ private const int LongPressDelay = 15; // 长按延迟时间(毫秒)
+ private const int LongPressInterval = 15; // 长按翻页间隔(毫秒)
#endregion
#region PPT Managers
From 9e511d29a604ae35c869e45c52fe936d8623af7f Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 21:18:34 +0800
Subject: [PATCH 08/23] =?UTF-8?q?delete:=E5=9B=BE=E7=89=87=E6=8F=92?=
=?UTF-8?q?=E5=85=A5=E9=80=89=E4=B8=AD=E5=92=8C=E7=A7=BB=E5=8A=A8=E7=9B=B8?=
=?UTF-8?q?=E5=85=B3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow.xaml | 107 +---
Ink Canvas/MainWindow.xaml.cs | 88 +--
Ink Canvas/MainWindow_cs/MW_BoardControls.cs | 21 +-
.../MainWindow_cs/MW_ClipboardHandler.cs | 4 -
.../MainWindow_cs/MW_ElementsControls.cs | 27 -
.../MainWindow_cs/MW_FloatingBarIcons.cs | 16 -
Ink Canvas/MainWindow_cs/MW_ImageInsert.cs | 4 -
Ink Canvas/MainWindow_cs/MW_PageListView.cs | 6 -
.../MainWindow_cs/MW_SelectionGestures.cs | 536 +-----------------
Ink Canvas/MainWindow_cs/MW_TimeMachine.cs | 8 -
10 files changed, 34 insertions(+), 783 deletions(-)
diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml
index 1a087381..dc8c2a44 100644
--- a/Ink Canvas/MainWindow.xaml
+++ b/Ink Canvas/MainWindow.xaml
@@ -3443,112 +3443,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs
index 33c4260e..f31390d8 100644
--- a/Ink Canvas/MainWindow.xaml.cs
+++ b/Ink Canvas/MainWindow.xaml.cs
@@ -509,7 +509,7 @@ namespace Ink_Canvas
ApplyAlwaysOnTop();
// 初始化UIElement选择系统
- InitializeUIElementSelection();
+
// 初始化剪贴板监控
InitializeClipboardMonitoring();
@@ -1003,19 +1003,7 @@ namespace Ink_Canvas
// 如果点击的不是图片或其他UI元素,则取消选择
if (!(hitTest is Image) && !(hitTest is MediaElement))
{
- // 检查是否点击在已选择的UI元素上
- bool clickedOnSelectedElement = false;
- if (selectedUIElement != null)
- {
- var elementBounds = GetUIElementBounds(selectedUIElement);
- var clickPoint = e.GetPosition(inkCanvas);
- clickedOnSelectedElement = elementBounds.Contains(clickPoint);
- }
-
- if (!clickedOnSelectedElement)
- {
- DeselectUIElement();
- }
+
}
}
}
@@ -1931,79 +1919,7 @@ namespace Ink_Canvas
}
}
- #region Image Toolbar Event Handlers
- private void BorderImageClone_MouseUp(object sender, MouseButtonEventArgs e)
- {
- if (lastBorderMouseDownObject != sender) return;
-
- if (selectedUIElement is Image image)
- {
- CloneImage(image);
- }
- }
-
- private void BorderImageCloneToNewBoard_MouseUp(object sender, MouseButtonEventArgs e)
- {
- if (lastBorderMouseDownObject != sender) return;
-
- if (selectedUIElement is Image image)
- {
- CloneImageToNewBoard(image);
- }
- }
-
- private void BorderImageRotateLeft_MouseUp(object sender, MouseButtonEventArgs e)
- {
- if (lastBorderMouseDownObject != sender) return;
-
- if (selectedUIElement is Image image)
- {
- RotateImage(image, -90);
- }
- }
-
- private void BorderImageRotateRight_MouseUp(object sender, MouseButtonEventArgs e)
- {
- if (lastBorderMouseDownObject != sender) return;
-
- if (selectedUIElement is Image image)
- {
- RotateImage(image, 90);
- }
- }
-
- private void GridImageScaleIncrease_MouseUp(object sender, MouseButtonEventArgs e)
- {
- if (lastBorderMouseDownObject != sender) return;
-
- if (selectedUIElement is Image image)
- {
- ScaleImage(image, 1.25);
- }
- }
-
- private void GridImageScaleDecrease_MouseUp(object sender, MouseButtonEventArgs e)
- {
- if (lastBorderMouseDownObject != sender) return;
-
- if (selectedUIElement is Image image)
- {
- ScaleImage(image, 0.8);
- }
- }
-
- private void BorderImageDelete_MouseUp(object sender, MouseButtonEventArgs e)
- {
- if (lastBorderMouseDownObject != sender) return;
-
- if (selectedUIElement is Image image)
- {
- DeleteImage(image);
- }
- }
-
- #endregion
#region 全局快捷键管理
///
diff --git a/Ink Canvas/MainWindow_cs/MW_BoardControls.cs b/Ink Canvas/MainWindow_cs/MW_BoardControls.cs
index f011d0c8..8ecc3345 100644
--- a/Ink Canvas/MainWindow_cs/MW_BoardControls.cs
+++ b/Ink Canvas/MainWindow_cs/MW_BoardControls.cs
@@ -116,8 +116,7 @@ namespace Ink_Canvas
_currentCommitType = CommitReason.ClearingCanvas;
if (isErasedByCode) _currentCommitType = CommitReason.CodeInput;
- // 取消任何UI元素的选择,隐藏拉伸控件
- DeselectUIElement();
+
// 只清除笔画,不清除图片元素
// 图片元素的清除由调用方决定
@@ -159,11 +158,7 @@ namespace Ink_Canvas
foreach (var item in TimeMachineHistories[CurrentWhiteboardIndex]) ApplyHistoryToCanvas(item);
}
- // 确保选中状态被清除,因为我们切换了页面
- if (selectedUIElement != null)
- {
- DeselectUIElement();
- }
+
}
catch
{
@@ -214,8 +209,7 @@ namespace Ink_Canvas
{
if (CurrentWhiteboardIndex <= 1) return;
- // 取消任何UI元素的选择
- DeselectUIElement();
+
SaveStrokes();
@@ -239,9 +233,7 @@ namespace Ink_Canvas
BtnWhiteBoardAdd_Click(sender, e);
return;
}
-
- // 取消任何UI元素的选择
- DeselectUIElement();
+
SaveStrokes();
@@ -258,10 +250,7 @@ namespace Ink_Canvas
if (WhiteboardTotalCount >= 99) return;
if (Settings.Automation.IsAutoSaveStrokesAtClear &&
inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber) SaveScreenShot(true);
-
- // 取消任何UI元素的选择
- DeselectUIElement();
-
+
SaveStrokes();
ClearStrokes(true);
diff --git a/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs b/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs
index ee4a6152..6a136cd3 100644
--- a/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs
@@ -145,10 +145,6 @@ namespace Ink_Canvas
// 添加到画布
inkCanvas.Children.Add(image);
- // 添加鼠标事件处理
- image.MouseDown += UIElement_MouseDown;
- image.IsManipulationEnabled = true;
-
// 提交到历史记录
timeMachine.CommitElementInsertHistory(image);
diff --git a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
index ea0176cc..058ab986 100644
--- a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
@@ -31,10 +31,6 @@ namespace Ink_Canvas
CenterAndScaleElement(image);
inkCanvas.Children.Add(image);
- // 添加鼠标事件处理,使图片可以被选择
- image.MouseDown += UIElement_MouseDown;
- image.IsManipulationEnabled = true;
-
timeMachine.CommitElementInsertHistory(image);
}
}
@@ -115,10 +111,6 @@ namespace Ink_Canvas
InkCanvas.SetTop(mediaElement, 0);
inkCanvas.Children.Add(mediaElement);
- // 添加鼠标事件处理,使媒体元素可以被选择
- mediaElement.MouseDown += UIElement_MouseDown;
- mediaElement.IsManipulationEnabled = true;
-
mediaElement.LoadedBehavior = MediaState.Manual;
mediaElement.UnloadedBehavior = MediaState.Manual;
mediaElement.Loaded += async (_, args) =>
@@ -242,17 +234,9 @@ namespace Ink_Canvas
InkCanvas.SetLeft(clonedImage, InkCanvas.GetLeft(image) + 20);
InkCanvas.SetTop(clonedImage, InkCanvas.GetTop(image) + 20);
- // 添加鼠标事件处理,使图片可以被选择
- clonedImage.MouseDown += UIElement_MouseDown;
- clonedImage.IsManipulationEnabled = true;
-
// 添加到画布
inkCanvas.Children.Add(clonedImage);
- // 选择新克隆的图片
- DeselectUIElement();
- SelectUIElement(clonedImage);
-
// 提交到时间机器以支持撤销
timeMachine.CommitElementInsertHistory(clonedImage);
}
@@ -287,20 +271,12 @@ namespace Ink_Canvas
InkCanvas.SetLeft(clonedImage, InkCanvas.GetLeft(image) + 20);
InkCanvas.SetTop(clonedImage, InkCanvas.GetTop(image) + 20);
- // 添加鼠标事件处理,使图片可以被选择
- clonedImage.MouseDown += UIElement_MouseDown;
- clonedImage.IsManipulationEnabled = true;
-
// 创建新页面
BtnWhiteBoardAdd_Click(null, null);
// 添加到新页面的画布
inkCanvas.Children.Add(clonedImage);
- // 选择新克隆的图片
- DeselectUIElement();
- SelectUIElement(clonedImage);
-
// 提交到时间机器以支持撤销
timeMachine.CommitElementInsertHistory(clonedImage);
}
@@ -379,9 +355,6 @@ namespace Ink_Canvas
{
inkCanvas.Children.Remove(image);
- // 取消选择
- DeselectUIElement();
-
// 提交到时间机器以支持撤销
timeMachine.CommitElementRemoveHistory(image);
}
diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
index ae4e514e..a3ba95ea 100644
--- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
+++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
@@ -2500,8 +2500,6 @@ namespace Ink_Canvas
AnimationsHelper.HideWithSlideAndFade(BlackboardCenterSide);
AnimationsHelper.HideWithSlideAndFade(BlackboardRightSide);
- DeselectUIElement();
-
// 在PPT模式下隐藏手势面板和手势按钮
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
@@ -2549,9 +2547,6 @@ namespace Ink_Canvas
AnimationsHelper.HideWithSlideAndFade(BlackboardCenterSide);
AnimationsHelper.HideWithSlideAndFade(BlackboardRightSide);
- // 取消任何UI元素的选择
- DeselectUIElement();
-
// 在PPT模式下隐藏手势面板和手势按钮
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
@@ -2592,9 +2587,6 @@ namespace Ink_Canvas
AnimationsHelper.ShowWithSlideFromBottomAndFade(BlackboardCenterSide);
AnimationsHelper.ShowWithSlideFromBottomAndFade(BlackboardRightSide);
- // 取消任何UI元素的选择
- DeselectUIElement();
-
SaveStrokes(true);
ClearStrokes(true);
@@ -2831,10 +2823,6 @@ namespace Ink_Canvas
CenterAndScaleElement(image);
inkCanvas.Children.Add(image);
- // 添加鼠标事件处理,使图片可以被选择
- image.MouseDown += UIElement_MouseDown;
- image.IsManipulationEnabled = true;
-
timeMachine.CommitElementInsertHistory(image);
}
}
@@ -2859,10 +2847,6 @@ namespace Ink_Canvas
CenterAndScaleElement(image);
inkCanvas.Children.Add(image);
- // 添加鼠标事件处理,使图片可以被选择
- image.MouseDown += UIElement_MouseDown;
- image.IsManipulationEnabled = true;
-
timeMachine.CommitElementInsertHistory(image);
}
}
diff --git a/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs b/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
index 9a0b08d3..fdaa10b0 100644
--- a/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
@@ -188,10 +188,6 @@ namespace Ink_Canvas
// 添加到画布
inkCanvas.Children.Add(image);
- // 添加鼠标事件处理,使图片可以被选择
- image.MouseDown += UIElement_MouseDown;
- image.IsManipulationEnabled = true;
-
// 提交历史记录
timeMachine.CommitElementInsertHistory(image);
diff --git a/Ink Canvas/MainWindow_cs/MW_PageListView.cs b/Ink Canvas/MainWindow_cs/MW_PageListView.cs
index 70521c1d..c82a0113 100644
--- a/Ink Canvas/MainWindow_cs/MW_PageListView.cs
+++ b/Ink Canvas/MainWindow_cs/MW_PageListView.cs
@@ -86,9 +86,6 @@ namespace Ink_Canvas
// 只有当选择的页面与当前页面不同时才进行切换
if (index + 1 != CurrentWhiteboardIndex)
{
- // 取消任何UI元素的选择(只在真正切换页面时)
- DeselectUIElement();
-
SaveStrokes();
ClearStrokes(true);
CurrentWhiteboardIndex = index + 1;
@@ -111,9 +108,6 @@ namespace Ink_Canvas
// 只有当选择的页面与当前页面不同时才进行切换
if (index + 1 != CurrentWhiteboardIndex)
{
- // 取消任何UI元素的选择(只在真正切换页面时)
- DeselectUIElement();
-
SaveStrokes();
ClearStrokes(true);
CurrentWhiteboardIndex = index + 1;
diff --git a/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs b/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs
index a87dec7b..346bbde6 100644
--- a/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs
+++ b/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs
@@ -29,6 +29,7 @@ namespace Ink_Canvas
return;
}
}
+
lastBorderMouseDownObject = sender;
}
@@ -93,6 +94,7 @@ namespace Ink_Canvas
stroke.DrawingAttributes.Width = newWidth;
stroke.DrawingAttributes.Height = newHeight;
}
+
if (DrawingAttributesHistory.Count > 0)
{
@@ -240,6 +242,7 @@ namespace Ink_Canvas
{
collecion.Add(item.Key);
}
+
timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory);
DrawingAttributesHistory = new Dictionary>();
foreach (var item in DrawingAttributesHistoryFlag)
@@ -277,9 +280,8 @@ namespace Ink_Canvas
if (inkCanvas.GetSelectedStrokes().Count == inkCanvas.Strokes.Count)
{
// 使用集中化的工具模式切换方法
- SetCurrentToolMode(InkCanvasEditingMode.Ink, () => {
- SetCurrentToolMode(InkCanvasEditingMode.Select);
- });
+ SetCurrentToolMode(InkCanvasEditingMode.Ink,
+ () => { SetCurrentToolMode(InkCanvasEditingMode.Select); });
}
else
{
@@ -307,8 +309,7 @@ namespace Ink_Canvas
if (inkCanvas.GetSelectedStrokes().Count == 0)
{
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
- // 当没有选中笔画时,检查是否有选中的UIElement
- CheckUIElementSelection();
+
}
else
{
@@ -316,25 +317,11 @@ namespace Ink_Canvas
BorderStrokeSelectionClone.Background = Brushes.Transparent;
isStrokeSelectionCloneOn = false;
updateBorderStrokeSelectionControlLocation();
- // 当选中笔画时,取消UIElement选择
- DeselectUIElement();
+
}
}
- private void CheckUIElementSelection()
- {
- // 检查InkCanvas中的UIElement是否被选中
- var selectedElements = inkCanvas.GetSelectedElements();
- if (selectedElements.Count > 0)
- {
- var element = selectedElements[0];
- SelectUIElement(element);
- }
- else
- {
- DeselectUIElement();
- }
- }
+
private void updateBorderStrokeSelectionControlLocation()
{
@@ -366,8 +353,10 @@ namespace Ink_Canvas
{
StrokeInitialHistory[item.Key] = item.Value.Item2;
}
+
StrokeManipulationHistory = null;
}
+
if (DrawingAttributesHistory.Count > 0)
{
timeMachine.CommitStrokeDrawingAttributesHistory(DrawingAttributesHistory);
@@ -419,18 +408,26 @@ namespace Ink_Canvas
stroke.DrawingAttributes.Width *= md.Scale.X;
stroke.DrawingAttributes.Height *= md.Scale.Y;
}
- catch { }
+ catch
+ {
+ }
}
updateBorderStrokeSelectionControlLocation();
}
}
- catch { }
+ catch
+ {
+ }
}
- private void GridInkCanvasSelectionCover_TouchDown(object sender, TouchEventArgs e) { }
+ private void GridInkCanvasSelectionCover_TouchDown(object sender, TouchEventArgs e)
+ {
+ }
- private void GridInkCanvasSelectionCover_TouchUp(object sender, TouchEventArgs e) { }
+ private void GridInkCanvasSelectionCover_TouchUp(object sender, TouchEventArgs e)
+ {
+ }
private Point lastTouchPointOnGridInkCanvasCover = new Point(0, 0);
@@ -514,225 +511,7 @@ namespace Ink_Canvas
}
#region UIElement Selection and Resize
-
- private UIElement selectedUIElement;
- private System.Windows.Controls.Canvas resizeHandlesCanvas;
- private readonly List resizeHandles = new List();
- private bool isResizing;
- private ResizeDirection currentResizeDirection = ResizeDirection.None;
- private Point resizeStartPoint;
- private Rect originalElementBounds;
-
- // 图片工具栏相关
- private Border borderImageSelectionControl;
- private double BorderImageSelectionControlWidth = 490.0; // 6个按钮 + 分隔线的实际宽度
- private double BorderImageSelectionControlHeight = 80.0;
-
- // 元素变化监听相关
- private DispatcherTimer elementUpdateTimer;
- private Rect lastElementBounds;
-
- private enum ResizeDirection
- {
- None,
- TopLeft,
- TopCenter,
- TopRight,
- MiddleLeft,
- MiddleRight,
- BottomLeft,
- BottomCenter,
- BottomRight
- }
-
- private void InitializeUIElementSelection()
- {
- // 创建拖拽手柄画布
- if (resizeHandlesCanvas == null)
- {
- resizeHandlesCanvas = new System.Windows.Controls.Canvas
- {
- Background = Brushes.Transparent,
- IsHitTestVisible = true,
- Visibility = Visibility.Collapsed
- };
-
- // 将手柄画布添加到主网格中,确保它在InkCanvas之上
- var mainGrid = inkCanvas.Parent as Grid;
- if (mainGrid != null)
- {
- mainGrid.Children.Add(resizeHandlesCanvas);
- Panel.SetZIndex(resizeHandlesCanvas, 1000); // 确保在最上层
- }
- }
-
- // 初始化图片工具栏引用
- if (borderImageSelectionControl == null)
- {
- borderImageSelectionControl = FindName("BorderImageSelectionControl") as Border;
- }
-
- // 创建8个拖拽手柄
- CreateResizeHandles();
- }
-
- private void CreateResizeHandles()
- {
- resizeHandles.Clear();
- resizeHandlesCanvas.Children.Clear();
-
- var directions = new[]
- {
- ResizeDirection.TopLeft, ResizeDirection.TopCenter, ResizeDirection.TopRight,
- ResizeDirection.MiddleLeft, ResizeDirection.MiddleRight,
- ResizeDirection.BottomLeft, ResizeDirection.BottomCenter, ResizeDirection.BottomRight
- };
-
- foreach (var direction in directions)
- {
- var handle = new Rectangle
- {
- Width = 12,
- Height = 12,
- Fill = Brushes.White,
- Stroke = Brushes.DodgerBlue,
- StrokeThickness = 2,
- Cursor = GetCursorForDirection(direction),
- Tag = direction
- };
-
- handle.MouseDown += ResizeHandle_MouseDown;
- handle.MouseMove += ResizeHandle_MouseMove;
- handle.MouseUp += ResizeHandle_MouseUp;
-
- resizeHandles.Add(handle);
- resizeHandlesCanvas.Children.Add(handle);
- }
- }
-
- private Cursor GetCursorForDirection(ResizeDirection direction)
- {
- switch (direction)
- {
- case ResizeDirection.TopLeft:
- case ResizeDirection.BottomRight:
- return Cursors.SizeNWSE;
- case ResizeDirection.TopRight:
- case ResizeDirection.BottomLeft:
- return Cursors.SizeNESW;
- case ResizeDirection.TopCenter:
- case ResizeDirection.BottomCenter:
- return Cursors.SizeNS;
- case ResizeDirection.MiddleLeft:
- case ResizeDirection.MiddleRight:
- return Cursors.SizeWE;
- default:
- return Cursors.Arrow;
- }
- }
-
- private void SelectUIElement(UIElement element)
- {
- if (selectedUIElement == element) return;
-
- // 取消之前的选择
- DeselectUIElement();
-
- // 清除笔画选择
- if (inkCanvas.GetSelectedStrokes().Count > 0)
- {
- isProgramChangeStrokeSelection = true;
- inkCanvas.Select(new StrokeCollection());
- isProgramChangeStrokeSelection = false;
- }
-
- selectedUIElement = element;
-
- if (element != null)
- {
- // 初始化选择系统(如果还没有初始化)
- if (resizeHandlesCanvas == null)
- {
- InitializeUIElementSelection();
- }
-
- // 显示拖拽手柄(所有UI元素都需要)
- ShowResizeHandles();
-
- // 根据元素类型显示特定的工具栏
- if (element is Image)
- {
- ShowImageToolbar();
- }
-
- // 监听元素的布局变化,以便实时更新手柄位置
- StartMonitoringElementChanges(element);
- }
- }
-
- private void DeselectUIElement()
- {
- // 停止监听之前选中元素的变化
- StopMonitoringElementChanges();
-
- selectedUIElement = null;
- HideResizeHandles();
- HideImageToolbar();
- }
-
- private void ShowResizeHandles()
- {
- if (selectedUIElement == null || resizeHandlesCanvas == null) return;
-
- var bounds = GetUIElementBounds(selectedUIElement);
- UpdateResizeHandlesPosition(bounds);
- resizeHandlesCanvas.Visibility = Visibility.Visible;
- }
-
- private void HideResizeHandles()
- {
- if (resizeHandlesCanvas != null)
- {
- resizeHandlesCanvas.Visibility = Visibility.Collapsed;
- }
- }
-
- private void ShowImageToolbar()
- {
- if (selectedUIElement == null || borderImageSelectionControl == null) return;
-
- var bounds = GetUIElementBounds(selectedUIElement);
- UpdateImageToolbarPosition(bounds);
- borderImageSelectionControl.Visibility = Visibility.Visible;
- }
-
- private void HideImageToolbar()
- {
- if (borderImageSelectionControl != null)
- {
- borderImageSelectionControl.Visibility = Visibility.Collapsed;
- }
- }
-
- private void UpdateImageToolbarPosition(Rect bounds)
- {
- if (borderImageSelectionControl == null) return;
-
- // 计算工具栏位置,类似于墨迹选择工具栏的逻辑
- var toolbarX = bounds.X + bounds.Width / 2 - BorderImageSelectionControlWidth / 2;
- var toolbarY = bounds.Y + bounds.Height + 10; // 在图片下方10像素处
-
- // 确保工具栏不会超出画布边界
- if (toolbarX < 0) toolbarX = 0;
- if (toolbarX + BorderImageSelectionControlWidth > inkCanvas.ActualWidth)
- toolbarX = inkCanvas.ActualWidth - BorderImageSelectionControlWidth;
-
- if (toolbarY + BorderImageSelectionControlHeight > inkCanvas.ActualHeight)
- toolbarY = bounds.Y - BorderImageSelectionControlHeight - 10; // 如果下方空间不够,显示在上方
-
- borderImageSelectionControl.Margin = new Thickness(toolbarX, toolbarY, 0, 0);
- }
-
+
private Rect GetUIElementBounds(UIElement element)
{
if (element is FrameworkElement fe)
@@ -772,270 +551,7 @@ namespace Ink_Canvas
return new Rect(0, 0, 0, 0);
}
-
- private void UpdateResizeHandlesPosition(Rect bounds)
- {
- if (resizeHandles.Count != 8) return;
-
- var handleSize = 12.0;
- var halfHandle = handleSize / 2;
-
- // 计算手柄位置
- var positions = new[]
- {
- new Point(bounds.Left - halfHandle, bounds.Top - halfHandle), // TopLeft
- new Point(bounds.Left + bounds.Width / 2 - halfHandle, bounds.Top - halfHandle), // TopCenter
- new Point(bounds.Right - halfHandle, bounds.Top - halfHandle), // TopRight
- new Point(bounds.Left - halfHandle, bounds.Top + bounds.Height / 2 - halfHandle), // MiddleLeft
- new Point(bounds.Right - halfHandle, bounds.Top + bounds.Height / 2 - halfHandle), // MiddleRight
- new Point(bounds.Left - halfHandle, bounds.Bottom - halfHandle), // BottomLeft
- new Point(bounds.Left + bounds.Width / 2 - halfHandle, bounds.Bottom - halfHandle), // BottomCenter
- new Point(bounds.Right - halfHandle, bounds.Bottom - halfHandle) // BottomRight
- };
-
- for (int i = 0; i < resizeHandles.Count && i < positions.Length; i++)
- {
- System.Windows.Controls.Canvas.SetLeft(resizeHandles[i], positions[i].X);
- System.Windows.Controls.Canvas.SetTop(resizeHandles[i], positions[i].Y);
- }
- }
-
- private void ResizeHandle_MouseDown(object sender, MouseButtonEventArgs e)
- {
- if (selectedUIElement == null) return;
-
- var handle = sender as Rectangle;
- if (handle?.Tag is ResizeDirection direction)
- {
- isResizing = true;
- currentResizeDirection = direction;
- resizeStartPoint = e.GetPosition(inkCanvas);
- originalElementBounds = GetUIElementBounds(selectedUIElement);
-
- handle.CaptureMouse();
- e.Handled = true;
- }
- }
-
- private void ResizeHandle_MouseMove(object sender, MouseEventArgs e)
- {
- if (!isResizing || selectedUIElement == null) return;
-
- var currentPoint = e.GetPosition(inkCanvas);
- var deltaX = currentPoint.X - resizeStartPoint.X;
- var deltaY = currentPoint.Y - resizeStartPoint.Y;
-
- ResizeUIElement(deltaX, deltaY);
- e.Handled = true;
- }
-
- private void ResizeHandle_MouseUp(object sender, MouseButtonEventArgs e)
- {
- if (isResizing)
- {
- isResizing = false;
- currentResizeDirection = ResizeDirection.None;
-
- var handle = sender as Rectangle;
- handle?.ReleaseMouseCapture();
- e.Handled = true;
- }
- }
-
- private void ResizeUIElement(double deltaX, double deltaY)
- {
- if (selectedUIElement == null) return;
-
- var newBounds = originalElementBounds;
- const double minSize = 20.0;
-
- switch (currentResizeDirection)
- {
- case ResizeDirection.TopLeft:
- var newWidth = originalElementBounds.Width - deltaX;
- var newHeight = originalElementBounds.Height - deltaY;
- if (newWidth >= minSize && newHeight >= minSize)
- {
- newBounds.X = originalElementBounds.X + deltaX;
- newBounds.Y = originalElementBounds.Y + deltaY;
- newBounds.Width = newWidth;
- newBounds.Height = newHeight;
- }
- break;
-
- case ResizeDirection.TopCenter:
- var newHeightTC = originalElementBounds.Height - deltaY;
- if (newHeightTC >= minSize)
- {
- newBounds.Y = originalElementBounds.Y + deltaY;
- newBounds.Height = newHeightTC;
- }
- break;
-
- case ResizeDirection.TopRight:
- var newWidthTR = originalElementBounds.Width + deltaX;
- var newHeightTR = originalElementBounds.Height - deltaY;
- if (newWidthTR >= minSize && newHeightTR >= minSize)
- {
- newBounds.Y = originalElementBounds.Y + deltaY;
- newBounds.Width = newWidthTR;
- newBounds.Height = newHeightTR;
- }
- break;
-
- case ResizeDirection.MiddleLeft:
- var newWidthML = originalElementBounds.Width - deltaX;
- if (newWidthML >= minSize)
- {
- newBounds.X = originalElementBounds.X + deltaX;
- newBounds.Width = newWidthML;
- }
- break;
-
- case ResizeDirection.MiddleRight:
- var newWidthMR = originalElementBounds.Width + deltaX;
- if (newWidthMR >= minSize)
- {
- newBounds.Width = newWidthMR;
- }
- break;
-
- case ResizeDirection.BottomLeft:
- var newWidthBL = originalElementBounds.Width - deltaX;
- var newHeightBL = originalElementBounds.Height + deltaY;
- if (newWidthBL >= minSize && newHeightBL >= minSize)
- {
- newBounds.X = originalElementBounds.X + deltaX;
- newBounds.Width = newWidthBL;
- newBounds.Height = newHeightBL;
- }
- break;
-
- case ResizeDirection.BottomCenter:
- var newHeightBC = originalElementBounds.Height + deltaY;
- if (newHeightBC >= minSize)
- {
- newBounds.Height = newHeightBC;
- }
- break;
-
- case ResizeDirection.BottomRight:
- var newWidthBR = originalElementBounds.Width + deltaX;
- var newHeightBR = originalElementBounds.Height + deltaY;
- if (newWidthBR >= minSize && newHeightBR >= minSize)
- {
- newBounds.Width = newWidthBR;
- newBounds.Height = newHeightBR;
- }
- break;
- }
-
- // 应用新的尺寸和位置
- ApplyUIElementBounds(selectedUIElement, newBounds);
-
- // 更新手柄位置
- UpdateResizeHandlesPosition(newBounds);
-
- // 如果是图片,也更新工具栏位置
- if (selectedUIElement is Image)
- {
- UpdateImageToolbarPosition(newBounds);
- }
- }
-
- private void ApplyUIElementBounds(UIElement element, Rect bounds)
- {
- if (element is FrameworkElement fe)
- {
- // 清除RenderTransform,避免与直接设置Width/Height冲突
- fe.RenderTransform = Transform.Identity;
-
- // 直接设置位置和大小
- InkCanvas.SetLeft(element, bounds.X);
- InkCanvas.SetTop(element, bounds.Y);
- fe.Width = bounds.Width;
- fe.Height = bounds.Height;
- }
- }
-
- private void UIElement_MouseDown(object sender, MouseButtonEventArgs e)
- {
- if (inkCanvas.EditingMode == InkCanvasEditingMode.Select)
- {
- var element = sender as UIElement;
- if (element != null)
- {
- // 切换到选择模式并选择这个元素
- inkCanvas.Select(new[] { element });
- SelectUIElement(element);
- e.Handled = true;
- }
- }
- }
-
- private void StartMonitoringElementChanges(UIElement element)
- {
- // 停止之前的监听
- StopMonitoringElementChanges();
-
- if (element == null) return;
-
- // 记录初始边界
- lastElementBounds = GetUIElementBounds(element);
-
- // 创建定时器,定期检查元素边界变化
- elementUpdateTimer = new DispatcherTimer
- {
- Interval = TimeSpan.FromMilliseconds(16) // 约60FPS的更新频率
- };
-
- elementUpdateTimer.Tick += (sender, e) =>
- {
- if (selectedUIElement == null)
- {
- StopMonitoringElementChanges();
- return;
- }
-
- var currentBounds = GetUIElementBounds(selectedUIElement);
-
- // 检查边界是否发生变化
- if (!AreRectsEqual(lastElementBounds, currentBounds))
- {
- lastElementBounds = currentBounds;
-
- // 更新手柄位置
- UpdateResizeHandlesPosition(currentBounds);
-
- // 如果是图片,也更新工具栏位置
- if (selectedUIElement is Image)
- {
- UpdateImageToolbarPosition(currentBounds);
- }
- }
- };
-
- elementUpdateTimer.Start();
- }
-
- private void StopMonitoringElementChanges()
- {
- if (elementUpdateTimer != null)
- {
- elementUpdateTimer.Stop();
- elementUpdateTimer = null;
- }
- }
-
- private bool AreRectsEqual(Rect rect1, Rect rect2)
- {
- const double tolerance = 0.1; // 允许的误差范围
- return Math.Abs(rect1.X - rect2.X) < tolerance &&
- Math.Abs(rect1.Y - rect2.Y) < tolerance &&
- Math.Abs(rect1.Width - rect2.Width) < tolerance &&
- Math.Abs(rect1.Height - rect2.Height) < tolerance;
- }
-
- #endregion
}
-}
\ No newline at end of file
+}
+
+#endregion
diff --git a/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs b/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs
index cc44b98b..2d9ea05a 100644
--- a/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs
+++ b/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs
@@ -190,19 +190,11 @@ namespace Ink_Canvas
{
if (item.InsertedElement is Image img)
{
- img.MouseDown -= UIElement_MouseDown;
- img.MouseDown += UIElement_MouseDown;
- img.IsManipulationEnabled = true;
-
// 重新应用CenterAndScaleElement变换
CenterAndScaleElement(img);
}
else if (item.InsertedElement is MediaElement media)
{
- media.MouseDown -= UIElement_MouseDown;
- media.MouseDown += UIElement_MouseDown;
- media.IsManipulationEnabled = true;
-
// 重新应用CenterAndScaleElement变换
CenterAndScaleElement(media);
}
From b61c7490b3d4aef7148664ca680103ddea25eb81 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 21:23:36 +0800
Subject: [PATCH 09/23] =?UTF-8?q?improve:=E6=88=AA=E5=9B=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../MainWindow_cs/MW_ElementsControls.cs | 98 +++++++++++++++----
Ink Canvas/MainWindow_cs/MW_ImageInsert.cs | 93 +++++++++++++++++-
2 files changed, 169 insertions(+), 22 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
index 058ab986..3eb8dc2c 100644
--- a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
@@ -6,6 +6,8 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
+using System.Windows.Threading;
+using Ink_Canvas.Helpers;
namespace Ink_Canvas
{
@@ -370,32 +372,88 @@ namespace Ink_Canvas
private void CenterAndScaleElement(FrameworkElement element)
{
- double maxWidth = SystemParameters.PrimaryScreenWidth / 2;
- double maxHeight = SystemParameters.PrimaryScreenHeight / 2;
+ try
+ {
+ // 确保元素已加载且有有效尺寸
+ if (element == null || element.ActualWidth <= 0 || element.ActualHeight <= 0)
+ {
+ // 如果元素尺寸无效,等待加载完成后再处理
+ element.Loaded += (sender, e) =>
+ {
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ CenterAndScaleElement(element);
+ }), System.Windows.Threading.DispatcherPriority.Loaded);
+ };
+ return;
+ }
- double scaleX = maxWidth / element.Width;
- double scaleY = maxHeight / element.Height;
- double scale = Math.Min(scaleX, scaleY);
+ // 获取画布的实际尺寸
+ double canvasWidth = inkCanvas.ActualWidth;
+ double canvasHeight = inkCanvas.ActualHeight;
- // 直接设置元素的大小,而不使用RenderTransform
- double newWidth = element.Width * scale;
- double newHeight = element.Height * scale;
+ // 如果画布尺寸为0,使用窗口尺寸作为备选
+ if (canvasWidth <= 0 || canvasHeight <= 0)
+ {
+ canvasWidth = this.ActualWidth;
+ canvasHeight = this.ActualHeight;
+ }
- element.Width = newWidth;
- element.Height = newHeight;
+ // 如果仍然为0,使用屏幕尺寸
+ if (canvasWidth <= 0 || canvasHeight <= 0)
+ {
+ canvasWidth = SystemParameters.PrimaryScreenWidth;
+ canvasHeight = SystemParameters.PrimaryScreenHeight;
+ }
- // 计算居中位置
- double canvasWidth = inkCanvas.ActualWidth;
- double canvasHeight = inkCanvas.ActualHeight;
- double centerX = (canvasWidth - newWidth) / 2;
- double centerY = (canvasHeight - newHeight) / 2;
+ // 计算最大允许尺寸(画布的70%)
+ double maxWidth = canvasWidth * 0.7;
+ double maxHeight = canvasHeight * 0.7;
- // 直接设置位置,而不使用RenderTransform
- InkCanvas.SetLeft(element, centerX);
- InkCanvas.SetTop(element, centerY);
+ // 获取元素的当前尺寸
+ double elementWidth = element.ActualWidth;
+ double elementHeight = element.ActualHeight;
- // 清除任何现有的RenderTransform
- element.RenderTransform = Transform.Identity;
+ // 计算缩放比例
+ double scaleX = maxWidth / elementWidth;
+ double scaleY = maxHeight / elementHeight;
+ double scale = Math.Min(scaleX, scaleY);
+
+ // 如果元素本身比最大尺寸小,不进行缩放
+ if (scale > 1.0)
+ {
+ scale = 1.0;
+ }
+
+ // 计算新的尺寸
+ double newWidth = elementWidth * scale;
+ double newHeight = elementHeight * scale;
+
+ // 设置元素尺寸
+ element.Width = newWidth;
+ element.Height = newHeight;
+
+ // 计算居中位置
+ double centerX = (canvasWidth - newWidth) / 2;
+ double centerY = (canvasHeight - newHeight) / 2;
+
+ // 确保位置不为负数
+ centerX = Math.Max(0, centerX);
+ centerY = Math.Max(0, centerY);
+
+ // 设置位置
+ InkCanvas.SetLeft(element, centerX);
+ InkCanvas.SetTop(element, centerY);
+
+ // 清除任何现有的RenderTransform
+ element.RenderTransform = Transform.Identity;
+
+ LogHelper.WriteLogToFile($"元素居中完成: 位置({centerX}, {centerY}), 尺寸({newWidth}x{newHeight})");
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"元素居中失败: {ex.Message}", LogHelper.LogType.Error);
+ }
}
}
}
diff --git a/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs b/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
index fdaa10b0..000f4f02 100644
--- a/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
@@ -7,6 +7,7 @@ using System.Drawing.Imaging;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
+using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;
@@ -182,8 +183,15 @@ namespace Ink_Canvas
string timestamp = "screenshot_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
- // 居中并缩放图片
- CenterAndScaleElement(image);
+ // 等待图片加载完成后再进行居中处理
+ image.Loaded += (sender, e) =>
+ {
+ // 确保在UI线程中执行
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ CenterAndScaleScreenshot(image);
+ }), System.Windows.Threading.DispatcherPriority.Loaded);
+ };
// 添加到画布
inkCanvas.Children.Add(image);
@@ -200,6 +208,87 @@ namespace Ink_Canvas
}
}
+ // 专门为截图优化的居中缩放方法
+ private void CenterAndScaleScreenshot(System.Windows.Controls.Image image)
+ {
+ try
+ {
+ // 确保图片已加载
+ if (image.Source == null || image.ActualWidth == 0 || image.ActualHeight == 0)
+ {
+ return;
+ }
+
+ // 获取画布的实际尺寸
+ double canvasWidth = inkCanvas.ActualWidth;
+ double canvasHeight = inkCanvas.ActualHeight;
+
+ // 如果画布尺寸为0,使用窗口尺寸作为备选
+ if (canvasWidth <= 0 || canvasHeight <= 0)
+ {
+ canvasWidth = this.ActualWidth;
+ canvasHeight = this.ActualHeight;
+ }
+
+ // 如果仍然为0,使用屏幕尺寸
+ if (canvasWidth <= 0 || canvasHeight <= 0)
+ {
+ canvasWidth = SystemParameters.PrimaryScreenWidth;
+ canvasHeight = SystemParameters.PrimaryScreenHeight;
+ }
+
+ // 计算最大允许尺寸(画布的80%)
+ double maxWidth = canvasWidth * 0.8;
+ double maxHeight = canvasHeight * 0.8;
+
+ // 获取图片的原始尺寸
+ double originalWidth = image.Source.Width;
+ double originalHeight = image.Source.Height;
+
+ // 计算缩放比例
+ double scaleX = maxWidth / originalWidth;
+ double scaleY = maxHeight / originalHeight;
+ double scale = Math.Min(scaleX, scaleY);
+
+ // 如果图片本身比最大尺寸小,不进行缩放
+ if (scale > 1.0)
+ {
+ scale = 1.0;
+ }
+
+ // 计算新的尺寸
+ double newWidth = originalWidth * scale;
+ double newHeight = originalHeight * scale;
+
+ // 设置图片尺寸
+ image.Width = newWidth;
+ image.Height = newHeight;
+
+ // 计算居中位置
+ double centerX = (canvasWidth - newWidth) / 2;
+ double centerY = (canvasHeight - newHeight) / 2;
+
+ // 确保位置不为负数
+ centerX = Math.Max(0, centerX);
+ centerY = Math.Max(0, centerY);
+
+ // 设置位置
+ InkCanvas.SetLeft(image, centerX);
+ InkCanvas.SetTop(image, centerY);
+
+ // 清除任何现有的RenderTransform
+ image.RenderTransform = Transform.Identity;
+
+ LogHelper.WriteLogToFile($"截图居中完成: 位置({centerX}, {centerY}), 尺寸({newWidth}x{newHeight})");
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"截图居中失败: {ex.Message}", LogHelper.LogType.Error);
+ // 如果居中失败,使用默认的居中方法作为备选
+ CenterAndScaleElement(image);
+ }
+ }
+
// 应用形状遮罩到截图
private Bitmap ApplyShapeMask(Bitmap bitmap, List path, Rectangle area)
{
From 4690ab3c300f9f434bdd85db28173efca79f053e Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 22:14:39 +0800
Subject: [PATCH 10/23] =?UTF-8?q?improve:=E6=8F=92=E5=85=A5=E5=9B=BE?=
=?UTF-8?q?=E7=89=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../MainWindow_cs/MW_ElementsControls.cs | 462 ++++++++++++++++++
Ink Canvas/MainWindow_cs/MW_ImageInsert.cs | 44 ++
.../MainWindow_cs/MW_SelectionGestures.cs | 15 +-
3 files changed, 519 insertions(+), 2 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
index 3eb8dc2c..37c553f6 100644
--- a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
@@ -8,11 +8,21 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Ink_Canvas.Helpers;
+using System.Windows.Input;
+using System.Linq;
+using System.Collections.Generic;
+using System.Windows.Ink;
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
+ // 当前选中的可操作元素
+ private FrameworkElement currentSelectedElement;
+ private bool isDragging = false;
+ private Point dragStartPoint;
+ private bool isElementSelected = false;
+
#region Image
private async void BtnImageInsert_Click(object sender, RoutedEventArgs e)
{
@@ -30,14 +40,449 @@ namespace Ink_Canvas
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
+ // 初始化TransformGroup
+ InitializeElementTransform(image);
+
CenterAndScaleElement(image);
+
+ // 设置图片属性,避免被InkCanvas选择系统处理
+ image.IsHitTestVisible = true;
+ image.Focusable = false;
+
+ // 初始化InkCanvas选择设置
+ InitializeInkCanvasSelectionSettings();
+
inkCanvas.Children.Add(image);
+ // 绑定事件处理器
+ BindElementEvents(image);
+
timeMachine.CommitElementInsertHistory(image);
}
}
}
+ // 初始化元素的TransformGroup
+ private void InitializeElementTransform(FrameworkElement element)
+ {
+ var transformGroup = new TransformGroup();
+ transformGroup.Children.Add(new ScaleTransform(1, 1));
+ transformGroup.Children.Add(new TranslateTransform(0, 0));
+ transformGroup.Children.Add(new RotateTransform(0));
+ element.RenderTransform = transformGroup;
+ }
+
+ // 绑定元素事件处理器
+ private void BindElementEvents(FrameworkElement element)
+ {
+ // 鼠标事件
+ element.MouseLeftButtonDown += Element_MouseLeftButtonDown;
+ element.MouseLeftButtonUp += Element_MouseLeftButtonUp;
+ element.MouseMove += Element_MouseMove;
+ element.MouseWheel += Element_MouseWheel;
+
+ // 触摸事件
+ element.IsManipulationEnabled = true;
+ element.ManipulationDelta += Element_ManipulationDelta;
+ element.ManipulationCompleted += Element_ManipulationCompleted;
+
+ // 设置光标
+ element.Cursor = Cursors.Hand;
+
+ // 禁用InkCanvas对图片的选择处理
+ element.IsHitTestVisible = true;
+ element.Focusable = false;
+ }
+
+ // 鼠标左键按下事件
+ private void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ if (sender is FrameworkElement element)
+ {
+ // 取消之前选中的元素
+ if (currentSelectedElement != null && currentSelectedElement != element)
+ {
+ UnselectElement(currentSelectedElement);
+ }
+
+ // 选中当前元素
+ SelectElement(element);
+
+ // 开始拖动
+ isDragging = true;
+ dragStartPoint = e.GetPosition(inkCanvas);
+ element.CaptureMouse();
+ element.Cursor = Cursors.SizeAll;
+
+ e.Handled = true;
+ }
+ }
+
+ // 鼠标左键释放事件
+ private void Element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ if (sender is FrameworkElement element)
+ {
+ isDragging = false;
+ element.ReleaseMouseCapture();
+ element.Cursor = Cursors.Hand;
+
+ e.Handled = true;
+ }
+ }
+
+ // 鼠标移动事件
+ private void Element_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (sender is FrameworkElement element && isDragging && element.IsMouseCaptured)
+ {
+ var currentPoint = e.GetPosition(inkCanvas);
+
+ // 使用鼠标拖动的完整实现机制
+ ApplyMouseDragTransform(element, currentPoint, dragStartPoint);
+
+ dragStartPoint = currentPoint;
+ e.Handled = true;
+ }
+ }
+
+ // 鼠标滚轮事件 - 缩放
+ private void Element_MouseWheel(object sender, MouseWheelEventArgs e)
+ {
+ if (sender is FrameworkElement element)
+ {
+ // 使用滚轮缩放的核心机制
+ ApplyWheelScaleTransform(element, e);
+
+ e.Handled = true;
+ }
+ }
+
+ // 触摸操作事件
+ private void Element_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
+ {
+ if (sender is FrameworkElement element)
+ {
+ // 使用触摸拖动的完整实现
+ ApplyTouchManipulationTransform(element, e);
+
+ e.Handled = true;
+ }
+ }
+
+ // 触摸操作完成事件
+ private void Element_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
+ {
+ // 可以在这里添加操作完成后的处理逻辑
+ }
+
+ // 应用平移变换
+ private void ApplyTranslateTransform(FrameworkElement element, double deltaX, double deltaY)
+ {
+ if (element.RenderTransform is TransformGroup transformGroup)
+ {
+ var translateTransform = transformGroup.Children.OfType().FirstOrDefault();
+ if (translateTransform != null)
+ {
+ translateTransform.X += deltaX;
+ translateTransform.Y += deltaY;
+ }
+ }
+ }
+
+ // 应用缩放变换
+ private void ApplyScaleTransform(FrameworkElement element, double scaleFactor, Point center)
+ {
+ if (element.RenderTransform is TransformGroup transformGroup)
+ {
+ var scaleTransform = transformGroup.Children.OfType().FirstOrDefault();
+ if (scaleTransform != null)
+ {
+ // 设置缩放中心
+ scaleTransform.CenterX = center.X;
+ scaleTransform.CenterY = center.Y;
+
+ // 应用缩放
+ scaleTransform.ScaleX *= scaleFactor;
+ scaleTransform.ScaleY *= scaleFactor;
+
+ // 限制缩放范围
+ scaleTransform.ScaleX = Math.Max(0.1, Math.Min(scaleTransform.ScaleX, 5.0));
+ scaleTransform.ScaleY = Math.Max(0.1, Math.Min(scaleTransform.ScaleY, 5.0));
+ }
+ }
+ }
+
+ // 应用旋转变换
+ private void ApplyRotateTransform(FrameworkElement element, double angle)
+ {
+ if (element.RenderTransform is TransformGroup transformGroup)
+ {
+ var rotateTransform = transformGroup.Children.OfType().FirstOrDefault();
+ if (rotateTransform != null)
+ {
+ rotateTransform.Angle += angle;
+ }
+ }
+ }
+
+ // 选中元素
+ private void SelectElement(FrameworkElement element)
+ {
+ currentSelectedElement = element;
+ isElementSelected = true;
+
+ // 去除选中效果,避免蓝色底边问题
+ // 可以通过其他方式(如状态变量)来跟踪选中状态
+
+ // 确保选择框不显示,避免蓝色边框
+ if (GridInkCanvasSelectionCover != null)
+ {
+ GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
+ }
+
+ // 禁用InkCanvas的选择功能,去除控制点
+ if (inkCanvas != null)
+ {
+ // 清除当前选择
+ inkCanvas.Select(new StrokeCollection());
+ // 设置编辑模式为非选择模式
+ inkCanvas.EditingMode = InkCanvasEditingMode.None;
+ }
+ }
+
+ // 取消选中元素
+ private void UnselectElement(FrameworkElement element)
+ {
+ // 去除选中效果
+ isElementSelected = false;
+
+ // 确保选择框隐藏
+ if (GridInkCanvasSelectionCover != null)
+ {
+ GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
+ }
+
+ // 恢复InkCanvas的编辑模式
+ if (inkCanvas != null)
+ {
+ inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
+ }
+ }
+
+ // 应用矩阵变换到元素
+ private void ApplyElementMatrixTransform(FrameworkElement element, Matrix matrix)
+ {
+ if (element.RenderTransform is TransformGroup transformGroup)
+ {
+ // 创建MatrixTransform
+ var matrixTransform = new MatrixTransform(matrix);
+
+ // 将MatrixTransform添加到TransformGroup
+ transformGroup.Children.Add(matrixTransform);
+ }
+ }
+
+ // 滚轮缩放的核心机制
+ private void ApplyWheelScaleTransform(FrameworkElement element, MouseWheelEventArgs e)
+ {
+ try
+ {
+ // 根据滚轮方向确定缩放比例(向上1.1倍,向下0.9倍)
+ double scaleFactor = e.Delta > 0 ? 1.1 : 0.9;
+
+ // 计算选中元素的中心点作为缩放中心
+ var elementCenter = new Point(element.ActualWidth / 2, element.ActualHeight / 2);
+
+ // 创建 Matrix 对象并应用 ScaleAt 变换
+ var matrix = new Matrix();
+ matrix.ScaleAt(scaleFactor, scaleFactor, elementCenter.X, elementCenter.Y);
+
+ // 对选中的图片元素调用 ApplyElementMatrixTransform
+ ApplyElementMatrixTransform(element, matrix);
+
+ // 对选中的笔画应用 Transform 方法(如果有选中的笔画)
+ var selectedStrokes = inkCanvas.GetSelectedStrokes();
+ foreach (var stroke in selectedStrokes)
+ {
+ stroke.Transform(matrix, false);
+ }
+
+ LogHelper.WriteLogToFile($"滚轮缩放应用: 缩放因子={scaleFactor}, 中心点=({elementCenter.X}, {elementCenter.Y})");
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"滚轮缩放失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 矩阵变换的完整实现
+ private void ApplyMatrixTransformToElement(FrameworkElement element, Matrix matrix, bool saveHistory = true)
+ {
+ try
+ {
+ // 获取元素的 RenderTransform,如果不存在则创建新的 TransformGroup
+ TransformGroup transformGroup = element.RenderTransform as TransformGroup;
+ if (transformGroup == null)
+ {
+ transformGroup = new TransformGroup();
+ element.RenderTransform = transformGroup;
+ }
+
+ // 保存初始变换状态用于历史记录
+ var initialTransform = transformGroup.Clone() as TransformGroup;
+
+ // 创建新的 TransformGroup 并添加 MatrixTransform
+ var newTransformGroup = new TransformGroup();
+ newTransformGroup.Children.Add(new MatrixTransform(matrix));
+
+ // 将新的变换组添加到现有的变换组中
+ transformGroup.Children.Add(newTransformGroup);
+
+ // 如果启用了历史记录,提交变换历史
+ if (saveHistory)
+ {
+ CommitTransformHistory(element, initialTransform, transformGroup);
+ }
+
+ LogHelper.WriteLogToFile($"矩阵变换应用成功: 元素={element.Name}");
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"矩阵变换失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 鼠标拖动的完整实现机制
+ private void ApplyMouseDragTransform(FrameworkElement element, Point currentPoint, Point startPoint)
+ {
+ try
+ {
+ // 计算鼠标移动的位移向量
+ var delta = currentPoint - startPoint;
+
+ // 创建 Matrix 对象并应用 Translate 变换
+ var matrix = new Matrix();
+ matrix.Translate(delta.X, delta.Y);
+
+ // 对选中的图片元素应用矩阵变换
+ ApplyMatrixTransformToElement(element, matrix, false);
+
+ // 对选中的笔画应用变换
+ var selectedStrokes = inkCanvas.GetSelectedStrokes();
+ foreach (var stroke in selectedStrokes)
+ {
+ stroke.Transform(matrix, false);
+ }
+
+ // 更新选择框的位置(如果有选择框)
+ UpdateSelectionBorderPosition(delta);
+
+ LogHelper.WriteLogToFile($"鼠标拖动应用: 位移=({delta.X}, {delta.Y})");
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"鼠标拖动失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 更新选择框位置
+ private void UpdateSelectionBorderPosition(Vector delta)
+ {
+ try
+ {
+ // 这里可以添加更新选择框位置的逻辑
+ // 例如更新 BorderStrokeSelectionControl 的位置
+ if (BorderStrokeSelectionControl != null)
+ {
+ var currentMargin = BorderStrokeSelectionControl.Margin;
+ BorderStrokeSelectionControl.Margin = new Thickness(
+ currentMargin.Left + delta.X,
+ currentMargin.Top + delta.Y,
+ currentMargin.Right,
+ currentMargin.Bottom
+ );
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"更新选择框位置失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 提交变换历史
+ private void CommitTransformHistory(FrameworkElement element, TransformGroup initialTransform, TransformGroup finalTransform)
+ {
+ try
+ {
+ // 这里可以添加提交变换历史到时间机器的逻辑
+ // 例如记录变换前后的状态
+ LogHelper.WriteLogToFile($"变换历史已记录: 元素={element.Name}");
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"提交变换历史失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 触摸拖动的完整实现
+ private void ApplyTouchManipulationTransform(FrameworkElement element, ManipulationDeltaEventArgs e)
+ {
+ try
+ {
+ var md = e.DeltaManipulation;
+ var matrix = new Matrix();
+
+ // 支持单指拖动和多指手势
+ // 可以同时进行平移、旋转和缩放
+
+ // 通过 ManipulationDelta 获取手势变化信息
+ var translation = md.Translation;
+ var rotation = md.Rotation;
+ var scale = md.Scale;
+
+ // 应用平移
+ if (translation.X != 0 || translation.Y != 0)
+ {
+ matrix.Translate(translation.X, translation.Y);
+ }
+
+ // 支持两指缩放和旋转操作
+ if (e.Manipulators.Count() >= 2)
+ {
+ var center = e.ManipulationOrigin;
+
+ // 应用缩放
+ if (scale.X != 1.0 || scale.Y != 1.0)
+ {
+ matrix.ScaleAt(scale.X, scale.Y, center.X, center.Y);
+ }
+
+ // 应用旋转
+ if (rotation != 0)
+ {
+ matrix.RotateAt(rotation, center.X, center.Y);
+ }
+ }
+
+ // 应用变换到元素
+ ApplyMatrixTransformToElement(element, matrix, false);
+
+ // 应用变换到选中的笔画
+ var selectedStrokes = inkCanvas.GetSelectedStrokes();
+ foreach (var stroke in selectedStrokes)
+ {
+ stroke.Transform(matrix, false);
+ }
+
+ LogHelper.WriteLogToFile($"触摸操作应用: 平移=({translation.X}, {translation.Y}), 旋转={rotation}, 缩放=({scale.X}, {scale.Y})");
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"触摸操作失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
private async Task CreateAndCompressImageAsync(string filePath)
{
string savePath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "File Dependency");
@@ -455,5 +900,22 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile($"元素居中失败: {ex.Message}", LogHelper.LogType.Error);
}
}
+
+ // 初始化InkCanvas选择设置
+ private void InitializeInkCanvasSelectionSettings()
+ {
+ if (inkCanvas != null)
+ {
+ // 隐藏选择控制点 - 通过清除选择和设置编辑模式
+ inkCanvas.Select(new StrokeCollection());
+ inkCanvas.EditingMode = InkCanvasEditingMode.None;
+
+ // 隐藏选择框
+ if (GridInkCanvasSelectionCover != null)
+ {
+ GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
+ }
+ }
+ }
}
}
diff --git a/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs b/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
index 000f4f02..2ebc2189 100644
--- a/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ImageInsert.cs
@@ -183,6 +183,16 @@ namespace Ink_Canvas
string timestamp = "screenshot_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
+ // 初始化TransformGroup
+ InitializeScreenshotTransform(image);
+
+ // 设置截图属性,避免被InkCanvas选择系统处理
+ image.IsHitTestVisible = true;
+ image.Focusable = false;
+
+ // 初始化InkCanvas选择设置
+ InitializeInkCanvasSelectionSettings();
+
// 等待图片加载完成后再进行居中处理
image.Loaded += (sender, e) =>
{
@@ -190,6 +200,8 @@ namespace Ink_Canvas
Dispatcher.BeginInvoke(new Action(() =>
{
CenterAndScaleScreenshot(image);
+ // 绑定事件处理器
+ BindScreenshotEvents(image);
}), System.Windows.Threading.DispatcherPriority.Loaded);
};
@@ -208,6 +220,38 @@ namespace Ink_Canvas
}
}
+ // 初始化截图的TransformGroup
+ private void InitializeScreenshotTransform(System.Windows.Controls.Image image)
+ {
+ var transformGroup = new TransformGroup();
+ transformGroup.Children.Add(new ScaleTransform(1, 1));
+ transformGroup.Children.Add(new TranslateTransform(0, 0));
+ transformGroup.Children.Add(new RotateTransform(0));
+ image.RenderTransform = transformGroup;
+ }
+
+ // 绑定截图事件处理器
+ private void BindScreenshotEvents(System.Windows.Controls.Image image)
+ {
+ // 鼠标事件
+ image.MouseLeftButtonDown += Element_MouseLeftButtonDown;
+ image.MouseLeftButtonUp += Element_MouseLeftButtonUp;
+ image.MouseMove += Element_MouseMove;
+ image.MouseWheel += Element_MouseWheel;
+
+ // 触摸事件
+ image.IsManipulationEnabled = true;
+ image.ManipulationDelta += Element_ManipulationDelta;
+ image.ManipulationCompleted += Element_ManipulationCompleted;
+
+ // 设置光标
+ image.Cursor = System.Windows.Input.Cursors.Hand;
+
+ // 禁用InkCanvas对截图的选择处理
+ image.IsHitTestVisible = true;
+ image.Focusable = false;
+ }
+
// 专门为截图优化的居中缩放方法
private void CenterAndScaleScreenshot(System.Windows.Controls.Image image)
{
diff --git a/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs b/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs
index 346bbde6..6c6ab016 100644
--- a/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs
+++ b/Ink Canvas/MainWindow_cs/MW_SelectionGestures.cs
@@ -9,6 +9,7 @@ using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using Point = System.Windows.Point;
+using System.Linq;
namespace Ink_Canvas
{
@@ -306,10 +307,21 @@ namespace Ink_Canvas
private void inkCanvas_SelectionChanged(object sender, EventArgs e)
{
if (isProgramChangeStrokeSelection) return;
+
+ // 检查是否有图片元素被选中
+ var selectedElements = inkCanvas.GetSelectedElements();
+ bool hasImageElement = selectedElements.Any(element => element is System.Windows.Controls.Image);
+
+ // 如果有图片元素被选中,不显示选择框
+ if (hasImageElement)
+ {
+ GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
+ return;
+ }
+
if (inkCanvas.GetSelectedStrokes().Count == 0)
{
GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
-
}
else
{
@@ -317,7 +329,6 @@ namespace Ink_Canvas
BorderStrokeSelectionClone.Background = Brushes.Transparent;
isStrokeSelectionCloneOn = false;
updateBorderStrokeSelectionControlLocation();
-
}
}
From b64cefad464c9e945f13181148816848a4166413 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 22:54:16 +0800
Subject: [PATCH 11/23] =?UTF-8?q?improve:=E6=8F=92=E5=85=A5=E5=9B=BE?=
=?UTF-8?q?=E7=89=87=E5=8F=8A=E5=A2=A8=E8=BF=B9=E5=B9=B3=E6=BB=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Helpers/AdvancedBezierSmoothing.cs | 174 ++++++++--
.../HardwareAcceleratedInkProcessor.cs | 56 ---
Ink Canvas/Helpers/ImprovedBezierSmoothing.cs | 325 ++++++++++++++++++
Ink Canvas/Helpers/InkSmoothingConfig.cs | 155 +++++++++
Ink Canvas/Helpers/InkSmoothingManager.cs | 6 +-
.../MainWindow_cs/MW_ClipboardHandler.cs | 43 +++
.../MainWindow_cs/MW_ElementsControls.cs | 9 +-
.../MainWindow_cs/MW_FloatingBarIcons.cs | 109 ++++++
8 files changed, 786 insertions(+), 91 deletions(-)
create mode 100644 Ink Canvas/Helpers/ImprovedBezierSmoothing.cs
create mode 100644 Ink Canvas/Helpers/InkSmoothingConfig.cs
diff --git a/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs b/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs
index 56a2e6f3..a9015cd6 100644
--- a/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs
+++ b/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs
@@ -7,11 +7,12 @@ using System.Threading.Tasks;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Threading;
+using System.Windows;
namespace Ink_Canvas.Helpers
{
///
- /// 异步硬件加速的墨迹平滑处理器
+ /// 改进的异步硬件加速墨迹平滑处理器,使用优化的三次贝塞尔曲线拟合
///
public class AsyncAdvancedBezierSmoothing
{
@@ -26,11 +27,13 @@ namespace Ink_Canvas.Helpers
_processingTasks = new ConcurrentDictionary();
}
- public double SmoothingStrength { get; set; } = 0.3; // 大幅降低强度
- public double ResampleInterval { get; set; } = 3.0; // 大幅增加间隔减少点数
- public int InterpolationSteps { get; set; } = 8; // 从4增加到8,提高插值步数
+ public double SmoothingStrength { get; set; } = 0.4; // 适中的平滑强度
+ public double ResampleInterval { get; set; } = 2.5; // 适中的重采样间隔
+ public int InterpolationSteps { get; set; } = 12; // 增加插值步数提高精度
public bool UseHardwareAcceleration { get; set; } = true;
public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount;
+ public bool UseAdaptiveInterpolation { get; set; } = true; // 自适应插值
+ public double CurveTension { get; set; } = 0.3; // 曲线张力参数
///
/// 异步平滑笔画
@@ -89,16 +92,16 @@ namespace Ink_Canvas.Helpers
cancellationToken.ThrowIfCancellationRequested();
- // 简化处理:只进行轻度平滑,避免点数爆炸
- var smoothedPoints = ApplyLightSmoothing(originalPoints);
+ // 使用改进的贝塞尔曲线拟合
+ var smoothedPoints = ApplyImprovedBezierSmoothing(originalPoints);
cancellationToken.ThrowIfCancellationRequested();
- // 确保点数不会过多
- if (smoothedPoints.Length > originalPoints.Length * 2)
+ // 确保点数合理
+ if (smoothedPoints.Length > originalPoints.Length * 3)
{
- // 如果点数增加太多,回退到原始笔画
- return stroke;
+ // 如果点数增加太多,进行重采样
+ smoothedPoints = ResampleEquidistantOptimized(smoothedPoints, ResampleInterval);
}
// 创建平滑后的笔画
@@ -111,34 +114,155 @@ namespace Ink_Canvas.Helpers
}
///
- /// 轻度平滑处理,避免点数爆炸
+ /// 改进的贝塞尔曲线平滑处理
///
- private StylusPoint[] ApplyLightSmoothing(StylusPoint[] points)
+ private StylusPoint[] ApplyImprovedBezierSmoothing(StylusPoint[] points)
{
- if (points.Length < 3) return points;
+ if (points.Length < 4) return points;
var result = new List();
- result.Add(points[0]); // 保持第一个点
+
+ // 添加第一个点
+ result.Add(points[0]);
- // 简单的3点平均平滑
- for (int i = 1; i < points.Length - 1; i++)
+ // 使用滑动窗口进行贝塞尔曲线拟合
+ for (int i = 0; i <= points.Length - 4; i++)
{
- var prev = points[i - 1];
- var curr = points[i];
- var next = points[i + 1];
+ var p0 = points[i];
+ var p1 = points[i + 1];
+ var p2 = points[i + 2];
+ var p3 = points[i + 3];
- // 3点平均
- double x = (prev.X + curr.X + next.X) / 3.0;
- double y = (prev.Y + curr.Y + next.Y) / 3.0;
- float pressure = (prev.PressureFactor + curr.PressureFactor + next.PressureFactor) / 3.0f;
+ // 计算改进的控制点
+ var controlPoints = CalculateImprovedControlPoints(p0, p1, p2, p3);
+
+ // 自适应插值步数
+ int steps = UseAdaptiveInterpolation ?
+ CalculateAdaptiveSteps(p0, p1, p2, p3) : InterpolationSteps;
- result.Add(new StylusPoint(x, y, Math.Max(pressure, 0.1f)));
+ // 生成贝塞尔曲线点
+ for (int j = 1; j <= steps; j++) // 从1开始避免重复第一个点
+ {
+ double t = (double)j / steps;
+ var bezierPoint = CubicBezierWithControlPoints(controlPoints, t, p0, p3);
+ result.Add(bezierPoint);
+ }
}
- result.Add(points[points.Length - 1]); // 保持最后一个点
+ // 添加最后一个点
+ result.Add(points[points.Length - 1]);
return result.ToArray();
}
+
+ ///
+ /// 计算改进的控制点
+ ///
+ private (Point cp1, Point cp2) CalculateImprovedControlPoints(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
+ {
+ // 计算切线方向
+ var tangent1 = new Vector(p1.X - p0.X, p1.Y - p0.Y);
+ var tangent2 = new Vector(p3.X - p2.X, p3.Y - p2.Y);
+
+ // 归一化切线
+ if (tangent1.Length > 0) tangent1.Normalize();
+ if (tangent2.Length > 0) tangent2.Normalize();
+
+ // 计算控制点距离(基于点间距离)
+ double dist1 = Math.Sqrt((p1.X - p0.X) * (p1.X - p0.X) + (p1.Y - p0.Y) * (p1.Y - p0.Y));
+ double dist2 = Math.Sqrt((p3.X - p2.X) * (p3.X - p2.X) + (p3.Y - p2.Y) * (p3.Y - p2.Y));
+
+ double controlDist1 = dist1 * CurveTension;
+ double controlDist2 = dist2 * CurveTension;
+
+ // 计算控制点
+ var cp1 = new Point(
+ p1.X + tangent1.X * controlDist1,
+ p1.Y + tangent1.Y * controlDist1
+ );
+
+ var cp2 = new Point(
+ p2.X - tangent2.X * controlDist2,
+ p2.Y - tangent2.Y * controlDist2
+ );
+
+ return (cp1, cp2);
+ }
+
+ ///
+ /// 自适应插值步数计算
+ ///
+ private int CalculateAdaptiveSteps(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
+ {
+ // 基于曲线长度和复杂度计算步数
+ double totalLength = 0;
+ totalLength += Math.Sqrt((p1.X - p0.X) * (p1.X - p0.X) + (p1.Y - p0.Y) * (p1.Y - p0.Y));
+ totalLength += Math.Sqrt((p2.X - p1.X) * (p2.X - p1.X) + (p2.Y - p1.Y) * (p2.Y - p1.Y));
+ totalLength += Math.Sqrt((p3.X - p2.X) * (p3.X - p2.X) + (p3.Y - p2.Y) * (p3.Y - p2.Y));
+
+ // 计算曲率(简化版本)
+ double curvature = CalculateCurvature(p0, p1, p2, p3);
+
+ // 基于长度和曲率计算步数
+ int baseSteps = Math.Max(8, Math.Min(20, (int)(totalLength / 10)));
+ int curvatureSteps = (int)(curvature * 10);
+
+ return Math.Max(InterpolationSteps, Math.Min(24, baseSteps + curvatureSteps));
+ }
+
+ ///
+ /// 计算曲率(简化版本)
+ ///
+ private double CalculateCurvature(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
+ {
+ // 计算三个向量的角度变化
+ var v1 = new Vector(p1.X - p0.X, p1.Y - p0.Y);
+ var v2 = new Vector(p2.X - p1.X, p2.Y - p1.Y);
+ var v3 = new Vector(p3.X - p2.X, p3.Y - p2.Y);
+
+ if (v1.Length == 0 || v2.Length == 0 || v3.Length == 0) return 0;
+
+ v1.Normalize();
+ v2.Normalize();
+ v3.Normalize();
+
+ // 计算角度变化
+ double angle1 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v1, v2))));
+ double angle2 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v2, v3))));
+
+ return (angle1 + angle2) / Math.PI; // 归一化到0-1
+ }
+
+ ///
+ /// 使用控制点的三次贝塞尔曲线计算
+ ///
+ private StylusPoint CubicBezierWithControlPoints((Point cp1, Point cp2) controlPoints, double t, StylusPoint p0, StylusPoint p3)
+ {
+ var p1 = controlPoints.cp1;
+ var p2 = controlPoints.cp2;
+
+ double u = 1 - t;
+ double tt = t * t;
+ double uu = u * u;
+ double uuu = uu * u;
+ double ttt = tt * t;
+
+ // 预计算系数
+ double c0 = uuu;
+ double c1 = 3 * uu * t;
+ double c2 = 3 * u * tt;
+ double c3 = ttt;
+
+ double x = c0 * p0.X + c1 * p1.X + c2 * p2.X + c3 * p3.X;
+ double y = c0 * p0.Y + c1 * p1.Y + c2 * p2.Y + c3 * p3.Y;
+
+ // 插值压力值
+ float pressure = (float)(p0.PressureFactor * u + p3.PressureFactor * t);
+ pressure = Math.Max(pressure, 0.1f);
+
+ return new StylusPoint(x, y, pressure);
+ }
+
///
/// 硬件加速的向量化指数平滑
///
diff --git a/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs b/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs
index ccea253c..1ca4689f 100644
--- a/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs
+++ b/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs
@@ -197,61 +197,5 @@ namespace Ink_Canvas.Helpers
}
}
- ///
- /// 质量配置枚举
- ///
- public enum InkSmoothingQuality
- {
- HighPerformance = 0, // 高性能低质量
- Balanced = 1, // 平衡
- HighQuality = 2 // 高质量低性能
- }
- ///
- /// 墨迹平滑配置
- ///
- public class InkSmoothingConfig
- {
- public InkSmoothingQuality Quality { get; set; } = InkSmoothingQuality.HighQuality;
- public bool UseHardwareAcceleration { get; set; } = true;
- public bool UseAsyncProcessing { get; set; } = true;
- public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount;
- public double SmoothingStrength { get; set; } = 0.8; // 高质量模式的平滑强度
- public double ResampleInterval { get; set; } = 0.8; // 高质量模式的重采样间隔
- public int InterpolationSteps { get; set; } = 64; // 高质量模式的插值步数
-
- public static InkSmoothingConfig FromSettings()
- {
- return new InkSmoothingConfig
- {
- Quality = (InkSmoothingQuality)MainWindow.Settings.Canvas.InkSmoothingQuality,
- UseHardwareAcceleration = MainWindow.Settings.Canvas.UseHardwareAcceleration,
- UseAsyncProcessing = MainWindow.Settings.Canvas.UseAsyncInkSmoothing,
- MaxConcurrentTasks = MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ?
- MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount
- };
- }
-
- public void ApplyQualitySettings()
- {
- switch (Quality)
- {
- case InkSmoothingQuality.HighPerformance:
- SmoothingStrength = 0.4;
- ResampleInterval = 2.0;
- InterpolationSteps = 16;
- break;
- case InkSmoothingQuality.Balanced:
- SmoothingStrength = 0.6;
- ResampleInterval = 1.2;
- InterpolationSteps = 32;
- break;
- case InkSmoothingQuality.HighQuality:
- SmoothingStrength = 0.8;
- ResampleInterval = 0.8;
- InterpolationSteps = 64;
- break;
- }
- }
- }
}
diff --git a/Ink Canvas/Helpers/ImprovedBezierSmoothing.cs b/Ink Canvas/Helpers/ImprovedBezierSmoothing.cs
new file mode 100644
index 00000000..f249885d
--- /dev/null
+++ b/Ink Canvas/Helpers/ImprovedBezierSmoothing.cs
@@ -0,0 +1,325 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Ink;
+using System.Windows.Input;
+
+namespace Ink_Canvas.Helpers
+{
+ ///
+ /// 改进的三次贝塞尔曲线平滑算法
+ ///
+ public class ImprovedBezierSmoothing
+ {
+ private readonly InkSmoothingConfig _config;
+
+ public ImprovedBezierSmoothing(InkSmoothingConfig config = null)
+ {
+ _config = config ?? new InkSmoothingConfig();
+ }
+
+ ///
+ /// 使用改进的贝塞尔曲线算法平滑笔画
+ ///
+ public Stroke SmoothStroke(Stroke originalStroke)
+ {
+ if (originalStroke == null || originalStroke.StylusPoints.Count < 3)
+ return originalStroke;
+
+ var originalPoints = originalStroke.StylusPoints.ToArray();
+
+ // 预处理:去除噪声点
+ var cleanedPoints = RemoveNoisePoints(originalPoints);
+
+ // 使用改进的贝塞尔曲线拟合
+ var smoothedPoints = ApplyCubicBezierSmoothing(cleanedPoints);
+
+ // 后处理:重采样和优化
+ var finalPoints = PostProcessPoints(smoothedPoints);
+
+ return new Stroke(new StylusPointCollection(finalPoints))
+ {
+ DrawingAttributes = originalStroke.DrawingAttributes.Clone()
+ };
+ }
+
+ ///
+ /// 去除噪声点
+ ///
+ private StylusPoint[] RemoveNoisePoints(StylusPoint[] points)
+ {
+ if (points.Length < 3) return points;
+
+ var result = new List { points[0] };
+ double minDistance = _config.ResampleInterval * 0.5;
+
+ for (int i = 1; i < points.Length - 1; i++)
+ {
+ var prev = result[result.Count - 1];
+ var curr = points[i];
+ var next = points[i + 1];
+
+ // 计算到前一个点的距离
+ double distToPrev = Math.Sqrt((curr.X - prev.X) * (curr.X - prev.X) +
+ (curr.Y - prev.Y) * (curr.Y - prev.Y));
+
+ // 如果距离太近,跳过这个点
+ if (distToPrev < minDistance)
+ continue;
+
+ // 检查是否为异常点(与前后点形成锐角)
+ if (IsOutlierPoint(prev, curr, next))
+ continue;
+
+ result.Add(curr);
+ }
+
+ result.Add(points[points.Length - 1]);
+ return result.ToArray();
+ }
+
+ ///
+ /// 检查是否为异常点
+ ///
+ private bool IsOutlierPoint(StylusPoint prev, StylusPoint curr, StylusPoint next)
+ {
+ var v1 = new Vector(curr.X - prev.X, curr.Y - prev.Y);
+ var v2 = new Vector(next.X - curr.X, next.Y - curr.Y);
+
+ if (v1.Length == 0 || v2.Length == 0) return false;
+
+ v1.Normalize();
+ v2.Normalize();
+
+ double dotProduct = Vector.Multiply(v1, v2);
+ double angle = Math.Acos(Math.Max(-1, Math.Min(1, dotProduct)));
+
+ // 如果角度小于30度,认为是异常点
+ return angle < Math.PI / 6;
+ }
+
+ ///
+ /// 应用三次贝塞尔曲线平滑
+ ///
+ private StylusPoint[] ApplyCubicBezierSmoothing(StylusPoint[] points)
+ {
+ if (points.Length < 4) return points;
+
+ var result = new List();
+ result.Add(points[0]);
+
+ // 使用滑动窗口进行贝塞尔曲线拟合
+ for (int i = 0; i <= points.Length - 4; i++)
+ {
+ var p0 = points[i];
+ var p1 = points[i + 1];
+ var p2 = points[i + 2];
+ var p3 = points[i + 3];
+
+ // 计算控制点
+ var controlPoints = CalculateOptimalControlPoints(p0, p1, p2, p3);
+
+ // 计算插值步数
+ int steps = CalculateInterpolationSteps(p0, p1, p2, p3);
+
+ // 生成贝塞尔曲线点
+ for (int j = 1; j <= steps; j++)
+ {
+ double t = (double)j / steps;
+ var bezierPoint = CalculateBezierPoint(p0, controlPoints.cp1, controlPoints.cp2, p3, t);
+ result.Add(bezierPoint);
+ }
+ }
+
+ result.Add(points[points.Length - 1]);
+ return result.ToArray();
+ }
+
+ ///
+ /// 计算最优控制点
+ ///
+ private (Point cp1, Point cp2) CalculateOptimalControlPoints(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
+ {
+ // 计算切线方向
+ var tangent1 = CalculateTangent(p0, p1, p2);
+ var tangent2 = CalculateTangent(p1, p2, p3);
+
+ // 计算控制点距离
+ double dist1 = CalculateDistance(p0, p1);
+ double dist2 = CalculateDistance(p2, p3);
+
+ double controlDist1 = dist1 * _config.CurveTension;
+ double controlDist2 = dist2 * _config.CurveTension;
+
+ // 计算控制点
+ var cp1 = new Point(
+ p1.X + tangent1.X * controlDist1,
+ p1.Y + tangent1.Y * controlDist1
+ );
+
+ var cp2 = new Point(
+ p2.X - tangent2.X * controlDist2,
+ p2.Y - tangent2.Y * controlDist2
+ );
+
+ return (cp1, cp2);
+ }
+
+ ///
+ /// 计算切线方向
+ ///
+ private Vector CalculateTangent(StylusPoint p0, StylusPoint p1, StylusPoint p2)
+ {
+ var v1 = new Vector(p1.X - p0.X, p1.Y - p0.Y);
+ var v2 = new Vector(p2.X - p1.X, p2.Y - p1.Y);
+
+ // 如果向量长度为零,返回零向量
+ if (v1.Length == 0 || v2.Length == 0)
+ return new Vector(0, 0);
+
+ v1.Normalize();
+ v2.Normalize();
+
+ // 返回平均方向
+ var tangent = (v1 + v2) / 2;
+ if (tangent.Length > 0)
+ tangent.Normalize();
+
+ return tangent;
+ }
+
+ ///
+ /// 计算两点间距离
+ ///
+ private double CalculateDistance(StylusPoint p1, StylusPoint p2)
+ {
+ double dx = p2.X - p1.X;
+ double dy = p2.Y - p1.Y;
+ return Math.Sqrt(dx * dx + dy * dy);
+ }
+
+ ///
+ /// 计算插值步数
+ ///
+ private int CalculateInterpolationSteps(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
+ {
+ if (!_config.UseAdaptiveInterpolation)
+ return _config.InterpolationSteps;
+
+ // 计算曲线长度
+ double totalLength = CalculateDistance(p0, p1) + CalculateDistance(p1, p2) + CalculateDistance(p2, p3);
+
+ // 计算曲率
+ double curvature = CalculateCurvature(p0, p1, p2, p3);
+
+ // 基于长度和曲率计算步数
+ int baseSteps = Math.Max(8, Math.Min(20, (int)(totalLength / 10)));
+ int curvatureSteps = (int)(curvature * 15);
+
+ return Math.Max(_config.InterpolationSteps, Math.Min(30, baseSteps + curvatureSteps));
+ }
+
+ ///
+ /// 计算曲率
+ ///
+ private double CalculateCurvature(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3)
+ {
+ var v1 = new Vector(p1.X - p0.X, p1.Y - p0.Y);
+ var v2 = new Vector(p2.X - p1.X, p2.Y - p1.Y);
+ var v3 = new Vector(p3.X - p2.X, p3.Y - p2.Y);
+
+ if (v1.Length == 0 || v2.Length == 0 || v3.Length == 0) return 0;
+
+ v1.Normalize();
+ v2.Normalize();
+ v3.Normalize();
+
+ // 计算角度变化
+ double angle1 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v1, v2))));
+ double angle2 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v2, v3))));
+
+ return (angle1 + angle2) / Math.PI; // 归一化到0-1
+ }
+
+ ///
+ /// 计算贝塞尔曲线上的点
+ ///
+ private StylusPoint CalculateBezierPoint(StylusPoint p0, Point cp1, Point cp2, StylusPoint p3, double t)
+ {
+ double u = 1 - t;
+ double tt = t * t;
+ double uu = u * u;
+ double uuu = uu * u;
+ double ttt = tt * t;
+
+ // 预计算系数
+ double c0 = uuu;
+ double c1 = 3 * uu * t;
+ double c2 = 3 * u * tt;
+ double c3 = ttt;
+
+ double x = c0 * p0.X + c1 * cp1.X + c2 * cp2.X + c3 * p3.X;
+ double y = c0 * p0.Y + c1 * cp1.Y + c2 * cp2.Y + c3 * p3.Y;
+
+ // 插值压力值
+ float pressure = (float)(p0.PressureFactor * u + p3.PressureFactor * t);
+ pressure = Math.Max(pressure, 0.1f);
+
+ return new StylusPoint(x, y, pressure);
+ }
+
+ ///
+ /// 后处理点集
+ ///
+ private StylusPoint[] PostProcessPoints(StylusPoint[] points)
+ {
+ if (points.Length == 0) return points;
+
+ // 如果点数过多,进行重采样
+ if (points.Length > _config.MaxPointsPerStroke)
+ {
+ return ResamplePoints(points, _config.ResampleInterval);
+ }
+
+ return points;
+ }
+
+ ///
+ /// 重采样点集
+ ///
+ private StylusPoint[] ResamplePoints(StylusPoint[] points, double interval)
+ {
+ var result = new List { points[0] };
+ double accumulated = 0;
+
+ for (int i = 1; i < points.Length; i++)
+ {
+ var prev = result[result.Count - 1];
+ var curr = points[i];
+ double dx = curr.X - prev.X;
+ double dy = curr.Y - prev.Y;
+ double dist = Math.Sqrt(dx * dx + dy * dy);
+
+ if (dist + accumulated >= interval)
+ {
+ double t = (interval - accumulated) / dist;
+ double x = prev.X + t * dx;
+ double y = prev.Y + t * dy;
+ float pressure = (float)(prev.PressureFactor * (1 - t) + curr.PressureFactor * t);
+ pressure = Math.Max(pressure, 0.1f);
+
+ result.Add(new StylusPoint(x, y, pressure));
+ accumulated = 0;
+ i--; // 重新处理当前点
+ }
+ else
+ {
+ accumulated += dist;
+ }
+ }
+
+ return result.ToArray();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Ink Canvas/Helpers/InkSmoothingConfig.cs b/Ink Canvas/Helpers/InkSmoothingConfig.cs
new file mode 100644
index 00000000..e57aad38
--- /dev/null
+++ b/Ink Canvas/Helpers/InkSmoothingConfig.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Configuration;
+
+namespace Ink_Canvas.Helpers
+{
+ ///
+ /// 墨迹平滑配置类
+ ///
+ public class InkSmoothingConfig
+ {
+ // 基本平滑参数
+ public double SmoothingStrength { get; set; } = 0.4;
+ public double ResampleInterval { get; set; } = 2.5;
+ public int InterpolationSteps { get; set; } = 12;
+
+ // 贝塞尔曲线参数
+ public bool UseAdaptiveInterpolation { get; set; } = true;
+ public double CurveTension { get; set; } = 0.3;
+ public double MinCurvatureThreshold { get; set; } = 0.1;
+ public double MaxCurvatureThreshold { get; set; } = 0.8;
+
+ // 性能参数
+ public bool UseHardwareAcceleration { get; set; } = true;
+ public bool UseAsyncProcessing { get; set; } = true;
+ public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount;
+ public int MaxPointsPerStroke { get; set; } = 10000;
+
+ // 质量设置
+ public SmoothingQuality Quality { get; set; } = SmoothingQuality.Balanced;
+
+ public enum SmoothingQuality
+ {
+ Performance, // 性能优先
+ Balanced, // 平衡
+ Quality // 质量优先
+ }
+
+ // 兼容性枚举
+ public enum InkSmoothingQuality
+ {
+ HighPerformance = 0, // 高性能低质量
+ Balanced = 1, // 平衡
+ HighQuality = 2 // 高质量低性能
+ }
+
+ ///
+ /// 从设置中加载配置
+ ///
+ public static InkSmoothingConfig FromSettings()
+ {
+ var config = new InkSmoothingConfig();
+
+ try
+ {
+ // 尝试从MainWindow.Settings加载配置(兼容性)
+ if (MainWindow.Settings?.Canvas != null)
+ {
+ config.Quality = (SmoothingQuality)MainWindow.Settings.Canvas.InkSmoothingQuality;
+ config.UseHardwareAcceleration = MainWindow.Settings.Canvas.UseHardwareAcceleration;
+ config.UseAsyncProcessing = MainWindow.Settings.Canvas.UseAsyncInkSmoothing;
+ config.MaxConcurrentTasks = MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ?
+ MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount;
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"加载平滑配置失败: {ex.Message}");
+ }
+
+ return config;
+ }
+
+ ///
+ /// 应用质量设置
+ ///
+ public void ApplyQualitySettings()
+ {
+ switch (Quality)
+ {
+ case SmoothingQuality.Performance:
+ SmoothingStrength = 0.2;
+ ResampleInterval = 4.0;
+ InterpolationSteps = 6;
+ UseAdaptiveInterpolation = false;
+ CurveTension = 0.2;
+ MaxConcurrentTasks = Math.Max(1, Environment.ProcessorCount / 2);
+ break;
+
+ case SmoothingQuality.Balanced:
+ SmoothingStrength = 0.4;
+ ResampleInterval = 2.5;
+ InterpolationSteps = 12;
+ UseAdaptiveInterpolation = true;
+ CurveTension = 0.3;
+ MaxConcurrentTasks = Environment.ProcessorCount;
+ break;
+
+ case SmoothingQuality.Quality:
+ SmoothingStrength = 0.6;
+ ResampleInterval = 1.5;
+ InterpolationSteps = 20;
+ UseAdaptiveInterpolation = true;
+ CurveTension = 0.4;
+ MaxConcurrentTasks = Environment.ProcessorCount;
+ break;
+ }
+ }
+
+ ///
+ /// 保存配置到设置
+ ///
+ public void SaveToSettings()
+ {
+ try
+ {
+ // 尝试保存到MainWindow.Settings(兼容性)
+ if (MainWindow.Settings?.Canvas != null)
+ {
+ MainWindow.Settings.Canvas.InkSmoothingQuality = (int)Quality;
+ MainWindow.Settings.Canvas.UseHardwareAcceleration = UseHardwareAcceleration;
+ MainWindow.Settings.Canvas.UseAsyncInkSmoothing = UseAsyncProcessing;
+ MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks = MaxConcurrentTasks;
+
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"保存平滑配置失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 验证配置参数
+ ///
+ public bool Validate()
+ {
+ return SmoothingStrength >= 0.0 && SmoothingStrength <= 1.0 &&
+ ResampleInterval > 0.0 &&
+ InterpolationSteps > 0 && InterpolationSteps <= 50 &&
+ CurveTension >= 0.0 && CurveTension <= 1.0 &&
+ MaxConcurrentTasks > 0 &&
+ MaxPointsPerStroke > 0;
+ }
+
+ ///
+ /// 获取配置摘要
+ ///
+ public string GetSummary()
+ {
+ return $"质量: {Quality}, 强度: {SmoothingStrength:F2}, 间隔: {ResampleInterval:F1}, " +
+ $"步数: {InterpolationSteps}, 自适应: {UseAdaptiveInterpolation}, " +
+ $"张力: {CurveTension:F2}, 硬件加速: {UseHardwareAcceleration}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/Ink Canvas/Helpers/InkSmoothingManager.cs b/Ink Canvas/Helpers/InkSmoothingManager.cs
index 3d2e5db5..489cdda3 100644
--- a/Ink Canvas/Helpers/InkSmoothingManager.cs
+++ b/Ink Canvas/Helpers/InkSmoothingManager.cs
@@ -197,7 +197,7 @@ namespace Ink_Canvas.Helpers
if (processorCount >= 4 && isHardwareAccelerated)
{
// 降低高质量模式的门槛,4核以上且支持硬件加速就使用高质量
- config.Quality = InkSmoothingQuality.HighQuality;
+ config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.HighQuality;
config.UseHardwareAcceleration = true;
config.UseAsyncProcessing = true;
config.MaxConcurrentTasks = Math.Min(processorCount, 8);
@@ -205,7 +205,7 @@ namespace Ink_Canvas.Helpers
else if (processorCount >= 2)
{
// 2核以上使用平衡模式
- config.Quality = InkSmoothingQuality.Balanced;
+ config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.Balanced;
config.UseHardwareAcceleration = isHardwareAccelerated;
config.UseAsyncProcessing = true;
config.MaxConcurrentTasks = Math.Min(processorCount, 4);
@@ -213,7 +213,7 @@ namespace Ink_Canvas.Helpers
else
{
// 单核或性能较低的设备使用高性能模式
- config.Quality = InkSmoothingQuality.HighPerformance;
+ config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.HighPerformance;
config.UseHardwareAcceleration = false;
config.UseAsyncProcessing = false;
config.MaxConcurrentTasks = 1;
diff --git a/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs b/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs
index 6a136cd3..0db312ab 100644
--- a/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs
@@ -3,8 +3,10 @@ using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media.Imaging;
+using System.Windows.Media;
namespace Ink_Canvas
{
@@ -129,6 +131,16 @@ namespace Ink_Canvas
string timestamp = "img_clipboard_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
+ // 初始化TransformGroup
+ if (image is FrameworkElement element)
+ {
+ var transformGroup = new TransformGroup();
+ transformGroup.Children.Add(new ScaleTransform(1, 1));
+ transformGroup.Children.Add(new TranslateTransform(0, 0));
+ transformGroup.Children.Add(new RotateTransform(0));
+ element.RenderTransform = transformGroup;
+ }
+
// 设置位置
if (position.HasValue)
{
@@ -142,9 +154,40 @@ namespace Ink_Canvas
CenterAndScaleElement(image);
}
+ // 设置图片属性,避免被InkCanvas选择系统处理
+ image.IsHitTestVisible = true;
+ image.Focusable = false;
+
+ // 初始化InkCanvas选择设置
+ if (inkCanvas != null)
+ {
+ // 清除当前选择,避免显示控制点
+ inkCanvas.Select(new StrokeCollection());
+ // 设置编辑模式为非选择模式
+ inkCanvas.EditingMode = InkCanvasEditingMode.None;
+ }
+
// 添加到画布
inkCanvas.Children.Add(image);
+ // 绑定事件处理器
+ if (image is FrameworkElement elementForEvents)
+ {
+ // 鼠标事件
+ elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown;
+ elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp;
+ elementForEvents.MouseMove += Element_MouseMove;
+ elementForEvents.MouseWheel += Element_MouseWheel;
+
+ // 触摸事件
+ elementForEvents.IsManipulationEnabled = true;
+ elementForEvents.ManipulationDelta += Element_ManipulationDelta;
+ elementForEvents.ManipulationCompleted += Element_ManipulationCompleted;
+
+ // 设置光标
+ elementForEvents.Cursor = Cursors.Hand;
+ }
+
// 提交到历史记录
timeMachine.CommitElementInsertHistory(image);
diff --git a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
index 37c553f6..4c2e89c0 100644
--- a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
@@ -906,15 +906,10 @@ namespace Ink_Canvas
{
if (inkCanvas != null)
{
- // 隐藏选择控制点 - 通过清除选择和设置编辑模式
+ // 清除当前选择,避免显示控制点
inkCanvas.Select(new StrokeCollection());
+ // 设置编辑模式为非选择模式
inkCanvas.EditingMode = InkCanvasEditingMode.None;
-
- // 隐藏选择框
- if (GridInkCanvasSelectionCover != null)
- {
- GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
- }
}
}
}
diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
index a3ba95ea..f6fe9c6f 100644
--- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
+++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
@@ -15,6 +15,7 @@ using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using Application = System.Windows.Application;
using Button = System.Windows.Controls.Button;
+using Cursors = System.Windows.Input.Cursors;
using HorizontalAlignment = System.Windows.HorizontalAlignment;
using Image = System.Windows.Controls.Image;
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
@@ -2828,6 +2829,72 @@ namespace Ink_Canvas
}
}
+ // 新增:插入图片方法
+ private async void InsertImage_MouseUp_New(object sender, MouseButtonEventArgs e)
+ {
+ var dialog = new OpenFileDialog
+ {
+ Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
+ };
+ if (dialog.ShowDialog() == true)
+ {
+ string filePath = dialog.FileName;
+ Image image = await CreateAndCompressImageAsync(filePath);
+ if (image != null)
+ {
+ string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
+ image.Name = timestamp;
+
+ // 初始化TransformGroup
+ if (image is FrameworkElement element)
+ {
+ var transformGroup = new TransformGroup();
+ transformGroup.Children.Add(new ScaleTransform(1, 1));
+ transformGroup.Children.Add(new TranslateTransform(0, 0));
+ transformGroup.Children.Add(new RotateTransform(0));
+ element.RenderTransform = transformGroup;
+ }
+
+ CenterAndScaleElement(image);
+
+ // 设置图片属性,避免被InkCanvas选择系统处理
+ image.IsHitTestVisible = true;
+ image.Focusable = false;
+
+ // 初始化InkCanvas选择设置
+ if (inkCanvas != null)
+ {
+ // 清除当前选择,避免显示控制点
+ inkCanvas.Select(new StrokeCollection());
+ // 设置编辑模式为非选择模式
+ inkCanvas.EditingMode = InkCanvasEditingMode.None;
+ }
+
+ inkCanvas.Children.Add(image);
+
+ // 绑定事件处理器
+ if (image is FrameworkElement elementForEvents)
+ {
+ // 鼠标事件
+ elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown;
+ elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp;
+ elementForEvents.MouseMove += Element_MouseMove;
+ elementForEvents.MouseWheel += Element_MouseWheel;
+
+ // 触摸事件
+ elementForEvents.IsManipulationEnabled = true;
+ elementForEvents.ManipulationDelta += Element_ManipulationDelta;
+ elementForEvents.ManipulationCompleted += Element_ManipulationCompleted;
+
+ // 设置光标
+ elementForEvents.Cursor = Cursors.Hand;
+ }
+
+ timeMachine.CommitElementInsertHistory(image);
+ }
+ }
+ }
+
// Keep the old method for backward compatibility
private async void InsertImage_MouseUp(object sender, MouseButtonEventArgs e)
{
@@ -2844,9 +2911,51 @@ namespace Ink_Canvas
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
+ // 初始化TransformGroup
+ if (image is FrameworkElement element)
+ {
+ var transformGroup = new TransformGroup();
+ transformGroup.Children.Add(new ScaleTransform(1, 1));
+ transformGroup.Children.Add(new TranslateTransform(0, 0));
+ transformGroup.Children.Add(new RotateTransform(0));
+ element.RenderTransform = transformGroup;
+ }
+
CenterAndScaleElement(image);
+
+ // 设置图片属性,避免被InkCanvas选择系统处理
+ image.IsHitTestVisible = true;
+ image.Focusable = false;
+
+ // 初始化InkCanvas选择设置
+ if (inkCanvas != null)
+ {
+ // 清除当前选择,避免显示控制点
+ inkCanvas.Select(new StrokeCollection());
+ // 设置编辑模式为非选择模式
+ inkCanvas.EditingMode = InkCanvasEditingMode.None;
+ }
+
inkCanvas.Children.Add(image);
+ // 绑定事件处理器
+ if (image is FrameworkElement elementForEvents)
+ {
+ // 鼠标事件
+ elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown;
+ elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp;
+ elementForEvents.MouseMove += Element_MouseMove;
+ elementForEvents.MouseWheel += Element_MouseWheel;
+
+ // 触摸事件
+ elementForEvents.IsManipulationEnabled = true;
+ elementForEvents.ManipulationDelta += Element_ManipulationDelta;
+ elementForEvents.ManipulationCompleted += Element_ManipulationCompleted;
+
+ // 设置光标
+ elementForEvents.Cursor = Cursors.Hand;
+ }
+
timeMachine.CommitElementInsertHistory(image);
}
}
From d7df39290f18cc3da84a0384332b2caf56d45f53 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 22:59:12 +0800
Subject: [PATCH 12/23] =?UTF-8?q?improve:=E6=8F=92=E5=85=A5=E5=9B=BE?=
=?UTF-8?q?=E7=89=87=E5=8F=8A=E5=A2=A8=E8=BF=B9=E5=B9=B3=E6=BB=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Helpers/AdvancedBezierSmoothing.cs | 66 +++++++++++++++----
.../MainWindow_cs/MW_ElementsControls.cs | 25 +++++--
2 files changed, 71 insertions(+), 20 deletions(-)
diff --git a/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs b/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs
index a9015cd6..ab7aa9fd 100644
--- a/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs
+++ b/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs
@@ -97,13 +97,20 @@ namespace Ink_Canvas.Helpers
cancellationToken.ThrowIfCancellationRequested();
- // 确保点数合理
- if (smoothedPoints.Length > originalPoints.Length * 3)
+ // 严格控制点数,避免产生过多点
+ if (smoothedPoints.Length > originalPoints.Length * 2)
{
// 如果点数增加太多,进行重采样
smoothedPoints = ResampleEquidistantOptimized(smoothedPoints, ResampleInterval);
}
+ // 最终检查:确保点数不会过多
+ if (smoothedPoints.Length > originalPoints.Length * 1.5)
+ {
+ // 如果仍然太多点,使用原始笔画
+ return stroke;
+ }
+
// 创建平滑后的笔画
var smoothedStroke = new Stroke(new StylusPointCollection(smoothedPoints))
{
@@ -125,23 +132,23 @@ namespace Ink_Canvas.Helpers
// 添加第一个点
result.Add(points[0]);
- // 使用滑动窗口进行贝塞尔曲线拟合
- for (int i = 0; i <= points.Length - 4; i++)
+ // 使用非重叠的窗口进行贝塞尔曲线拟合
+ for (int i = 0; i < points.Length - 3; i += 3) // 每次移动3个点,避免重叠
{
var p0 = points[i];
- var p1 = points[i + 1];
- var p2 = points[i + 2];
- var p3 = points[i + 3];
+ var p1 = points[Math.Min(i + 1, points.Length - 1)];
+ var p2 = points[Math.Min(i + 2, points.Length - 1)];
+ var p3 = points[Math.Min(i + 3, points.Length - 1)];
// 计算改进的控制点
var controlPoints = CalculateImprovedControlPoints(p0, p1, p2, p3);
- // 自适应插值步数
- int steps = UseAdaptiveInterpolation ?
- CalculateAdaptiveSteps(p0, p1, p2, p3) : InterpolationSteps;
+ // 限制插值步数,避免点数爆炸
+ int steps = Math.Min(UseAdaptiveInterpolation ?
+ CalculateAdaptiveSteps(p0, p1, p2, p3) : InterpolationSteps, 16);
- // 生成贝塞尔曲线点
- for (int j = 1; j <= steps; j++) // 从1开始避免重复第一个点
+ // 生成贝塞尔曲线点,但跳过第一个点避免重复
+ for (int j = 1; j <= steps; j++)
{
double t = (double)j / steps;
var bezierPoint = CubicBezierWithControlPoints(controlPoints, t, p0, p3);
@@ -152,7 +159,8 @@ namespace Ink_Canvas.Helpers
// 添加最后一个点
result.Add(points[points.Length - 1]);
- return result.ToArray();
+ // 去重和优化点数
+ return RemoveDuplicatePoints(result.ToArray());
}
///
@@ -233,6 +241,38 @@ namespace Ink_Canvas.Helpers
return (angle1 + angle2) / Math.PI; // 归一化到0-1
}
+ ///
+ /// 去除重复和过近的点
+ ///
+ private StylusPoint[] RemoveDuplicatePoints(StylusPoint[] points)
+ {
+ if (points.Length < 2) return points;
+
+ var result = new List();
+ result.Add(points[0]);
+
+ double minDistance = ResampleInterval * 0.5; // 最小距离阈值
+
+ for (int i = 1; i < points.Length; i++)
+ {
+ var lastPoint = result[result.Count - 1];
+ var currentPoint = points[i];
+
+ // 计算距离
+ double distance = Math.Sqrt(
+ (currentPoint.X - lastPoint.X) * (currentPoint.X - lastPoint.X) +
+ (currentPoint.Y - lastPoint.Y) * (currentPoint.Y - lastPoint.Y));
+
+ // 如果距离足够大,添加这个点
+ if (distance >= minDistance)
+ {
+ result.Add(currentPoint);
+ }
+ }
+
+ return result.ToArray();
+ }
+
///
/// 使用控制点的三次贝塞尔曲线计算
///
diff --git a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
index 4c2e89c0..ca5a5f8b 100644
--- a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
@@ -40,11 +40,6 @@ namespace Ink_Canvas
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
- // 初始化TransformGroup
- InitializeElementTransform(image);
-
- CenterAndScaleElement(image);
-
// 设置图片属性,避免被InkCanvas选择系统处理
image.IsHitTestVisible = true;
image.Focusable = false;
@@ -52,8 +47,15 @@ namespace Ink_Canvas
// 初始化InkCanvas选择设置
InitializeInkCanvasSelectionSettings();
+ // 先添加到画布
inkCanvas.Children.Add(image);
+ // 初始化TransformGroup
+ InitializeElementTransform(image);
+
+ // 居中缩放
+ CenterAndScaleElement(image);
+
// 绑定事件处理器
BindElementEvents(image);
@@ -99,6 +101,8 @@ namespace Ink_Canvas
{
if (sender is FrameworkElement element)
{
+ LogHelper.WriteLogToFile($"图片鼠标按下: {element.Name}");
+
// 取消之前选中的元素
if (currentSelectedElement != null && currentSelectedElement != element)
{
@@ -151,6 +155,8 @@ namespace Ink_Canvas
{
if (sender is FrameworkElement element)
{
+ LogHelper.WriteLogToFile($"图片滚轮事件: {element.Name}, Delta={e.Delta}");
+
// 使用滚轮缩放的核心机制
ApplyWheelScaleTransform(element, e);
@@ -890,8 +896,13 @@ namespace Ink_Canvas
InkCanvas.SetLeft(element, centerX);
InkCanvas.SetTop(element, centerY);
- // 清除任何现有的RenderTransform
- element.RenderTransform = Transform.Identity;
+ // 保持TransformGroup,不清除RenderTransform
+ // 这样可以保持滚轮缩放和拖动功能
+ if (element.RenderTransform == null || element.RenderTransform == Transform.Identity)
+ {
+ // 只有在没有TransformGroup时才创建
+ InitializeElementTransform(element);
+ }
LogHelper.WriteLogToFile($"元素居中完成: 位置({centerX}, {centerY}), 尺寸({newWidth}x{newHeight})");
}
From c6c0789794db2831cc142ec174ce2b0f011bc933 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 23:10:02 +0800
Subject: [PATCH 13/23] =?UTF-8?q?improve:=E8=87=AA=E5=8A=A8=E6=9B=B4?=
=?UTF-8?q?=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Helpers/AutoUpdateHelper.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ink Canvas/Helpers/AutoUpdateHelper.cs b/Ink Canvas/Helpers/AutoUpdateHelper.cs
index 1cd7b7d1..c403f286 100644
--- a/Ink Canvas/Helpers/AutoUpdateHelper.cs
+++ b/Ink Canvas/Helpers/AutoUpdateHelper.cs
@@ -28,7 +28,7 @@ namespace Ink_Canvas.Helpers
private static string statusFilePath;
// GitHub Token认证
- private static readonly string GitHubToken = "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";
+ private static readonly string GitHubToken = "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";
// 线路组结构体(包含版本、下载、日志地址)
public class UpdateLineGroup
From 5d13c8b543f26a7172543c19759b8f269551e890 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 23:14:00 +0800
Subject: [PATCH 14/23] =?UTF-8?q?improve:=E6=8F=92=E5=85=A5=E5=9B=BE?=
=?UTF-8?q?=E7=89=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_TimeMachine.cs | 28 ++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs b/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs
index 2d9ea05a..c663f058 100644
--- a/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs
+++ b/Ink Canvas/MainWindow_cs/MW_TimeMachine.cs
@@ -190,13 +190,33 @@ namespace Ink_Canvas
{
if (item.InsertedElement is Image img)
{
- // 重新应用CenterAndScaleElement变换
- CenterAndScaleElement(img);
+ // 检查图片是否有位置信息,如果没有则应用居中
+ double left = InkCanvas.GetLeft(img);
+ double top = InkCanvas.GetTop(img);
+
+ if (double.IsNaN(left) || double.IsNaN(top))
+ {
+ // 图片没有位置信息,应用居中
+ CenterAndScaleElement(img);
+ }
+
+ // 重新绑定事件处理器
+ BindElementEvents(img);
}
else if (item.InsertedElement is MediaElement media)
{
- // 重新应用CenterAndScaleElement变换
- CenterAndScaleElement(media);
+ // 检查媒体元素是否有位置信息,如果没有则应用居中
+ double left = InkCanvas.GetLeft(media);
+ double top = InkCanvas.GetTop(media);
+
+ if (double.IsNaN(left) || double.IsNaN(top))
+ {
+ // 媒体元素没有位置信息,应用居中
+ CenterAndScaleElement(media);
+ }
+
+ // 重新绑定事件处理器
+ BindElementEvents(media);
}
}
}
From 3bae64a2c7048cb66f88ef3b4cb9b3ac0580948f Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 23:19:53 +0800
Subject: [PATCH 15/23] =?UTF-8?q?improve:=E6=8F=92=E5=85=A5=E5=9B=BE?=
=?UTF-8?q?=E7=89=87improve:=E6=8F=92=E5=85=A5=E5=9B=BE=E7=89=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../MainWindow_cs/MW_ClipboardHandler.cs | 5 ++-
.../MainWindow_cs/MW_ElementsControls.cs | 33 +++++++++------
.../MainWindow_cs/MW_FloatingBarIcons.cs | 42 +++++++++++++++++++
3 files changed, 66 insertions(+), 14 deletions(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs b/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs
index 0db312ab..fc8eb43e 100644
--- a/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs
@@ -163,8 +163,9 @@ namespace Ink_Canvas
{
// 清除当前选择,避免显示控制点
inkCanvas.Select(new StrokeCollection());
- // 设置编辑模式为非选择模式
- inkCanvas.EditingMode = InkCanvasEditingMode.None;
+ // 设置编辑模式为Ink模式,这样可以保持图片的交互功能
+ // 同时通过图片的IsHitTestVisible和Focusable属性来避免InkCanvas选择系统的干扰
+ inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
}
// 添加到画布
diff --git a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
index ca5a5f8b..c23719af 100644
--- a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
@@ -50,14 +50,23 @@ namespace Ink_Canvas
// 先添加到画布
inkCanvas.Children.Add(image);
- // 初始化TransformGroup
- InitializeElementTransform(image);
+ // 等待图片加载完成后再进行后续处理
+ image.Loaded += (s, args) =>
+ {
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ // 初始化TransformGroup
+ InitializeElementTransform(image);
- // 居中缩放
- CenterAndScaleElement(image);
+ // 居中缩放
+ CenterAndScaleElement(image);
- // 绑定事件处理器
- BindElementEvents(image);
+ // 最后绑定事件处理器
+ BindElementEvents(image);
+
+ LogHelper.WriteLogToFile($"图片插入完成: {image.Name}");
+ }), System.Windows.Threading.DispatcherPriority.Loaded);
+ };
timeMachine.CommitElementInsertHistory(image);
}
@@ -101,7 +110,7 @@ namespace Ink_Canvas
{
if (sender is FrameworkElement element)
{
- LogHelper.WriteLogToFile($"图片鼠标按下: {element.Name}");
+
// 取消之前选中的元素
if (currentSelectedElement != null && currentSelectedElement != element)
@@ -155,7 +164,7 @@ namespace Ink_Canvas
{
if (sender is FrameworkElement element)
{
- LogHelper.WriteLogToFile($"图片滚轮事件: {element.Name}, Delta={e.Delta}");
+
// 使用滚轮缩放的核心机制
ApplyWheelScaleTransform(element, e);
@@ -314,7 +323,7 @@ namespace Ink_Canvas
stroke.Transform(matrix, false);
}
- LogHelper.WriteLogToFile($"滚轮缩放应用: 缩放因子={scaleFactor}, 中心点=({elementCenter.X}, {elementCenter.Y})");
+
}
catch (Exception ex)
{
@@ -351,7 +360,7 @@ namespace Ink_Canvas
CommitTransformHistory(element, initialTransform, transformGroup);
}
- LogHelper.WriteLogToFile($"矩阵变换应用成功: 元素={element.Name}");
+
}
catch (Exception ex)
{
@@ -384,7 +393,7 @@ namespace Ink_Canvas
// 更新选择框的位置(如果有选择框)
UpdateSelectionBorderPosition(delta);
- LogHelper.WriteLogToFile($"鼠标拖动应用: 位移=({delta.X}, {delta.Y})");
+
}
catch (Exception ex)
{
@@ -481,7 +490,7 @@ namespace Ink_Canvas
stroke.Transform(matrix, false);
}
- LogHelper.WriteLogToFile($"触摸操作应用: 平移=({translation.X}, {translation.Y}), 旋转={rotation}, 缩放=({scale.X}, {scale.Y})");
+
}
catch (Exception ex)
{
diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
index f6fe9c6f..3e90076a 100644
--- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
+++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
@@ -2821,9 +2821,51 @@ namespace Ink_Canvas
string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
image.Name = timestamp;
+ // 初始化TransformGroup
+ if (image is FrameworkElement element)
+ {
+ var transformGroup = new TransformGroup();
+ transformGroup.Children.Add(new ScaleTransform(1, 1));
+ transformGroup.Children.Add(new TranslateTransform(0, 0));
+ transformGroup.Children.Add(new RotateTransform(0));
+ element.RenderTransform = transformGroup;
+ }
+
CenterAndScaleElement(image);
+
+ // 设置图片属性,避免被InkCanvas选择系统处理
+ image.IsHitTestVisible = true;
+ image.Focusable = false;
+
+ // 初始化InkCanvas选择设置
+ if (inkCanvas != null)
+ {
+ // 清除当前选择,避免显示控制点
+ inkCanvas.Select(new StrokeCollection());
+ // 同时通过图片的IsHitTestVisible和Focusable属性来避免InkCanvas选择系统的干扰
+ inkCanvas.EditingMode = InkCanvasEditingMode.None;
+ }
+
inkCanvas.Children.Add(image);
+ // 绑定事件处理器
+ if (image is FrameworkElement elementForEvents)
+ {
+ // 鼠标事件
+ elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown;
+ elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp;
+ elementForEvents.MouseMove += Element_MouseMove;
+ elementForEvents.MouseWheel += Element_MouseWheel;
+
+ // 触摸事件
+ elementForEvents.IsManipulationEnabled = true;
+ elementForEvents.ManipulationDelta += Element_ManipulationDelta;
+ elementForEvents.ManipulationCompleted += Element_ManipulationCompleted;
+
+ // 设置光标
+ elementForEvents.Cursor = Cursors.Hand;
+ }
+
timeMachine.CommitElementInsertHistory(image);
}
}
From 463e506ca3bbe33824a04cd782c53fe8ff0ea2d9 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 23:28:28 +0800
Subject: [PATCH 16/23] =?UTF-8?q?improve:=E5=BF=AB=E6=8D=B7=E9=94=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Helpers/GlobalHotkeyManager.cs | 4 ++--
Ink Canvas/MainWindow_cs/MW_Hotkeys.cs | 12 ++++++++++--
Ink Canvas/Windows/HotkeySettingsWindow.xaml | 4 ++--
3 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/Ink Canvas/Helpers/GlobalHotkeyManager.cs b/Ink Canvas/Helpers/GlobalHotkeyManager.cs
index 07d62a59..dd90ddf7 100644
--- a/Ink Canvas/Helpers/GlobalHotkeyManager.cs
+++ b/Ink Canvas/Helpers/GlobalHotkeyManager.cs
@@ -238,7 +238,7 @@ namespace Ink_Canvas.Helpers
RegisterHotkey("DrawTool", Key.D, ModifierKeys.Alt, () => _mainWindow.PenIcon_Click(null, null));
RegisterHotkey("EraserTool", Key.E, ModifierKeys.Alt, () => _mainWindow.EraserIcon_Click(null, null));
RegisterHotkey("BlackboardTool", Key.B, ModifierKeys.Alt, () => _mainWindow.ImageBlackboard_MouseUp(null, null));
- RegisterHotkey("QuitDrawTool", Key.Q, ModifierKeys.Alt, () => _mainWindow.CursorIcon_Click(null, null));
+ RegisterHotkey("QuitDrawTool", Key.Q, ModifierKeys.Alt, () => _mainWindow.KeyChangeToQuitDrawTool(null, null));
// 画笔快捷键 - 使用反射访问penType字段
RegisterHotkey("Pen1", Key.D1, ModifierKeys.Alt, () => SwitchToPenType(0));
@@ -647,7 +647,7 @@ namespace Ink_Canvas.Helpers
case "BlackboardTool":
return () => _mainWindow.ImageBlackboard_MouseUp(null, null);
case "QuitDrawTool":
- return () => _mainWindow.CursorIcon_Click(null, null);
+ return () => _mainWindow.KeyChangeToQuitDrawTool(null, null);
case "Pen1":
return () => SwitchToPenType(0);
case "Pen2":
diff --git a/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs b/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs
index dc19ff5f..e7bed699 100644
--- a/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs
@@ -79,8 +79,16 @@ namespace Ink_Canvas
private void KeyChangeToQuitDrawTool(object sender, ExecutedRoutedEventArgs e)
{
- if (currentMode != 0) ImageBlackboard_MouseUp(lastBorderMouseDownObject, null);
- CursorIcon_Click(lastBorderMouseDownObject, null);
+ if (currentMode != 0)
+ {
+ // 在白板模式下,alt+q 退出白板模式
+ ImageBlackboard_MouseUp(lastBorderMouseDownObject, null);
+ }
+ else
+ {
+ // 在非白板模式下,alt+q 切换到鼠标模式
+ CursorIcon_Click(lastBorderMouseDownObject, null);
+ }
}
private void KeyChangeToSelect(object sender, ExecutedRoutedEventArgs e)
diff --git a/Ink Canvas/Windows/HotkeySettingsWindow.xaml b/Ink Canvas/Windows/HotkeySettingsWindow.xaml
index 68bde7dc..8b008b07 100644
--- a/Ink Canvas/Windows/HotkeySettingsWindow.xaml
+++ b/Ink Canvas/Windows/HotkeySettingsWindow.xaml
@@ -101,8 +101,8 @@
DefaultKey="B"
DefaultModifiers="Alt"/>
From ea1d52292e517fa7efdbd338e6aaf1acbb446040 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sat, 30 Aug 2025 23:53:26 +0800
Subject: [PATCH 17/23] =?UTF-8?q?improve:=E5=BF=AB=E6=8D=B7=E9=94=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_Hotkeys.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs b/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs
index e7bed699..81df3421 100644
--- a/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Hotkeys.cs
@@ -77,7 +77,7 @@ namespace Ink_Canvas
PenIcon_Click(lastBorderMouseDownObject, null);
}
- private void KeyChangeToQuitDrawTool(object sender, ExecutedRoutedEventArgs e)
+ internal void KeyChangeToQuitDrawTool(object sender, ExecutedRoutedEventArgs e)
{
if (currentMode != 0)
{
From de20b506f00d72e0b1dae7ffbdaac8b144dd3a4c Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sun, 31 Aug 2025 00:25:22 +0800
Subject: [PATCH 18/23] =?UTF-8?q?improve:=E7=94=A8=E6=88=B7=E4=BD=93?=
=?UTF-8?q?=E9=AA=8C=E5=88=86=E7=BA=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Helpers/DeviceIdentifier.cs | 2020 ++++--------------------
1 file changed, 265 insertions(+), 1755 deletions(-)
diff --git a/Ink Canvas/Helpers/DeviceIdentifier.cs b/Ink Canvas/Helpers/DeviceIdentifier.cs
index 79140029..82bc3897 100644
--- a/Ink Canvas/Helpers/DeviceIdentifier.cs
+++ b/Ink Canvas/Helpers/DeviceIdentifier.cs
@@ -1,4 +1,3 @@
-using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -16,26 +15,10 @@ namespace Ink_Canvas.Helpers
///
internal static class DeviceIdentifier
{
- // 多重备份路径策略
+ // 文件路径策略
private static readonly string DeviceIdFilePath = Path.Combine(App.RootPath, "device_id.dat");
- private static readonly string UsageStatsFilePath = Path.Combine(App.RootPath, "usage_stats.json");
-
- // 使用频率数据的多重隐藏备份路径
- private static readonly string BackupDeviceIdPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
- "ICC", ".sys", "device.dat");
- private static readonly string BackupUsageStatsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
- "ICC", ".sys", "usage.dat");
-
- // 使用频率数据的额外隐藏备份位置
- private static readonly string SecondaryUsageBackupPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
- "Microsoft", "Windows", ".icc", "usage_backup.tmp");
- private static readonly string TertiaryUsageBackupPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
- "ICC", ".cache", "usage_cache.dat");
- private static readonly string QuaternaryUsageBackupPath = Path.Combine(Path.GetTempPath(),
- ".icc_temp", "usage_temp.dat");
-
- // 数据完整性验证密钥
- private static readonly string DataIntegrityKey = "ICC_DEVICE_INTEGRITY_2024";
+ private static readonly string UsageStatsFilePath = Path.Combine(App.RootPath, "usage_stats.enc");
+ private static readonly string UsageStatsBackupPath = Path.Combine(App.RootPath, "saves", "usage_stats_backup.enc");
private static readonly string DeviceId;
private static readonly object fileLock = new object();
@@ -44,16 +27,6 @@ namespace Ink_Canvas.Helpers
{
// 在静态构造函数中初始化设备ID
DeviceId = GetOrCreateDeviceId();
-
- // 执行数据完整性检查和自动修复
- try
- {
- PerformDataIntegrityCheck();
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 初始化时数据完整性检查失败: {ex.Message}", LogHelper.LogType.Error);
- }
}
///
@@ -66,7 +39,7 @@ namespace Ink_Canvas.Helpers
}
///
- /// 获取或创建设备ID(内部方法)- 支持多重备份恢复
+ /// 获取或创建设备ID
///
private static string GetOrCreateDeviceId()
{
@@ -79,35 +52,15 @@ namespace Ink_Canvas.Helpers
if (!string.IsNullOrEmpty(deviceId))
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 从主文件读取设备ID: {deviceId}");
- // 确保备份同步
- SaveDeviceIdToAllLocations(deviceId);
return deviceId;
}
- // 2. 尝试从备份文件恢复
- deviceId = LoadDeviceIdFromFile(BackupDeviceIdPath);
- if (!string.IsNullOrEmpty(deviceId))
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 从备份文件恢复设备ID: {deviceId}");
- SaveDeviceIdToAllLocations(deviceId);
- return deviceId;
- }
-
- // 3. 尝试从注册表恢复
- deviceId = LoadDeviceIdFromRegistry();
- if (!string.IsNullOrEmpty(deviceId))
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表恢复设备ID: {deviceId}");
- SaveDeviceIdToAllLocations(deviceId);
- return deviceId;
- }
-
- // 4. 生成新的设备ID
+ // 2. 生成新的设备ID
string newDeviceId = GenerateDeviceId();
LogHelper.WriteLogToFile($"DeviceIdentifier | 生成新设备ID: {newDeviceId}");
- // 5. 保存到所有位置
- SaveDeviceIdToAllLocations(newDeviceId);
+ // 3. 保存到主文件
+ SaveDeviceIdToFile(DeviceIdFilePath, newDeviceId);
return newDeviceId;
}
@@ -350,47 +303,6 @@ namespace Ink_Canvas.Helpers
return null;
}
- ///
- /// 从注册表加载设备ID
- ///
- private static string LoadDeviceIdFromRegistry()
- {
- try
- {
- using (var key = Registry.CurrentUser.OpenSubKey(@"Software\ICC\DeviceInfo"))
- {
- if (key != null)
- {
- var value = key.GetValue("DeviceId") as string;
- if (IsValidDeviceId(value))
- {
- return value;
- }
- }
- }
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表加载设备ID失败: {ex.Message}", LogHelper.LogType.Error);
- }
- return null;
- }
-
- ///
- /// 保存设备ID到所有位置
- ///
- private static void SaveDeviceIdToAllLocations(string deviceId)
- {
- // 保存到主文件
- SaveDeviceIdToFile(DeviceIdFilePath, deviceId);
-
- // 保存到备份文件
- SaveDeviceIdToFile(BackupDeviceIdPath, deviceId);
-
- // 保存到注册表
- SaveDeviceIdToRegistry(deviceId);
- }
-
///
/// 保存设备ID到文件
///
@@ -403,50 +315,19 @@ namespace Ink_Canvas.Helpers
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
- // 设置隐藏属性
- if (filePath.Contains(".sys"))
- {
- var dirInfo = new DirectoryInfo(directory);
- dirInfo.Attributes |= FileAttributes.Hidden | FileAttributes.System;
- }
}
File.WriteAllText(filePath, deviceId);
- // 设置文件属性为隐藏和系统文件
- if (filePath.Contains(".sys"))
- {
- var fileInfo = new FileInfo(filePath);
- fileInfo.Attributes |= FileAttributes.Hidden | FileAttributes.System;
- }
-
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 设备ID已保存到: {filePath}");
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 设备ID已保存到: {filePath}");
}
catch (Exception ex)
{
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 保存设备ID到文件失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error);
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 保存设备ID到文件失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error);
}
}
- ///
- /// 保存设备ID到注册表
- ///
- private static void SaveDeviceIdToRegistry(string deviceId)
- {
- try
- {
- using (var key = Registry.CurrentUser.CreateSubKey(@"Software\ICC\DeviceInfo"))
- {
- key?.SetValue("DeviceId", deviceId);
- key?.SetValue("LastUpdate", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
- }
- // LogHelper.WriteLogToFile("DeviceIdentifier | 设备ID已保存到注册表");
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 保存设备ID到注册表失败: {ex.Message}", LogHelper.LogType.Error);
- }
- }
+
///
/// 使用频率统计数据结构(优化至秒级精度)
@@ -487,9 +368,6 @@ namespace Ink_Canvas.Helpers
[JsonProperty("usageFrequency")]
public UsageFrequency UsageFrequency { get; set; }
- [JsonProperty("dataHash")]
- public string DataHash { get; set; }
-
[JsonProperty("lastModified")]
public DateTime LastModified { get; set; }
@@ -613,57 +491,6 @@ namespace Ink_Canvas.Helpers
// 同时更新旧字段以保持兼容性
WeeklyUsageMinutes = WeeklyUsageSeconds / 60;
}
-
-
-
- ///
- /// 计算数据哈希值用于完整性验证(秒级精度)
- ///
- public void UpdateDataHash()
- {
- // 使用秒级精度数据计算哈希
- var dataString = $"{DeviceId}|{LaunchCount}|{TotalUsageSeconds}|{LastLaunchTime:yyyyMMddHHmmss}|{WeeklyLaunchCount}|{WeeklyUsageSeconds}|{DataIntegrityKey}";
- using (var sha256 = SHA256.Create())
- {
- var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(dataString));
- DataHash = Convert.ToBase64String(hashBytes);
- }
- LastModified = DateTime.Now;
- }
-
- ///
- /// 验证数据完整性(秒级精度)
- ///
- public bool VerifyDataIntegrity()
- {
- try
- {
- // 首先尝试使用秒级精度验证
- var dataString = $"{DeviceId}|{LaunchCount}|{TotalUsageSeconds}|{LastLaunchTime:yyyyMMddHHmmss}|{WeeklyLaunchCount}|{WeeklyUsageSeconds}|{DataIntegrityKey}";
- using (var sha256 = SHA256.Create())
- {
- var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(dataString));
- var expectedHash = Convert.ToBase64String(hashBytes);
- if (DataHash == expectedHash)
- {
- return true;
- }
- }
-
- // 如果秒级精度验证失败,尝试使用旧的分钟精度验证(向后兼容)
- var oldDataString = $"{DeviceId}|{LaunchCount}|{TotalUsageMinutes}|{LastLaunchTime:yyyyMMddHHmmss}|{WeeklyLaunchCount}|{WeeklyUsageMinutes}|{DataIntegrityKey}";
- using (var sha256 = SHA256.Create())
- {
- var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(oldDataString));
- var expectedHash = Convert.ToBase64String(hashBytes);
- return DataHash == expectedHash;
- }
- }
- catch
- {
- return false;
- }
- }
}
///
@@ -738,9 +565,6 @@ namespace Ink_Canvas.Helpers
// 计算使用频率
CalculateUsageFrequency(stats);
- // 更新数据完整性哈希
- stats.UpdateDataHash();
-
SaveUsageStats(stats);
LogHelper.WriteLogToFile($"DeviceIdentifier | 记录应用启动 - 设备ID: {DeviceId}, 总启动: {stats.LaunchCount}次, 本周启动: {stats.WeeklyLaunchCount}次");
@@ -794,9 +618,6 @@ namespace Ink_Canvas.Helpers
// 重新计算使用频率
CalculateUsageFrequency(stats);
- // 更新数据完整性哈希
- stats.UpdateDataHash();
-
SaveUsageStats(stats);
LogHelper.WriteLogToFile($"DeviceIdentifier | 记录应用退出 - 本次会话: {FormatDuration(sessionSeconds)}, " +
@@ -992,53 +813,42 @@ namespace Ink_Canvas.Helpers
}
///
- /// 加载使用统计 - 支持多重备份恢复和智能反篡改
+ /// 加载使用统计
///
private static UsageStats LoadUsageStats()
{
try
{
- // 智能恢复:收集所有可用的数据源,选择最可信的
- var allDataSources = CollectAllUsageDataSources();
-
- // 如果找到有效数据,返回最可信的
- if (allDataSources.Count > 0)
+ // 1. 尝试从主文件加载
+ var stats = LoadUsageStatsFromFile(UsageStatsFilePath);
+ if (stats != null)
{
- var bestData = SelectMostTrustedData(allDataSources);
- if (bestData != null)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 使用最可信数据源恢复使用统计: {bestData.Source}");
-
- // 执行数据迁移(如果需要)
- bestData.Stats.MigrateToSecondsPrecision();
-
- // 确保备份同步
- SaveUsageStatsToAllLocations(bestData.Stats);
- return bestData.Stats;
- }
- }
-
- // LogHelper.WriteLogToFile("DeviceIdentifier | 所有数据源都不可用,检查是否有部分可恢复数据", LogHelper.LogType.Warning);
-
- // 如果没有完全可信的数据,尝试从部分损坏的数据中恢复
- var partiallyRecoveredData = AttemptPartialDataRecovery(allDataSources);
- if (partiallyRecoveredData != null)
- {
- // LogHelper.WriteLogToFile("DeviceIdentifier | 从部分损坏数据中恢复使用统计");
-
// 执行数据迁移(如果需要)
- partiallyRecoveredData.MigrateToSecondsPrecision();
-
- SaveUsageStatsToAllLocations(partiallyRecoveredData);
- return partiallyRecoveredData;
+ stats.MigrateToSecondsPrecision();
+ return stats;
}
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 加载使用统计失败: {ex.Message}", LogHelper.LogType.Error);
- }
- // 返回新的统计对象(秒级精度)
+ // 2. 尝试从备份文件加载
+ stats = LoadUsageStatsFromFile(UsageStatsBackupPath);
+ if (stats != null)
+ {
+ LogHelper.WriteLogToFile("DeviceIdentifier | 从备份文件恢复使用统计");
+ stats.MigrateToSecondsPrecision();
+ // 尝试恢复主文件
+ try
+ {
+ SaveUsageStatsToFile(UsageStatsFilePath, stats);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 恢复主文件失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
+ return stats;
+ }
+
+
+
+ // 如果所有文件都不存在或损坏,返回新的统计对象
var newStats = new UsageStats
{
DeviceId = DeviceId,
@@ -1053,36 +863,47 @@ namespace Ink_Canvas.Helpers
UsageFrequency = UsageFrequency.Medium
};
- // 更新数据完整性哈希
- newStats.UpdateDataHash();
-
- // 保存新统计到所有位置
- SaveUsageStatsToAllLocations(newStats);
+ // 保存新统计到文件
+ SaveUsageStatsToFile(UsageStatsFilePath, newStats);
return newStats;
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 加载使用统计失败: {ex.Message}", LogHelper.LogType.Error);
+
+ // 返回默认统计对象
+ return new UsageStats
+ {
+ DeviceId = DeviceId,
+ LastLaunchTime = DateTime.Now,
+ LaunchCount = 0,
+ TotalUsageSeconds = 0,
+ AverageSessionSeconds = 0,
+ TotalUsageMinutes = 0,
+ AverageSessionMinutes = 0,
+ LastUpdateCheck = DateTime.MinValue,
+ UpdatePriority = UpdatePriority.Medium,
+ UsageFrequency = UsageFrequency.Medium
+ };
+ }
}
///
- /// 保存使用统计 - 多重备份
+ /// 保存使用统计
///
private static void SaveUsageStats(UsageStats stats)
{
- SaveUsageStatsToAllLocations(stats);
+ // 保存到主文件
+ SaveUsageStatsToFile(UsageStatsFilePath, stats);
+
+ // 保存到备份文件
+ SaveUsageStatsToFile(UsageStatsBackupPath, stats);
}
- ///
- /// 数据源信息结构
- ///
- private class DataSourceInfo
- {
- public UsageStats Stats { get; set; }
- public string Source { get; set; }
- public bool IsIntegrityValid { get; set; }
- public DateTime LastModified { get; set; }
- public int TrustScore { get; set; }
- }
+
///
- /// 从文件加载使用统计(带完整性验证,但不丢弃篡改数据)
+ /// 从文件加载使用统计(解密)
///
private static UsageStats LoadUsageStatsFromFile(string filePath)
{
@@ -1090,28 +911,28 @@ namespace Ink_Canvas.Helpers
{
if (File.Exists(filePath))
{
- string json = File.ReadAllText(filePath);
- var stats = JsonConvert.DeserializeObject(json);
- if (stats != null && !string.IsNullOrEmpty(stats.DeviceId))
+ // 尝试解密文件
+ var stats = LoadUsageStatsFromEncryptedFile(filePath);
+ if (stats != null)
{
- // 验证数据完整性
- if (!string.IsNullOrEmpty(stats.DataHash))
- {
- if (stats.VerifyDataIntegrity())
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性验证通过: {filePath}");
- return stats;
- }
-
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性验证失败,可能被篡改: {filePath}", LogHelper.LogType.Warning);
- return null; // 数据被篡改,不使用
- }
-
- // 旧版本数据,没有哈希值,更新哈希后返回
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 检测到旧版本数据,正在更新完整性哈希: {filePath}");
- stats.UpdateDataHash();
return stats;
}
+
+ // 如果解密失败,尝试作为普通JSON文件读取(向后兼容)
+ try
+ {
+ string json = File.ReadAllText(filePath);
+ var plainStats = JsonConvert.DeserializeObject(json);
+ if (plainStats != null && !string.IsNullOrEmpty(plainStats.DeviceId))
+ {
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 从普通JSON文件加载使用统计: {filePath}");
+ return plainStats;
+ }
+ }
+ catch
+ {
+ // 忽略JSON解析错误
+ }
}
}
catch (Exception ex)
@@ -1122,557 +943,67 @@ namespace Ink_Canvas.Helpers
}
///
- /// 从文件加载使用统计(包括被篡改的数据,用于恢复分析)
+ /// 从加密文件加载使用统计
///
- private static DataSourceInfo LoadUsageStatsFromFileWithInfo(string filePath, string sourceName)
+ private static UsageStats LoadUsageStatsFromEncryptedFile(string filePath)
{
try
{
if (File.Exists(filePath))
{
- string json = File.ReadAllText(filePath);
- var stats = JsonConvert.DeserializeObject(json);
- if (stats != null && !string.IsNullOrEmpty(stats.DeviceId))
+ byte[] encryptedData = File.ReadAllBytes(filePath);
+
+ if (encryptedData.Length < 32) // SHA256校验和长度为32字节
{
- var fileInfo = new FileInfo(filePath);
- var dataSource = new DataSourceInfo
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 加密文件格式错误: {filePath}", LogHelper.LogType.Error);
+ return null;
+ }
+
+ // 提取校验和和加密数据
+ byte[] checksum = new byte[32];
+ byte[] data = new byte[encryptedData.Length - 32];
+ Array.Copy(encryptedData, 0, checksum, 0, 32);
+ Array.Copy(encryptedData, 32, data, 0, data.Length);
+
+ // 使用SHA256生成解密密钥
+ using (var sha256 = SHA256.Create())
+ {
+ byte[] keyBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(DeviceId + "ICC_Usage_Stats_Salt"));
+
+ // XOR解密
+ byte[] decryptedData = new byte[data.Length];
+ for (int i = 0; i < data.Length; i++)
{
- Stats = stats,
- Source = sourceName,
- LastModified = stats.LastModified != DateTime.MinValue ? stats.LastModified : fileInfo.LastWriteTime,
- IsIntegrityValid = false,
- TrustScore = 0
- };
-
- // 验证数据完整性
- if (!string.IsNullOrEmpty(stats.DataHash))
- {
- dataSource.IsIntegrityValid = stats.VerifyDataIntegrity();
- dataSource.TrustScore = dataSource.IsIntegrityValid ? 100 : 20; // 完整数据100分,篡改数据20分
+ decryptedData[i] = (byte)(data[i] ^ keyBytes[i % keyBytes.Length]);
}
- else
+
+ // 验证校验和
+ byte[] computedChecksum = sha256.ComputeHash(decryptedData);
+ if (!checksum.SequenceEqual(computedChecksum))
{
- // 旧版本数据,中等信任度
- dataSource.IsIntegrityValid = true;
- dataSource.TrustScore = 60;
- stats.UpdateDataHash();
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 加密文件校验和验证失败: {filePath}", LogHelper.LogType.Error);
+ return null;
+ }
+
+ string json = Encoding.UTF8.GetString(decryptedData);
+ var stats = JsonConvert.DeserializeObject(json);
+ if (stats != null && !string.IsNullOrEmpty(stats.DeviceId))
+ {
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 从加密文件成功加载使用统计: {filePath}");
+ return stats;
}
-
- return dataSource;
}
}
}
catch (Exception ex)
{
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 从文件加载数据源信息失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error);
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 从加密文件加载使用统计失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error);
}
return null;
}
- ///
- /// 收集所有可用的使用统计数据源
- ///
- private static List CollectAllUsageDataSources()
- {
- var dataSources = new List();
-
- try
- {
- // 1. 收集文件数据源
- var fileSources = new[]
- {
- new { Path = UsageStatsFilePath, Name = "主文件" },
- new { Path = BackupUsageStatsPath, Name = "第一备份" },
- new { Path = SecondaryUsageBackupPath, Name = "第二备份" },
- new { Path = TertiaryUsageBackupPath, Name = "第三备份" },
- new { Path = QuaternaryUsageBackupPath, Name = "第四备份" }
- };
-
- foreach (var source in fileSources)
- {
- var dataSource = LoadUsageStatsFromFileWithInfo(source.Path, source.Name);
- if (dataSource != null)
- {
- dataSources.Add(dataSource);
- }
- }
-
- // 2. 收集注册表数据源
- var registrySource = LoadUsageStatsFromRegistryWithInfo(@"Software\ICC\DeviceInfo", "主注册表");
- if (registrySource != null)
- {
- dataSources.Add(registrySource);
- }
-
- // 3. 收集备用注册表数据源
- var backupRegistryPaths = new[]
- {
- new { Path = @"Software\Microsoft\Windows\CurrentVersion\ICC", Name = "备用注册表1" },
- new { Path = @"Software\Classes\.icc\UsageData", Name = "备用注册表2" },
- new { Path = @"Software\ICC\Config\Usage", Name = "备用注册表3" }
- };
-
- foreach (var regPath in backupRegistryPaths)
- {
- var regSource = LoadUsageStatsFromBackupRegistryWithInfo(regPath.Path, regPath.Name);
- if (regSource != null)
- {
- dataSources.Add(regSource);
- }
- }
-
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 收集到 {dataSources.Count} 个数据源");
- return dataSources;
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 收集数据源失败: {ex.Message}", LogHelper.LogType.Error);
- return dataSources;
- }
- }
-
- ///
- /// 选择最可信的数据源
- ///
- private static DataSourceInfo SelectMostTrustedData(List dataSources)
- {
- try
- {
- // 首先尝试找到完整性验证通过的数据
- var validSources = dataSources.Where(d => d.IsIntegrityValid).ToList();
-
- if (validSources.Count > 0)
- {
- // 在有效数据中选择最新的
- var bestValid = validSources.OrderByDescending(d => d.LastModified).First();
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 选择完整性验证通过的最新数据: {bestValid.Source}");
- return bestValid;
- }
-
- // 如果没有完整性验证通过的数据,选择信任度最高的
- var bestByTrust = dataSources.OrderByDescending(d => d.TrustScore).ThenByDescending(d => d.LastModified).FirstOrDefault();
- if (bestByTrust != null)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 选择信任度最高的数据: {bestByTrust.Source} (信任度: {bestByTrust.TrustScore})", LogHelper.LogType.Warning);
- return bestByTrust;
- }
-
- return null;
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 选择最可信数据失败: {ex.Message}", LogHelper.LogType.Error);
- return null;
- }
- }
-
- ///
- /// 尝试从部分损坏的数据中恢复
- ///
- private static UsageStats AttemptPartialDataRecovery(List dataSources)
- {
- try
- {
- if (dataSources.Count == 0)
- {
- // LogHelper.WriteLogToFile("DeviceIdentifier | 没有可用数据源进行部分恢复");
- return null;
- }
-
- // 从所有数据源中提取可信的字段
- var recoveredStats = new UsageStats
- {
- DeviceId = DeviceId,
- LastLaunchTime = DateTime.Now,
- LaunchCount = 0,
- TotalUsageMinutes = 0,
- AverageSessionMinutes = 0,
- LastUpdateCheck = DateTime.MinValue,
- UpdatePriority = UpdatePriority.Medium,
- UsageFrequency = UsageFrequency.Medium
- };
-
- // 使用多数投票或最大值策略恢复关键数据(秒级精度)
- var launchCounts = dataSources.Where(d => d.Stats.LaunchCount > 0).Select(d => d.Stats.LaunchCount).ToList();
- var usageSeconds = dataSources.Where(d => d.Stats.TotalUsageSeconds > 0).Select(d => d.Stats.TotalUsageSeconds).ToList();
- var usageMinutes = dataSources.Where(d => d.Stats.TotalUsageMinutes > 0).Select(d => d.Stats.TotalUsageMinutes).ToList();
-
- if (launchCounts.Count > 0)
- {
- recoveredStats.LaunchCount = (int)launchCounts.Average(); // 使用平均值
- }
-
- // 优先使用秒级数据,如果没有则使用分钟数据转换
- if (usageSeconds.Count > 0)
- {
- recoveredStats.TotalUsageSeconds = (long)usageSeconds.Average(); // 使用平均值
- recoveredStats.TotalUsageMinutes = recoveredStats.TotalUsageSeconds / 60; // 兼容性
- }
- else if (usageMinutes.Count > 0)
- {
- recoveredStats.TotalUsageMinutes = (long)usageMinutes.Average(); // 使用平均值
- recoveredStats.TotalUsageSeconds = recoveredStats.TotalUsageMinutes * 60; // 转换为秒
- }
-
- // 重新计算平均会话时长(秒级精度)
- if (recoveredStats.LaunchCount > 0)
- {
- recoveredStats.AverageSessionSeconds = (double)recoveredStats.TotalUsageSeconds / recoveredStats.LaunchCount;
- recoveredStats.AverageSessionMinutes = recoveredStats.AverageSessionSeconds / 60; // 兼容性
- }
-
- // 重新计算使用频率
- CalculateUsageFrequency(recoveredStats);
-
- // 更新数据完整性哈希
- recoveredStats.UpdateDataHash();
-
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 部分数据恢复完成 - 启动次数: {recoveredStats.LaunchCount}, 使用时长: {FormatDuration(recoveredStats.TotalUsageSeconds)}");
- return recoveredStats;
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 部分数据恢复失败: {ex.Message}", LogHelper.LogType.Error);
- return null;
- }
- }
-
- ///
- /// 从注册表加载使用统计(带数据源信息)
- ///
- private static DataSourceInfo LoadUsageStatsFromRegistryWithInfo(string registryPath, string sourceName)
- {
- try
- {
- using (var key = Registry.CurrentUser.OpenSubKey(registryPath))
- {
- if (key != null)
- {
- var deviceId = key.GetValue("DeviceId") as string;
- var launchCount = key.GetValue("LaunchCount");
-
- // 秒级精度数据
- var totalSeconds = key.GetValue("TotalUsageSeconds");
- var avgSessionSeconds = key.GetValue("AverageSessionSeconds");
-
- // 兼容性:分钟精度数据
- var totalMinutes = key.GetValue("TotalUsageMinutes");
- var avgSessionMinutes = key.GetValue("AverageSessionMinutes");
-
- var lastLaunch = key.GetValue("LastLaunchTime") as string;
- var priority = key.GetValue("UpdatePriority");
- var frequency = key.GetValue("UsageFrequency");
- var dataHash = key.GetValue("DataHash") as string;
- var lastUpdate = key.GetValue("LastUpdate") as string;
-
- // 每周统计数据(秒级精度)
- var weeklyLaunchCount = key.GetValue("WeeklyLaunchCount");
- var weeklyUsageSeconds = key.GetValue("WeeklyUsageSeconds");
- var lastWeekUsageSeconds = key.GetValue("LastWeekUsageSeconds");
-
- // 兼容性:分钟精度数据
- var weeklyUsageMinutes = key.GetValue("WeeklyUsageMinutes");
- var lastWeekUsageMinutes = key.GetValue("LastWeekUsageMinutes");
-
- var weekStartDate = key.GetValue("WeekStartDate") as string;
- var lastWeekLaunchCount = key.GetValue("LastWeekLaunchCount");
-
- if (!string.IsNullOrEmpty(deviceId) && launchCount != null)
- {
- var stats = new UsageStats
- {
- DeviceId = deviceId,
- LaunchCount = Convert.ToInt32(launchCount),
-
- // 秒级精度数据
- TotalUsageSeconds = totalSeconds != null ? Convert.ToInt64(totalSeconds) : 0,
- AverageSessionSeconds = avgSessionSeconds != null ? Convert.ToDouble(avgSessionSeconds) : 0,
-
- // 兼容性:分钟精度数据
- TotalUsageMinutes = totalMinutes != null ? Convert.ToInt64(totalMinutes) : 0,
- AverageSessionMinutes = avgSessionMinutes != null ? Convert.ToDouble(avgSessionMinutes) : 0,
-
- LastLaunchTime = DateTime.TryParse(lastLaunch, out var dt) ? dt : DateTime.Now,
- UpdatePriority = priority != null ? (UpdatePriority)Convert.ToInt32(priority) : UpdatePriority.Medium,
- UsageFrequency = frequency != null ? (UsageFrequency)Convert.ToInt32(frequency) : UsageFrequency.Medium,
- DataHash = dataHash,
- LastUpdateCheck = DateTime.MinValue,
-
- // 每周统计数据(秒级精度)
- WeeklyLaunchCount = weeklyLaunchCount != null ? Convert.ToInt32(weeklyLaunchCount) : 0,
- WeeklyUsageSeconds = weeklyUsageSeconds != null ? Convert.ToInt64(weeklyUsageSeconds) : 0,
- LastWeekUsageSeconds = lastWeekUsageSeconds != null ? Convert.ToInt64(lastWeekUsageSeconds) : 0,
-
- // 兼容性:分钟精度数据
- WeeklyUsageMinutes = weeklyUsageMinutes != null ? Convert.ToInt64(weeklyUsageMinutes) : 0,
- LastWeekUsageMinutes = lastWeekUsageMinutes != null ? Convert.ToInt64(lastWeekUsageMinutes) : 0,
-
- WeekStartDate = DateTime.TryParse(weekStartDate, out var wsd) ? wsd : DateTime.MinValue,
- LastWeekLaunchCount = lastWeekLaunchCount != null ? Convert.ToInt32(lastWeekLaunchCount) : 0
- };
-
- // 执行数据迁移(如果需要)
- stats.MigrateToSecondsPrecision();
-
- // 重新计算平均会话时长
- if (stats.LaunchCount > 0 && stats.AverageSessionSeconds == 0)
- {
- stats.AverageSessionSeconds = (double)stats.TotalUsageSeconds / stats.LaunchCount;
- stats.AverageSessionMinutes = stats.AverageSessionSeconds / 60;
- }
-
- var dataSource = new DataSourceInfo
- {
- Stats = stats,
- Source = sourceName,
- LastModified = DateTime.TryParse(lastUpdate, out var updateTime) ? updateTime : DateTime.Now,
- IsIntegrityValid = false,
- TrustScore = 80 // 注册表数据信任度较高
- };
-
- // 验证数据完整性
- if (!string.IsNullOrEmpty(stats.DataHash))
- {
- dataSource.IsIntegrityValid = stats.VerifyDataIntegrity();
- dataSource.TrustScore = dataSource.IsIntegrityValid ? 100 : 30;
- }
-
- return dataSource;
- }
- }
- }
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表加载数据源信息失败 ({registryPath}): {ex.Message}", LogHelper.LogType.Error);
- }
- return null;
- }
-
- ///
- /// 从备用注册表位置加载使用统计(带数据源信息)
- ///
- private static DataSourceInfo LoadUsageStatsFromBackupRegistryWithInfo(string registryPath, string sourceName)
- {
- try
- {
- using (var key = Registry.CurrentUser.OpenSubKey(registryPath))
- {
- if (key != null)
- {
- var launchCount = key.GetValue("LC");
-
- // 秒级精度数据
- var totalSeconds = key.GetValue("TUS");
- var avgSessionSeconds = key.GetValue("ASS");
-
- // 兼容性:分钟精度数据
- var totalMinutes = key.GetValue("TUM");
- var avgSessionMinutes = key.GetValue("ASM");
-
- var lastLaunchBinary = key.GetValue("LLT");
- var priority = key.GetValue("UP");
- var frequency = key.GetValue("UF");
- var dataHash = key.GetValue("DH") as string;
- var lastUpdateBinary = key.GetValue("LU");
-
- // 每周统计数据(秒级精度)
- var weeklyLaunchCount = key.GetValue("WLC");
- var weeklyUsageSeconds = key.GetValue("WUS");
- var lastWeekUsageSeconds = key.GetValue("LWUS");
-
- // 兼容性:分钟精度数据
- var weeklyUsageMinutes = key.GetValue("WUM");
- var lastWeekUsageMinutes = key.GetValue("LWUM");
-
- var weekStartDateBinary = key.GetValue("WSD");
- var lastWeekLaunchCount = key.GetValue("LWLC");
-
- if (launchCount != null && (totalSeconds != null || totalMinutes != null))
- {
- var stats = new UsageStats
- {
- DeviceId = DeviceId,
- LaunchCount = Convert.ToInt32(launchCount),
-
- // 秒级精度数据
- TotalUsageSeconds = totalSeconds != null ? Convert.ToInt64(totalSeconds) : 0,
- AverageSessionSeconds = avgSessionSeconds != null ? Convert.ToDouble(avgSessionSeconds) : 0,
-
- // 兼容性:分钟精度数据
- TotalUsageMinutes = totalMinutes != null ? Convert.ToInt64(totalMinutes) : 0,
- AverageSessionMinutes = avgSessionMinutes != null ? Convert.ToDouble(avgSessionMinutes) : 0,
-
- LastLaunchTime = lastLaunchBinary != null ? DateTime.FromBinary(Convert.ToInt64(lastLaunchBinary)) : DateTime.Now,
- UpdatePriority = priority != null ? (UpdatePriority)Convert.ToInt32(priority) : UpdatePriority.Medium,
- UsageFrequency = frequency != null ? (UsageFrequency)Convert.ToInt32(frequency) : UsageFrequency.Medium,
- DataHash = dataHash,
- LastUpdateCheck = DateTime.MinValue,
-
- // 每周统计数据(秒级精度)
- WeeklyLaunchCount = weeklyLaunchCount != null ? Convert.ToInt32(weeklyLaunchCount) : 0,
- WeeklyUsageSeconds = weeklyUsageSeconds != null ? Convert.ToInt64(weeklyUsageSeconds) : 0,
- LastWeekUsageSeconds = lastWeekUsageSeconds != null ? Convert.ToInt64(lastWeekUsageSeconds) : 0,
-
- // 兼容性:分钟精度数据
- WeeklyUsageMinutes = weeklyUsageMinutes != null ? Convert.ToInt64(weeklyUsageMinutes) : 0,
- LastWeekUsageMinutes = lastWeekUsageMinutes != null ? Convert.ToInt64(lastWeekUsageMinutes) : 0,
-
- WeekStartDate = weekStartDateBinary != null ? DateTime.FromBinary(Convert.ToInt64(weekStartDateBinary)) : DateTime.MinValue,
- LastWeekLaunchCount = lastWeekLaunchCount != null ? Convert.ToInt32(lastWeekLaunchCount) : 0
- };
-
- // 执行数据迁移(如果需要)
- stats.MigrateToSecondsPrecision();
-
- // 重新计算平均会话时长
- if (stats.LaunchCount > 0 && stats.AverageSessionSeconds == 0)
- {
- stats.AverageSessionSeconds = (double)stats.TotalUsageSeconds / stats.LaunchCount;
- stats.AverageSessionMinutes = stats.AverageSessionSeconds / 60;
- }
-
- var dataSource = new DataSourceInfo
- {
- Stats = stats,
- Source = sourceName,
- LastModified = lastUpdateBinary != null ? DateTime.FromBinary(Convert.ToInt64(lastUpdateBinary)) : DateTime.Now,
- IsIntegrityValid = false,
- TrustScore = 75 // 备用注册表数据信任度中等
- };
-
- // 验证数据完整性
- if (!string.IsNullOrEmpty(stats.DataHash))
- {
- dataSource.IsIntegrityValid = stats.VerifyDataIntegrity();
- dataSource.TrustScore = dataSource.IsIntegrityValid ? 100 : 25;
- }
-
- return dataSource;
- }
- }
- }
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 从备用注册表加载数据源信息失败 ({registryPath}): {ex.Message}", LogHelper.LogType.Error);
- }
- return null;
- }
-
- ///
- /// 从注册表加载使用统计(保持向后兼容)
- ///
- private static UsageStats LoadUsageStatsFromRegistry()
- {
- var dataSource = LoadUsageStatsFromRegistryWithInfo(@"Software\ICC\DeviceInfo", "主注册表");
- return dataSource?.Stats;
- }
-
- ///
- /// 从多个注册表位置加载使用统计(强化恢复)
- ///
- private static UsageStats LoadUsageStatsFromMultipleRegistryLocations()
- {
- var registryPaths = new[]
- {
- @"Software\Microsoft\Windows\CurrentVersion\ICC",
- @"Software\Classes\.icc\UsageData",
- @"Software\ICC\Config\Usage"
- };
-
- foreach (var path in registryPaths)
- {
- try
- {
- using (var key = Registry.CurrentUser.OpenSubKey(path))
- {
- if (key != null)
- {
- var launchCount = key.GetValue("LC");
- var totalMinutes = key.GetValue("TUM");
- var lastLaunchBinary = key.GetValue("LLT");
- var priority = key.GetValue("UP");
- var frequency = key.GetValue("UF");
- var dataHash = key.GetValue("DH") as string;
-
- if (launchCount != null && totalMinutes != null)
- {
- var stats = new UsageStats
- {
- DeviceId = DeviceId,
- LaunchCount = Convert.ToInt32(launchCount),
- TotalUsageMinutes = Convert.ToInt64(totalMinutes),
- LastLaunchTime = lastLaunchBinary != null ? DateTime.FromBinary(Convert.ToInt64(lastLaunchBinary)) : DateTime.Now,
- UpdatePriority = priority != null ? (UpdatePriority)Convert.ToInt32(priority) : UpdatePriority.Medium,
- UsageFrequency = frequency != null ? (UsageFrequency)Convert.ToInt32(frequency) : UsageFrequency.Medium,
- DataHash = dataHash,
- AverageSessionMinutes = 0,
- LastUpdateCheck = DateTime.MinValue
- };
-
- // 重新计算平均会话时长
- if (stats.LaunchCount > 0)
- {
- stats.AverageSessionMinutes = (double)stats.TotalUsageMinutes / stats.LaunchCount;
- }
-
- // 验证数据完整性(如果有哈希值)
- if (!string.IsNullOrEmpty(stats.DataHash))
- {
- if (stats.VerifyDataIntegrity())
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表位置恢复数据并验证完整性通过: {path}");
- return stats;
- }
-
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 注册表位置数据完整性验证失败: {path}", LogHelper.LogType.Warning);
- }
- else
- {
- // 没有哈希值的旧数据,更新哈希后返回
- stats.UpdateDataHash();
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表位置恢复旧版本数据: {path}");
- return stats;
- }
- }
- }
- }
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 从注册表位置加载失败 ({path}): {ex.Message}", LogHelper.LogType.Error);
- }
- }
-
- return null;
- }
-
- ///
- /// 保存使用统计到所有位置(强化版本 - 多重隐藏备份)
- ///
- private static void SaveUsageStatsToAllLocations(UsageStats stats)
- {
- // 保存到主文件
- SaveUsageStatsToFile(UsageStatsFilePath, stats);
-
- // 保存到第一备份文件
- SaveUsageStatsToFile(BackupUsageStatsPath, stats);
-
- // 保存到多个隐藏备份位置(专门针对使用频率数据保护)
- SaveUsageStatsToFile(SecondaryUsageBackupPath, stats);
- SaveUsageStatsToFile(TertiaryUsageBackupPath, stats);
- SaveUsageStatsToFile(QuaternaryUsageBackupPath, stats);
-
- // 保存到注册表
- SaveUsageStatsToRegistry(stats);
-
- // 保存到注册表的多个位置
- SaveUsageStatsToMultipleRegistryLocations(stats);
- }
-
- ///
- /// 保存使用统计到文件
+ ///
+ /// 保存使用统计到文件(加密)
///
private static void SaveUsageStatsToFile(string filePath, UsageStats stats)
{
@@ -1683,161 +1014,42 @@ namespace Ink_Canvas.Helpers
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
- // 设置隐藏属性
- if (filePath.Contains(".sys"))
- {
- var dirInfo = new DirectoryInfo(directory);
- dirInfo.Attributes |= FileAttributes.Hidden | FileAttributes.System;
- }
}
string json = JsonConvert.SerializeObject(stats, Formatting.Indented);
- File.WriteAllText(filePath, json);
-
- // 设置文件属性为隐藏和系统文件
- if (filePath.Contains(".sys"))
+ byte[] data = Encoding.UTF8.GetBytes(json);
+
+ // 使用SHA256生成加密密钥(基于设备ID)
+ using (var sha256 = SHA256.Create())
{
- var fileInfo = new FileInfo(filePath);
- fileInfo.Attributes |= FileAttributes.Hidden | FileAttributes.System;
- }
-
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 使用统计已保存到: {filePath}");
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 保存使用统计到文件失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error);
- }
- }
-
- ///
- /// 保存使用统计到注册表
- ///
- private static void SaveUsageStatsToRegistry(UsageStats stats)
- {
- try
- {
- // LogHelper.WriteLogToFile("DeviceIdentifier | 开始保存使用统计到主注册表位置");
-
- using (var key = Registry.CurrentUser.CreateSubKey(@"Software\ICC\DeviceInfo"))
- {
- if (key != null)
+ byte[] keyBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(DeviceId + "ICC_Usage_Stats_Salt"));
+
+ // 简单的XOR加密
+ byte[] encryptedData = new byte[data.Length];
+ for (int i = 0; i < data.Length; i++)
{
- key.SetValue("DeviceId", stats.DeviceId);
- key.SetValue("LaunchCount", stats.LaunchCount);
-
- // 秒级精度数据
- key.SetValue("TotalUsageSeconds", stats.TotalUsageSeconds);
- key.SetValue("AverageSessionSeconds", stats.AverageSessionSeconds);
-
- // 兼容性:分钟精度数据
- key.SetValue("TotalUsageMinutes", stats.TotalUsageMinutes);
- key.SetValue("AverageSessionMinutes", stats.AverageSessionMinutes);
-
- key.SetValue("LastLaunchTime", stats.LastLaunchTime.ToString("yyyy-MM-dd HH:mm:ss"));
- key.SetValue("UpdatePriority", (int)stats.UpdatePriority);
- key.SetValue("UsageFrequency", (int)stats.UsageFrequency);
- key.SetValue("DataHash", stats.DataHash ?? "");
- key.SetValue("LastUpdate", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
-
- // 每周统计数据(秒级精度)
- key.SetValue("WeeklyLaunchCount", stats.WeeklyLaunchCount);
- key.SetValue("WeeklyUsageSeconds", stats.WeeklyUsageSeconds);
- key.SetValue("LastWeekUsageSeconds", stats.LastWeekUsageSeconds);
-
- // 兼容性:分钟精度数据
- key.SetValue("WeeklyUsageMinutes", stats.WeeklyUsageMinutes);
- key.SetValue("LastWeekUsageMinutes", stats.LastWeekUsageMinutes);
-
- key.SetValue("WeekStartDate", stats.WeekStartDate.ToString("yyyy-MM-dd"));
- key.SetValue("LastWeekLaunchCount", stats.LastWeekLaunchCount);
-
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 使用统计已保存到主注册表 - 总启动: {stats.LaunchCount}次, 本周启动: {stats.WeeklyLaunchCount}次, " +
- // $"总时长: {FormatDuration(stats.TotalUsageSeconds)}, 本周时长: {FormatDuration(stats.WeeklyUsageSeconds)}");
- }
- else
- {
- // LogHelper.WriteLogToFile("DeviceIdentifier | 创建主注册表键失败", LogHelper.LogType.Error);
+ encryptedData[i] = (byte)(data[i] ^ keyBytes[i % keyBytes.Length]);
}
+
+ // 添加SHA256校验和
+ byte[] checksum = sha256.ComputeHash(data);
+ byte[] finalData = new byte[checksum.Length + encryptedData.Length];
+ checksum.CopyTo(finalData, 0);
+ encryptedData.CopyTo(finalData, checksum.Length);
+
+ File.WriteAllBytes(filePath, finalData);
+
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 加密使用统计已保存到: {filePath}");
}
}
catch (Exception ex)
{
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 保存使用统计到主注册表失败: {ex.Message}", LogHelper.LogType.Error);
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 保存使用统计到文件失败 ({filePath}): {ex.Message}", LogHelper.LogType.Error);
}
}
///
- /// 保存使用统计到多个注册表位置(强化保护)
- ///
- private static void SaveUsageStatsToMultipleRegistryLocations(UsageStats stats)
- {
- var registryPaths = new[]
- {
- @"Software\Microsoft\Windows\CurrentVersion\ICC",
- @"Software\Classes\.icc\UsageData",
- @"Software\ICC\Config\Usage"
- };
-
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 开始保存使用统计到{registryPaths.Length}个备用注册表位置");
- var successCount = 0;
-
- foreach (var path in registryPaths)
- {
- try
- {
- using (var key = Registry.CurrentUser.CreateSubKey(path))
- {
- if (key != null)
- {
- // 使用编码的键名来隐藏数据(秒级精度)
- key.SetValue("LC", stats.LaunchCount); // LaunchCount
-
- // 秒级精度数据
- key.SetValue("TUS", stats.TotalUsageSeconds); // TotalUsageSeconds
- key.SetValue("ASS", stats.AverageSessionSeconds); // AverageSessionSeconds
-
- // 兼容性:分钟精度数据
- key.SetValue("TUM", stats.TotalUsageMinutes); // TotalUsageMinutes
- key.SetValue("ASM", stats.AverageSessionMinutes); // AverageSessionMinutes
-
- key.SetValue("LLT", stats.LastLaunchTime.ToBinary()); // LastLaunchTime
- key.SetValue("UP", (int)stats.UpdatePriority); // UpdatePriority
- key.SetValue("UF", (int)stats.UsageFrequency); // UsageFrequency
- key.SetValue("DH", stats.DataHash ?? ""); // DataHash
- key.SetValue("LU", DateTime.Now.ToBinary()); // LastUpdate
-
- // 每周统计数据(秒级精度)
- key.SetValue("WLC", stats.WeeklyLaunchCount); // WeeklyLaunchCount
- key.SetValue("WUS", stats.WeeklyUsageSeconds); // WeeklyUsageSeconds
- key.SetValue("LWUS", stats.LastWeekUsageSeconds); // LastWeekUsageSeconds
-
- // 兼容性:分钟精度数据
- key.SetValue("WUM", stats.WeeklyUsageMinutes); // WeeklyUsageMinutes
- key.SetValue("LWUM", stats.LastWeekUsageMinutes); // LastWeekUsageMinutes
-
- key.SetValue("WSD", stats.WeekStartDate.ToBinary()); // WeekStartDate
- key.SetValue("LWLC", stats.LastWeekLaunchCount); // LastWeekLaunchCount
-
- successCount++;
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 成功保存到备用注册表位置: {path}");
- }
- else
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 创建备用注册表键失败: {path}", LogHelper.LogType.Error);
- }
- }
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 保存到备用注册表位置失败 ({path}): {ex.Message}", LogHelper.LogType.Error);
- }
- }
-
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 备用注册表保存完成: {successCount}/{registryPaths.Length} 成功");
- }
-
- ///
- /// 记录更新检查时间(同时执行数据保护检查)
+ /// 记录更新检查时间
///
public static void RecordUpdateCheck()
{
@@ -1847,14 +1059,7 @@ namespace Ink_Canvas.Helpers
{
var stats = LoadUsageStats();
stats.LastUpdateCheck = DateTime.Now;
- stats.UpdateDataHash();
SaveUsageStats(stats);
-
- // 定期执行数据保护检查(每10次更新检查执行一次)
- if (stats.LaunchCount % 10 == 0)
- {
- PerformUsageDataProtectionCheck();
- }
}
}
catch (Exception ex)
@@ -1864,139 +1069,130 @@ namespace Ink_Canvas.Helpers
}
///
- /// 执行使用频率数据保护检查和自动修复
+ /// 验证使用统计数据的完整性
///
- public static bool PerformUsageDataProtectionCheck()
+ public static bool ValidateUsageStatsIntegrity()
{
try
{
- lock (fileLock)
+ // 检查主文件
+ if (File.Exists(UsageStatsFilePath))
{
- LogHelper.WriteLogToFile("DeviceIdentifier | 开始使用频率数据保护检查");
-
- var issues = new List();
- var repaired = new List();
- var backupPaths = new[]
+ var mainStats = LoadUsageStatsFromFile(UsageStatsFilePath);
+ if (mainStats != null && mainStats.DeviceId == DeviceId)
{
- new { Path = UsageStatsFilePath, Name = "主文件" },
- new { Path = BackupUsageStatsPath, Name = "第一备份" },
- new { Path = SecondaryUsageBackupPath, Name = "第二备份" },
- new { Path = TertiaryUsageBackupPath, Name = "第三备份" },
- new { Path = QuaternaryUsageBackupPath, Name = "第四备份" }
- };
-
- // 检查所有备份文件
- UsageStats validStats = null;
- var missingFiles = new List();
-
- foreach (var backup in backupPaths)
- {
- if (!File.Exists(backup.Path))
- {
- issues.Add($"{backup.Name}丢失");
- missingFiles.Add(backup.Path);
- }
- else
- {
- var stats = LoadUsageStatsFromFile(backup.Path);
- if (stats != null && stats.VerifyDataIntegrity() && validStats == null)
- {
- validStats = stats;
- }
- }
+ LogHelper.WriteLogToFile("DeviceIdentifier | 主文件数据完整性验证通过");
+ return true;
}
-
- // 如果找到有效数据,修复丢失的文件
- if (validStats != null && missingFiles.Count > 0)
- {
- foreach (var missingFile in missingFiles)
- {
- SaveUsageStatsToFile(missingFile, validStats);
- repaired.Add($"恢复文件: {Path.GetFileName(missingFile)}");
- }
- }
-
- // 检查注册表备份
- var registryPaths = new[]
- {
- @"Software\ICC\DeviceInfo",
- @"Software\Microsoft\Windows\CurrentVersion\ICC",
- @"Software\Classes\.icc\UsageData",
- @"Software\ICC\Config\Usage"
- };
-
- var missingRegistryBackups = 0;
- foreach (var path in registryPaths)
- {
- try
- {
- using (var key = Registry.CurrentUser.OpenSubKey(path))
- {
- if (key == null) missingRegistryBackups++;
- }
- }
- catch
- {
- missingRegistryBackups++;
- }
- }
-
- if (missingRegistryBackups > 0)
- {
- issues.Add($"{missingRegistryBackups}个注册表备份丢失");
- if (validStats != null)
- {
- SaveUsageStatsToMultipleRegistryLocations(validStats);
- repaired.Add("重建注册表备份");
- }
- }
-
- // 如果没有找到任何有效数据,尝试从注册表恢复
- if (validStats == null)
- {
- validStats = LoadUsageStatsFromRegistry();
- if (validStats == null)
- {
- validStats = LoadUsageStatsFromMultipleRegistryLocations();
- }
-
- if (validStats != null)
- {
- SaveUsageStatsToAllLocations(validStats);
- repaired.Add("从注册表完全恢复数据");
- }
- else
- {
- // 最后手段:强制重建
- ForceRebuildUsageDataBackups();
- repaired.Add("强制重建所有备份");
- }
- }
-
- // 记录检查结果
- if (issues.Count > 0)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率数据保护检查发现问题: {string.Join(", ", issues)}", LogHelper.LogType.Warning);
- }
-
- if (repaired.Count > 0)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率数据保护修复: {string.Join(", ", repaired)}");
- }
-
- var protectionScore = CalculateUsageDataProtectionScore();
- LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率数据保护检查完成 - 问题: {issues.Count}, 修复: {repaired.Count}, 保护强度: {protectionScore}/100");
-
- return protectionScore >= 80;
}
+
+ // 检查备份文件
+ if (File.Exists(UsageStatsBackupPath))
+ {
+ var backupStats = LoadUsageStatsFromFile(UsageStatsBackupPath);
+ if (backupStats != null && backupStats.DeviceId == DeviceId)
+ {
+ LogHelper.WriteLogToFile("DeviceIdentifier | 备份文件数据完整性验证通过");
+ return true;
+ }
+ }
+
+ LogHelper.WriteLogToFile("DeviceIdentifier | 数据完整性验证失败", LogHelper.LogType.Warning);
+ return false;
}
catch (Exception ex)
{
- LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率数据保护检查失败: {ex.Message}", LogHelper.LogType.Error);
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性验证失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
+ ///
+ /// 从备份文件恢复使用统计数据
+ ///
+ public static bool RestoreUsageStatsFromBackup()
+ {
+ try
+ {
+ if (File.Exists(UsageStatsBackupPath))
+ {
+ var backupStats = LoadUsageStatsFromFile(UsageStatsBackupPath);
+ if (backupStats != null && backupStats.DeviceId == DeviceId)
+ {
+ // 保存到主文件
+ SaveUsageStatsToFile(UsageStatsFilePath, backupStats);
+ LogHelper.WriteLogToFile("DeviceIdentifier | 从备份文件成功恢复使用统计数据");
+ return true;
+ }
+ }
+
+ LogHelper.WriteLogToFile("DeviceIdentifier | 备份文件不存在或损坏,无法恢复", LogHelper.LogType.Warning);
+ return false;
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 从备份文件恢复失败: {ex.Message}", LogHelper.LogType.Error);
+ return false;
+ }
+ }
+
+ ///
+ /// 获取使用统计文件状态信息
+ ///
+ public static string GetUsageStatsFileStatus()
+ {
+ try
+ {
+ var status = new List();
+
+ // 检查主文件
+ if (File.Exists(UsageStatsFilePath))
+ {
+ var fileInfo = new FileInfo(UsageStatsFilePath);
+ var mainStats = LoadUsageStatsFromFile(UsageStatsFilePath);
+ if (mainStats != null && mainStats.DeviceId == DeviceId)
+ {
+ status.Add($"主文件: 正常 ({fileInfo.Length} 字节, 修改时间: {fileInfo.LastWriteTime:yyyy-MM-dd HH:mm:ss})");
+ }
+ else
+ {
+ status.Add($"主文件: 损坏 ({fileInfo.Length} 字节)");
+ }
+ }
+ else
+ {
+ status.Add("主文件: 不存在");
+ }
+
+ // 检查备份文件
+ if (File.Exists(UsageStatsBackupPath))
+ {
+ var fileInfo = new FileInfo(UsageStatsBackupPath);
+ var backupStats = LoadUsageStatsFromFile(UsageStatsBackupPath);
+ if (backupStats != null && backupStats.DeviceId == DeviceId)
+ {
+ status.Add($"备份文件: 正常 ({fileInfo.Length} 字节, 修改时间: {fileInfo.LastWriteTime:yyyy-MM-dd HH:mm:ss})");
+ }
+ else
+ {
+ status.Add($"备份文件: 损坏 ({fileInfo.Length} 字节)");
+ }
+ }
+ else
+ {
+ status.Add("备份文件: 不存在");
+ }
+
+ return string.Join("\n", status);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 获取文件状态失败: {ex.Message}", LogHelper.LogType.Error);
+ return "获取文件状态失败";
+ }
+ }
+
+
///
/// 根据优先级决定是否应该推送更新(仅适用于自动更新,版本修复功能不受影响)
///
@@ -2279,7 +1475,8 @@ namespace Ink_Canvas.Helpers
$"使用频率: {frequency}\n" +
$"更新优先级: {priority}\n" +
$"最后使用: {daysSinceLastUse:F1}天前\n" +
- $"用户类型: {GetUserTypeDescription(stats)}";
+ $"用户类型: {GetUserTypeDescription(stats)}\n\n" +
+ $"文件状态:\n{GetUsageStatsFileStatus()}";
}
catch (Exception ex)
{
@@ -2310,461 +1507,19 @@ namespace Ink_Canvas.Helpers
return descriptions.Count > 0 ? string.Join(", ", descriptions) : "普通用户";
}
- ///
- /// 数据自检和修复
- ///
- public static bool PerformDataIntegrityCheck()
- {
- try
- {
- lock (fileLock)
- {
- // LogHelper.WriteLogToFile("DeviceIdentifier | 开始数据完整性检查");
-
- var issues = new List();
- var repaired = new List();
-
- // 检查设备ID文件
- if (!File.Exists(DeviceIdFilePath))
- {
- issues.Add("主设备ID文件丢失");
- if (File.Exists(BackupDeviceIdPath))
- {
- var backupId = LoadDeviceIdFromFile(BackupDeviceIdPath);
- if (!string.IsNullOrEmpty(backupId))
- {
- SaveDeviceIdToFile(DeviceIdFilePath, backupId);
- repaired.Add("从备份恢复主设备ID文件");
- }
- }
- }
-
- // 检查备份设备ID文件
- if (!File.Exists(BackupDeviceIdPath))
- {
- issues.Add("备份设备ID文件丢失");
- var mainId = LoadDeviceIdFromFile(DeviceIdFilePath);
- if (!string.IsNullOrEmpty(mainId))
- {
- SaveDeviceIdToFile(BackupDeviceIdPath, mainId);
- repaired.Add("重建备份设备ID文件");
- }
- }
-
- // 检查使用统计文件
- if (!File.Exists(UsageStatsFilePath))
- {
- issues.Add("主使用统计文件丢失");
- var backupStatsForRestore = LoadUsageStatsFromFile(BackupUsageStatsPath);
- if (backupStatsForRestore != null)
- {
- SaveUsageStatsToFile(UsageStatsFilePath, backupStatsForRestore);
- repaired.Add("从备份恢复主使用统计文件");
- }
- }
-
- // 检查备份使用统计文件
- if (!File.Exists(BackupUsageStatsPath))
- {
- issues.Add("备份使用统计文件丢失");
- var mainStatsForBackup = LoadUsageStatsFromFile(UsageStatsFilePath);
- if (mainStatsForBackup != null)
- {
- SaveUsageStatsToFile(BackupUsageStatsPath, mainStatsForBackup);
- repaired.Add("重建备份使用统计文件");
- }
- }
-
- // 验证数据一致性
- var mainStats = LoadUsageStatsFromFile(UsageStatsFilePath);
- var backupStats = LoadUsageStatsFromFile(BackupUsageStatsPath);
-
- if (mainStats != null && backupStats != null)
- {
- if (mainStats.LaunchCount != backupStats.LaunchCount ||
- mainStats.TotalUsageMinutes != backupStats.TotalUsageMinutes)
- {
- issues.Add("主备份数据不一致");
- // 使用最新的数据
- var newerStats = mainStats.LastModified > backupStats.LastModified ? mainStats : backupStats;
- SaveUsageStatsToAllLocations(newerStats);
- repaired.Add("同步主备份数据");
- }
- }
-
- // 记录检查结果
- if (issues.Count > 0)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 发现问题: {string.Join(", ", issues)}", LogHelper.LogType.Warning);
- }
-
- if (repaired.Count > 0)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 已修复: {string.Join(", ", repaired)}");
- }
-
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性检查完成 - 问题: {issues.Count}, 修复: {repaired.Count}");
- return issues.Count == 0 || repaired.Count > 0;
- }
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 数据完整性检查失败: {ex.Message}", LogHelper.LogType.Error);
- return false;
- }
- }
-
- ///
- /// 获取使用频率数据保护状态摘要(强化版本)
- ///
- public static string GetUsageDataProtectionSummary()
- {
- try
- {
- var summary = new StringBuilder();
- summary.AppendLine("使用频率数据保护状态摘要:");
-
- // 检查主要文件
- summary.AppendLine($"主使用统计文件: {(File.Exists(UsageStatsFilePath) ? "✓" : "✗")}");
- summary.AppendLine($"第一备份文件: {(File.Exists(BackupUsageStatsPath) ? "✓" : "✗")}");
-
- // 检查多重隐藏备份
- summary.AppendLine($"第二备份文件: {(File.Exists(SecondaryUsageBackupPath) ? "✓" : "✗")}");
- summary.AppendLine($"第三备份文件: {(File.Exists(TertiaryUsageBackupPath) ? "✓" : "✗")}");
- summary.AppendLine($"第四备份文件: {(File.Exists(QuaternaryUsageBackupPath) ? "✓" : "✗")}");
-
- // 检查注册表备份
- var registryBackups = 0;
- var registryPaths = new[]
- {
- @"Software\ICC\DeviceInfo",
- @"Software\Microsoft\Windows\CurrentVersion\ICC",
- @"Software\Classes\.icc\UsageData",
- @"Software\ICC\Config\Usage"
- };
-
- foreach (var path in registryPaths)
- {
- try
- {
- using (var key = Registry.CurrentUser.OpenSubKey(path))
- {
- if (key != null) registryBackups++;
- }
- }
- catch { }
- }
-
- summary.AppendLine($"注册表备份位置: {registryBackups}/4 ✓");
-
- // 检查数据完整性和可恢复性
- var stats = LoadUsageStats();
- if (stats != null)
- {
- summary.AppendLine($"数据完整性: {(stats.VerifyDataIntegrity() ? "✓" : "✗")}");
- summary.AppendLine($"总启动次数: {stats.LaunchCount}");
- summary.AppendLine($"总使用时长: {FormatDuration(stats.TotalUsageSeconds)}");
- summary.AppendLine($"本周启动次数: {stats.WeeklyLaunchCount}");
- summary.AppendLine($"本周使用时长: {FormatDuration(stats.WeeklyUsageSeconds)}");
- summary.AppendLine($"上周启动次数: {stats.LastWeekLaunchCount}");
- summary.AppendLine($"上周使用时长: {FormatDuration(stats.LastWeekUsageSeconds)}");
- summary.AppendLine($"本周开始日期: {(stats.WeekStartDate != DateTime.MinValue ? stats.WeekStartDate.ToString("yyyy-MM-dd") : "未设置")}");
- summary.AppendLine($"使用频率: {stats.UsageFrequency}");
- summary.AppendLine($"更新优先级: {stats.UpdatePriority}");
- summary.AppendLine($"最后修改: {stats.LastModified:yyyy-MM-dd HH:mm:ss}");
- }
-
- // 计算保护强度评分
- var protectionScore = CalculateUsageDataProtectionScore();
- summary.AppendLine($"保护强度评分: {protectionScore}/100");
-
- return summary.ToString();
- }
- catch (Exception ex)
- {
- return $"获取使用频率数据保护状态失败: {ex.Message}";
- }
- }
-
- ///
- /// 计算使用频率数据保护强度评分
- ///
- private static int CalculateUsageDataProtectionScore()
- {
- var score = 0;
-
- try
- {
- // 文件备份评分(50分)
- if (File.Exists(UsageStatsFilePath)) score += 15;
- if (File.Exists(BackupUsageStatsPath)) score += 10;
- if (File.Exists(SecondaryUsageBackupPath)) score += 8;
- if (File.Exists(TertiaryUsageBackupPath)) score += 8;
- if (File.Exists(QuaternaryUsageBackupPath)) score += 9;
-
- // 注册表备份评分(30分)
- var registryPaths = new[]
- {
- @"Software\ICC\DeviceInfo",
- @"Software\Microsoft\Windows\CurrentVersion\ICC",
- @"Software\Classes\.icc\UsageData",
- @"Software\ICC\Config\Usage"
- };
-
- foreach (var path in registryPaths)
- {
- try
- {
- using (var key = Registry.CurrentUser.OpenSubKey(path))
- {
- if (key != null) score += 7;
- }
- }
- catch { }
- }
-
- // 数据完整性评分(20分)
- var stats = LoadUsageStats();
- if (stats != null)
- {
- if (!string.IsNullOrEmpty(stats.DataHash)) score += 10;
- if (stats.VerifyDataIntegrity()) score += 10;
- }
- }
- catch { }
-
- return Math.Min(100, score);
- }
-
- ///
- /// 强制重建所有使用频率数据备份
- ///
- public static bool ForceRebuildUsageDataBackups()
- {
- try
- {
- lock (fileLock)
- {
- // LogHelper.WriteLogToFile("DeviceIdentifier | 开始强制重建使用频率数据备份");
-
- var stats = LoadUsageStats();
- if (stats == null)
- {
- // 如果无法加载任何数据,创建基础数据
- stats = new UsageStats
- {
- DeviceId = DeviceId,
- LastLaunchTime = DateTime.Now,
- LaunchCount = 1,
- TotalUsageMinutes = 0,
- AverageSessionMinutes = 0,
- LastUpdateCheck = DateTime.MinValue,
- UpdatePriority = UpdatePriority.Medium,
- UsageFrequency = UsageFrequency.Medium
- };
- stats.UpdateDataHash();
- LogHelper.WriteLogToFile("DeviceIdentifier | 创建新的基础使用数据");
- }
-
- // 强制保存到所有位置
- SaveUsageStatsToAllLocations(stats);
-
- // 验证重建结果
- var protectionScore = CalculateUsageDataProtectionScore();
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 使用频率数据备份重建完成,保护强度: {protectionScore}/100");
-
- return protectionScore >= 80; // 80分以上认为重建成功
- }
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 强制重建使用频率数据备份失败: {ex.Message}", LogHelper.LogType.Error);
- return false;
- }
- }
-
- ///
- /// 获取数据保护状态摘要(保持向后兼容)
- ///
- public static string GetDataProtectionSummary()
- {
- return GetUsageDataProtectionSummary();
- }
-
-
-
- ///
- /// 强制执行一次完整的数据保存操作(包括注册表)
- ///
- public static bool ForceCompleteDataSave()
- {
- try
- {
- lock (fileLock)
- {
- // LogHelper.WriteLogToFile("DeviceIdentifier | 开始强制完整数据保存");
-
- // 保存设备ID到所有位置
- SaveDeviceIdToAllLocations(DeviceId);
-
- // 加载并保存使用统计到所有位置
- var stats = LoadUsageStats();
- if (stats != null)
- {
- stats.UpdateDataHash();
- SaveUsageStatsToAllLocations(stats);
-
- // 验证注册表保存是否成功
- var verificationResult = VerifyRegistryData();
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 注册表数据验证结果: {verificationResult}");
-
- // LogHelper.WriteLogToFile("DeviceIdentifier | 强制完整数据保存完成");
- return true;
- }
-
- // LogHelper.WriteLogToFile("DeviceIdentifier | 强制完整数据保存失败: 无法加载使用统计", LogHelper.LogType.Error);
- return false;
- }
- }
- catch (Exception ex)
- {
- // LogHelper.WriteLogToFile($"DeviceIdentifier | 强制完整数据保存失败: {ex.Message}", LogHelper.LogType.Error);
- return false;
- }
- }
-
- ///
- /// 验证注册表中的数据是否存在
- ///
- public static string VerifyRegistryData()
- {
- var results = new StringBuilder();
- results.AppendLine("注册表数据验证结果:");
-
- try
- {
- // 验证主注册表位置
- try
- {
- using (var key = Registry.CurrentUser.OpenSubKey(@"Software\ICC\DeviceInfo"))
- {
- if (key != null)
- {
- var deviceId = key.GetValue("DeviceId") as string;
- var launchCount = key.GetValue("LaunchCount");
- var totalMinutes = key.GetValue("TotalUsageMinutes");
- var lastUpdate = key.GetValue("LastUpdate") as string;
-
- results.AppendLine("✓ 主注册表位置存在");
- results.AppendLine($" 设备ID: {deviceId ?? "未找到"}");
- results.AppendLine($" 启动次数: {launchCount ?? "未找到"}");
- results.AppendLine($" 使用时长: {totalMinutes ?? "未找到"}分钟");
- results.AppendLine($" 最后更新: {lastUpdate ?? "未找到"}");
- }
- else
- {
- results.AppendLine("✗ 主注册表位置不存在");
- }
- }
- }
- catch (Exception ex)
- {
- results.AppendLine($"✗ 主注册表位置访问失败: {ex.Message}");
- }
-
- // 验证备用注册表位置
- var registryPaths = new[]
- {
- @"Software\Microsoft\Windows\CurrentVersion\ICC",
- @"Software\Classes\.icc\UsageData",
- @"Software\ICC\Config\Usage"
- };
-
- foreach (var path in registryPaths)
- {
- try
- {
- using (var key = Registry.CurrentUser.OpenSubKey(path))
- {
- if (key != null)
- {
- var launchCount = key.GetValue("LC");
- var totalMinutes = key.GetValue("TUM");
- var lastUpdate = key.GetValue("LU");
-
- results.AppendLine($"✓ 备用注册表位置存在: {path}");
- results.AppendLine($" 启动次数: {launchCount ?? "未找到"}");
- results.AppendLine($" 使用时长: {totalMinutes ?? "未找到"}");
- results.AppendLine($" 最后更新: {(lastUpdate != null ? DateTime.FromBinary(Convert.ToInt64(lastUpdate)).ToString("yyyy-MM-dd HH:mm:ss") : "未找到")}");
- }
- else
- {
- results.AppendLine($"✗ 备用注册表位置不存在: {path}");
- }
- }
- }
- catch (Exception ex)
- {
- results.AppendLine($"✗ 备用注册表位置访问失败 ({path}): {ex.Message}");
- }
- }
-
- return results.ToString();
- }
- catch (Exception ex)
- {
- return $"注册表数据验证失败: {ex.Message}";
- }
- }
-
- ///
- /// 立即执行一次数据保存并验证注册表写入
- ///
- public static string SaveAndVerifyRegistryData()
- {
- try
- {
- // LogHelper.WriteLogToFile("DeviceIdentifier | 开始保存并验证注册表数据");
-
- // 强制保存数据
- var saveSuccess = ForceCompleteDataSave();
-
- // 验证注册表数据
- var verificationResult = VerifyRegistryData();
-
- var result = $"保存操作: {(saveSuccess ? "成功" : "失败")}\n\n{verificationResult}";
-
- // LogHelper.WriteLogToFile("DeviceIdentifier | 保存并验证注册表数据完成");
- return result;
- }
- catch (Exception ex)
- {
- var errorMsg = $"保存并验证注册表数据失败: {ex.Message}";
- LogHelper.WriteLogToFile($"DeviceIdentifier | {errorMsg}", LogHelper.LogType.Error);
- return errorMsg;
- }
- }
///
/// 关机时保存使用时间数据
///
public static void SaveUsageStatsOnShutdown()
{
- // 使用超时锁防止死锁
- if (!Monitor.TryEnter(fileLock, TimeSpan.FromSeconds(30)))
- {
- LogHelper.WriteLogToFile("DeviceIdentifier | 关机保存超时,使用备用保存策略", LogHelper.LogType.Warning);
- SaveUsageStatsOnShutdownFallback();
- return;
- }
-
try
{
LogHelper.WriteLogToFile("DeviceIdentifier | 开始关机时保存使用时间数据", LogHelper.LogType.Info);
- // 1. 加载现有使用统计数据(多重恢复策略)
- UsageStats stats = LoadUsageStatsWithFallback();
+ // 1. 加载现有使用统计数据
+ var stats = LoadUsageStats();
if (stats == null)
{
stats = new UsageStats { DeviceId = DeviceId };
@@ -2788,261 +1543,16 @@ namespace Ink_Canvas.Helpers
stats.AverageSessionSeconds = stats.TotalUsageSeconds / (double)Math.Max(1, stats.LaunchCount);
stats.LastLaunchTime = DateTime.Now;
- // 更新数据哈希值
- stats.UpdateDataHash();
-
- // 4. 多重保存策略 - 确保数据不丢失
- var saveResults = new List();
+ // 4. 保存数据
+ SaveUsageStats(stats);
- // 4.1 保存到所有文件位置
- saveResults.Add(SaveUsageStatsToAllFileLocations(stats));
-
- // 4.2 保存到所有注册表位置
- saveResults.Add(SaveUsageStatsToAllRegistryLocations(stats));
-
- // 4.3 保存到内存缓存(作为最后防线)
- SaveUsageStatsToMemoryCache(stats);
-
- // 4.4 强制刷新文件系统缓存
- ForceFlushFileSystem();
-
- // 4.5 验证保存结果
- var verificationResult = VerifyDataSaveResults(stats, saveResults);
-
- LogHelper.WriteLogToFile($"DeviceIdentifier | 关机保存完成,验证结果: {verificationResult}", LogHelper.LogType.Info);
+ LogHelper.WriteLogToFile("DeviceIdentifier | 关机保存完成", LogHelper.LogType.Info);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"DeviceIdentifier | 关机时保存使用时间数据失败: {ex.Message}", LogHelper.LogType.Error);
-
- // 即使主保存失败,也要尝试备用保存
- try
- {
- SaveUsageStatsOnShutdownFallback();
- }
- catch (Exception fallbackEx)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 备用保存也失败: {fallbackEx.Message}", LogHelper.LogType.Error);
- }
- }
- finally
- {
- Monitor.Exit(fileLock);
}
}
-
- ///
- /// 关机保存的备用策略
- ///
- private static void SaveUsageStatsOnShutdownFallback()
- {
- try
- {
- LogHelper.WriteLogToFile("DeviceIdentifier | 执行关机保存备用策略", LogHelper.LogType.Warning);
-
- // 使用最基本的保存方式
- var stats = new UsageStats { DeviceId = DeviceId };
- stats.TotalUsageSeconds = 1; // 最小记录
- stats.LaunchCount = 1;
- stats.LastLaunchTime = DateTime.Now;
-
- // 只保存到最可靠的位置
- SaveUsageStatsToFile(BackupUsageStatsPath, stats);
- SaveUsageStatsToRegistry(stats);
-
- LogHelper.WriteLogToFile("DeviceIdentifier | 备用策略执行完成", LogHelper.LogType.Info);
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 备用策略执行失败: {ex.Message}", LogHelper.LogType.Error);
- }
- }
-
- ///
- /// 加载使用统计数据(带多重恢复策略)
- ///
- private static UsageStats LoadUsageStatsWithFallback()
- {
- try
- {
- // 1. 尝试从主文件加载
- var stats = LoadUsageStats();
- if (stats != null) return stats;
-
- // 2. 尝试从备份文件加载
- stats = LoadUsageStatsFromFile(BackupUsageStatsPath);
- if (stats != null) return stats;
-
- // 3. 尝试从其他备份位置加载
- var backupPaths = new[] { SecondaryUsageBackupPath, TertiaryUsageBackupPath, QuaternaryUsageBackupPath };
- foreach (var path in backupPaths)
- {
- stats = LoadUsageStatsFromFile(path);
- if (stats != null) return stats;
- }
-
- // 4. 尝试从注册表恢复
- stats = LoadUsageStatsFromRegistry();
- if (stats != null) return stats;
-
- return null;
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 多重恢复加载失败: {ex.Message}", LogHelper.LogType.Error);
- return null;
- }
- }
-
- ///
- /// 保存使用统计到所有文件位置
- ///
- private static string SaveUsageStatsToAllFileLocations(UsageStats stats)
- {
- var results = new List();
- var filePaths = new[]
- {
- UsageStatsFilePath,
- BackupUsageStatsPath,
- SecondaryUsageBackupPath,
- TertiaryUsageBackupPath,
- QuaternaryUsageBackupPath
- };
-
- foreach (var filePath in filePaths)
- {
- try
- {
- SaveUsageStatsToFile(filePath, stats);
- results.Add($"✓ {Path.GetFileName(filePath)}");
- }
- catch (Exception ex)
- {
- results.Add($"✗ {Path.GetFileName(filePath)}: {ex.Message}");
- }
- }
-
- return string.Join("\n", results);
- }
-
- ///
- /// 保存使用统计到所有注册表位置
- ///
- private static string SaveUsageStatsToAllRegistryLocations(UsageStats stats)
- {
- var results = new List();
-
- try
- {
- // 主注册表位置
- SaveUsageStatsToRegistry(stats);
- results.Add("✓ 主注册表位置");
- }
- catch (Exception ex)
- {
- results.Add($"✗ 主注册表位置: {ex.Message}");
- }
-
- try
- {
- // 备用注册表位置
- SaveUsageStatsToMultipleRegistryLocations(stats);
- results.Add("✓ 备用注册表位置");
- }
- catch (Exception ex)
- {
- results.Add($"✗ 备用注册表位置: {ex.Message}");
- }
-
- return string.Join("\n", results);
- }
-
- ///
- /// 保存使用统计到内存缓存
- ///
- private static void SaveUsageStatsToMemoryCache(UsageStats stats)
- {
- try
- {
- // 将数据保存到静态变量作为内存备份
- _cachedUsageStats = stats;
- LogHelper.WriteLogToFile("DeviceIdentifier | 数据已保存到内存缓存", LogHelper.LogType.Info);
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 保存到内存缓存失败: {ex.Message}", LogHelper.LogType.Error);
- }
- }
-
- ///
- /// 强制刷新文件系统缓存
- ///
- private static void ForceFlushFileSystem()
- {
- try
- {
- // 强制刷新所有相关目录
- var directories = new[]
- {
- Path.GetDirectoryName(UsageStatsFilePath),
- Path.GetDirectoryName(BackupUsageStatsPath),
- Path.GetDirectoryName(SecondaryUsageBackupPath),
- Path.GetDirectoryName(TertiaryUsageBackupPath),
- Path.GetDirectoryName(QuaternaryUsageBackupPath)
- };
-
- foreach (var dir in directories.Where(d => !string.IsNullOrEmpty(d) && Directory.Exists(d)))
- {
- try
- {
- // 创建临时文件来强制刷新
- var tempFile = Path.Combine(dir, ".flush.tmp");
- File.WriteAllText(tempFile, DateTime.Now.ToString());
- File.Delete(tempFile);
- }
- catch { /* 忽略刷新错误 */ }
- }
-
- LogHelper.WriteLogToFile("DeviceIdentifier | 文件系统缓存刷新完成", LogHelper.LogType.Info);
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"DeviceIdentifier | 文件系统缓存刷新失败: {ex.Message}", LogHelper.LogType.Error);
- }
- }
-
- ///
- /// 验证数据保存结果
- ///
- private static string VerifyDataSaveResults(UsageStats stats, List saveResults)
- {
- var verification = new StringBuilder();
- verification.AppendLine("数据保存验证结果:");
- verification.AppendLine(string.Join("\n", saveResults));
-
- // 验证关键数据是否保存成功
- try
- {
- var savedStats = LoadUsageStats();
- if (savedStats != null && savedStats.DeviceId == stats.DeviceId)
- {
- verification.AppendLine("✓ 主数据文件验证成功");
- }
- else
- {
- verification.AppendLine("✗ 主数据文件验证失败");
- }
- }
- catch (Exception ex)
- {
- verification.AppendLine($"✗ 主数据文件验证异常: {ex.Message}");
- }
-
- return verification.ToString();
- }
-
- // 内存缓存变量
- private static UsageStats _cachedUsageStats;
}
}
From f6d8558d07e19f412280e2f00173b572a0c1ce08 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sun, 31 Aug 2025 00:33:41 +0800
Subject: [PATCH 19/23] =?UTF-8?q?improve:=E8=87=AA=E5=8A=A8=E6=9B=B4?=
=?UTF-8?q?=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Helpers/AutoUpdateHelper.cs | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/Ink Canvas/Helpers/AutoUpdateHelper.cs b/Ink Canvas/Helpers/AutoUpdateHelper.cs
index c403f286..fe21d61c 100644
--- a/Ink Canvas/Helpers/AutoUpdateHelper.cs
+++ b/Ink Canvas/Helpers/AutoUpdateHelper.cs
@@ -27,8 +27,6 @@ namespace Ink_Canvas.Helpers
private static readonly string updatesFolderPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AutoUpdate");
private static string statusFilePath;
- // GitHub Token认证
- private static readonly string GitHubToken = "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";
// 线路组结构体(包含版本、下载、日志地址)
public class UpdateLineGroup
@@ -415,8 +413,7 @@ namespace Ink_Canvas.Helpers
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
- client.DefaultRequestHeaders.Add("Authorization", $"token {GitHubToken}");
- LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub Token进行API调用");
+ LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub API调用");
var response = await client.GetStringAsync(apiUrl);
var releases = JArray.Parse(response);
@@ -458,8 +455,7 @@ namespace Ink_Canvas.Helpers
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
- client.DefaultRequestHeaders.Add("Authorization", $"token {GitHubToken}");
- LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub Token进行API调用");
+ LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub API调用");
var response = await client.GetStringAsync(apiUrl);
var json = JObject.Parse(response);
string version = json["tag_name"]?.ToString();
@@ -1899,8 +1895,7 @@ namespace Ink_Canvas.Helpers
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "ICC-CE Auto Updater");
- client.DefaultRequestHeaders.Add("Authorization", $"token {GitHubToken}");
- LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub Token进行API调用");
+ LogHelper.WriteLogToFile("AutoUpdate | 使用GitHub API调用");
var response = await client.GetStringAsync(apiUrl);
var arr = JArray.Parse(response);
foreach (var item in arr)
From cd7a801400ec6be990ba568ded85182d7ab6efe3 Mon Sep 17 00:00:00 2001
From: CJKmkp <2564608840@qq.com>
Date: Sun, 31 Aug 2025 00:39:40 +0800
Subject: [PATCH 20/23] =?UTF-8?q?=E8=BF=98=E6=9C=AA=E5=AE=8C=E6=88=90?=
=?UTF-8?q?=E7=9A=84=E9=80=89=E6=8B=A9=E5=B7=A5=E5=85=B7=E6=A0=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow.xaml | 106 ++++++
.../MainWindow_cs/MW_ElementsControls.cs | 306 +++++++++++++++++-
2 files changed, 408 insertions(+), 4 deletions(-)
diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml
index dc8c2a44..c00ff977 100644
--- a/Ink Canvas/MainWindow.xaml
+++ b/Ink Canvas/MainWindow.xaml
@@ -3443,6 +3443,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
index c23719af..cbfb5a81 100644
--- a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs
@@ -154,6 +154,12 @@ namespace Ink_Canvas
// 使用鼠标拖动的完整实现机制
ApplyMouseDragTransform(element, currentPoint, dragStartPoint);
+ // 如果是图片元素,更新工具栏位置
+ if (element is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
+ {
+ UpdateImageSelectionToolbarPosition(element);
+ }
+
dragStartPoint = currentPoint;
e.Handled = true;
}
@@ -181,6 +187,12 @@ namespace Ink_Canvas
// 使用触摸拖动的完整实现
ApplyTouchManipulationTransform(element, e);
+ // 如果是图片元素,更新工具栏位置
+ if (element is Image && BorderImageSelectionControl?.Visibility == Visibility.Visible)
+ {
+ UpdateImageSelectionToolbarPosition(element);
+ }
+
e.Handled = true;
}
}
@@ -247,8 +259,37 @@ namespace Ink_Canvas
currentSelectedElement = element;
isElementSelected = true;
- // 去除选中效果,避免蓝色底边问题
- // 可以通过其他方式(如状态变量)来跟踪选中状态
+ // 根据元素类型显示不同的选择工具栏
+ if (element is Image)
+ {
+ // 显示图片选择工具栏并设置位置
+ if (BorderImageSelectionControl != null)
+ {
+ // 计算工具栏位置
+ UpdateImageSelectionToolbarPosition(element);
+ BorderImageSelectionControl.Visibility = Visibility.Visible;
+ }
+
+ // 隐藏笔画选择工具栏
+ if (BorderStrokeSelectionControl != null)
+ {
+ BorderStrokeSelectionControl.Visibility = Visibility.Collapsed;
+ }
+ }
+ else
+ {
+ // 显示笔画选择工具栏
+ if (BorderStrokeSelectionControl != null)
+ {
+ BorderStrokeSelectionControl.Visibility = Visibility.Visible;
+ }
+
+ // 隐藏图片选择工具栏
+ if (BorderImageSelectionControl != null)
+ {
+ BorderImageSelectionControl.Visibility = Visibility.Collapsed;
+ }
+ }
// 确保选择框不显示,避免蓝色边框
if (GridInkCanvasSelectionCover != null)
@@ -272,6 +313,17 @@ namespace Ink_Canvas
// 去除选中效果
isElementSelected = false;
+ // 隐藏所有选择工具栏
+ if (BorderImageSelectionControl != null)
+ {
+ BorderImageSelectionControl.Visibility = Visibility.Collapsed;
+ }
+
+ if (BorderStrokeSelectionControl != null)
+ {
+ BorderStrokeSelectionControl.Visibility = Visibility.Collapsed;
+ }
+
// 确保选择框隐藏
if (GridInkCanvasSelectionCover != null)
{
@@ -676,9 +728,9 @@ namespace Ink_Canvas
/// 克隆图片
///
/// 要克隆的图片
- private void CloneImage(Image image)
+ private Image CloneImage(Image image)
{
- if (image == null) return;
+ if (image == null) return null;
try
{
@@ -707,6 +759,8 @@ namespace Ink_Canvas
// 记录错误但不中断程序
System.Diagnostics.Debug.WriteLine($"克隆图片时发生错误: {ex.Message}");
}
+
+ return null;
}
///
@@ -932,5 +986,249 @@ namespace Ink_Canvas
inkCanvas.EditingMode = InkCanvasEditingMode.None;
}
}
+
+ // 更新图片选择工具栏位置
+ private void UpdateImageSelectionToolbarPosition(FrameworkElement element)
+ {
+ try
+ {
+ if (BorderImageSelectionControl == null || element == null) return;
+
+ // 获取元素在画布中的位置
+ double elementLeft = InkCanvas.GetLeft(element);
+ double elementTop = InkCanvas.GetTop(element);
+ double elementWidth = element.ActualWidth;
+ double elementHeight = element.ActualHeight;
+
+ // 如果元素位置未设置,使用默认值
+ if (double.IsNaN(elementLeft)) elementLeft = 0;
+ if (double.IsNaN(elementTop)) elementTop = 0;
+
+ // 计算工具栏位置(显示在图片下方)
+ double toolbarLeft = elementLeft + (elementWidth / 2) - (BorderImageSelectionControl.ActualWidth / 2);
+ double toolbarTop = elementTop + elementHeight + 10; // 图片下方10像素
+
+ // 确保工具栏不超出画布边界
+ double maxLeft = inkCanvas.ActualWidth - BorderImageSelectionControl.ActualWidth;
+ double maxTop = inkCanvas.ActualHeight - BorderImageSelectionControl.ActualHeight;
+
+ toolbarLeft = Math.Max(0, Math.Min(toolbarLeft, maxLeft));
+ toolbarTop = Math.Max(0, Math.Min(toolbarTop, maxTop));
+
+ // 设置工具栏位置
+ BorderImageSelectionControl.Margin = new Thickness(toolbarLeft, toolbarTop, 0, 0);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"更新图片选择工具栏位置失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ #region Image Selection Toolbar Event Handlers
+
+ // 图片克隆功能
+ private void BorderImageClone_MouseUp(object sender, MouseButtonEventArgs e)
+ {
+ try
+ {
+ if (currentSelectedElement is Image originalImage)
+ {
+ // 创建克隆图片
+ Image clonedImage = CloneImage(originalImage);
+
+ // 添加到画布
+ inkCanvas.Children.Add(clonedImage);
+
+ // 初始化变换
+ InitializeElementTransform(clonedImage);
+
+ // 绑定事件
+ BindElementEvents(clonedImage);
+
+ // 记录历史
+ timeMachine.CommitElementInsertHistory(clonedImage);
+
+ LogHelper.WriteLogToFile($"图片克隆完成: {clonedImage.Name}");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"图片克隆失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 图片克隆到新页面
+ private void BorderImageCloneToNewBoard_MouseUp(object sender, MouseButtonEventArgs e)
+ {
+ try
+ {
+ if (currentSelectedElement is Image originalImage)
+ {
+ // 创建克隆图片
+ Image clonedImage = CloneImage(originalImage);
+
+ // 这里可以添加切换到新页面的逻辑
+ // 暂时先添加到当前页面
+ inkCanvas.Children.Add(clonedImage);
+
+ // 初始化变换
+ InitializeElementTransform(clonedImage);
+
+ // 绑定事件
+ BindElementEvents(clonedImage);
+
+ // 记录历史
+ timeMachine.CommitElementInsertHistory(clonedImage);
+
+ LogHelper.WriteLogToFile($"图片克隆到新页面完成: {clonedImage.Name}");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"图片克隆到新页面失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 图片左旋转
+ private void BorderImageRotateLeft_MouseUp(object sender, MouseButtonEventArgs e)
+ {
+ try
+ {
+ if (currentSelectedElement != null)
+ {
+ ApplyRotateTransform(currentSelectedElement, -45);
+ LogHelper.WriteLogToFile($"图片左旋转完成");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"图片左旋转失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 图片右旋转
+ private void BorderImageRotateRight_MouseUp(object sender, MouseButtonEventArgs e)
+ {
+ try
+ {
+ if (currentSelectedElement != null)
+ {
+ ApplyRotateTransform(currentSelectedElement, 45);
+ LogHelper.WriteLogToFile($"图片右旋转完成");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"图片右旋转失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 图片缩放减小
+ private void GridImageScaleDecrease_MouseUp(object sender, MouseButtonEventArgs e)
+ {
+ try
+ {
+ if (currentSelectedElement != null)
+ {
+ var elementCenter = new Point(currentSelectedElement.ActualWidth / 2, currentSelectedElement.ActualHeight / 2);
+ ApplyScaleTransform(currentSelectedElement, 0.9, elementCenter);
+ LogHelper.WriteLogToFile($"图片缩放减小完成");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"图片缩放减小失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 图片缩放增大
+ private void GridImageScaleIncrease_MouseUp(object sender, MouseButtonEventArgs e)
+ {
+ try
+ {
+ if (currentSelectedElement != null)
+ {
+ var elementCenter = new Point(currentSelectedElement.ActualWidth / 2, currentSelectedElement.ActualHeight / 2);
+ ApplyScaleTransform(currentSelectedElement, 1.1, elementCenter);
+ LogHelper.WriteLogToFile($"图片缩放增大完成");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"图片缩放增大失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 图片删除
+ private void BorderImageDelete_MouseUp(object sender, MouseButtonEventArgs e)
+ {
+ try
+ {
+ if (currentSelectedElement != null)
+ {
+ // 记录删除历史
+ timeMachine.CommitElementRemoveHistory(currentSelectedElement);
+
+ // 从画布中移除
+ inkCanvas.Children.Remove(currentSelectedElement);
+
+ // 清除选中状态
+ UnselectElement(currentSelectedElement);
+ currentSelectedElement = null;
+
+ LogHelper.WriteLogToFile($"图片删除完成");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"图片删除失败: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ // 克隆图片的辅助方法
+ private Image CreateClonedImage(Image originalImage)
+ {
+ try
+ {
+ Image clonedImage = new Image();
+
+ // 复制图片源
+ if (originalImage.Source is BitmapSource bitmapSource)
+ {
+ clonedImage.Source = bitmapSource;
+ }
+
+ // 复制属性
+ clonedImage.Width = originalImage.Width;
+ clonedImage.Height = originalImage.Height;
+ clonedImage.Stretch = originalImage.Stretch;
+ clonedImage.StretchDirection = originalImage.StretchDirection;
+
+ // 复制位置
+ double left = InkCanvas.GetLeft(originalImage);
+ double top = InkCanvas.GetTop(originalImage);
+ InkCanvas.SetLeft(clonedImage, left + 20); // 稍微偏移位置
+ InkCanvas.SetTop(clonedImage, top + 20);
+
+ // 复制变换
+ if (originalImage.RenderTransform is TransformGroup originalTransformGroup)
+ {
+ clonedImage.RenderTransform = originalTransformGroup.Clone();
+ }
+
+ // 设置名称
+ string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff");
+ clonedImage.Name = timestamp;
+
+ return clonedImage;
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"克隆图片失败: {ex.Message}", LogHelper.LogType.Error);
+ return null;
+ }
+ }
+
+ #endregion
}
}
From 16283f4643790ba546df2fc9139c4f9cb8e5e4d3 Mon Sep 17 00:00:00 2001
From: PrefacedCorg <1876568293@qq.com>
Date: Sun, 31 Aug 2025 01:00:59 +0800
Subject: [PATCH 21/23] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=A9=BA=E5=80=BC?=
=?UTF-8?q?=E6=A3=80=E6=9F=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow.xaml.cs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs
index f31390d8..4de702cb 100644
--- a/Ink Canvas/MainWindow.xaml.cs
+++ b/Ink Canvas/MainWindow.xaml.cs
@@ -2030,7 +2030,10 @@ namespace Ink_Canvas
try
{
Settings.Canvas.InkFadeTime = (int)e.NewValue;
- _inkFadeManager.UpdateFadeTime(Settings.Canvas.InkFadeTime);
+ if (_inkFadeManager != null)
+ {
+ _inkFadeManager.UpdateFadeTime(Settings.Canvas.InkFadeTime);
+ }
LogHelper.WriteLogToFile($"墨迹渐隐时间已更新为 {Settings.Canvas.InkFadeTime}ms", LogHelper.LogType.Event);
}
catch (Exception ex)
From 2fe482b802e9292628fc8a7b94bb1796cfa8069d Mon Sep 17 00:00:00 2001
From: PrefacedCorg <1876568293@qq.com>
Date: Sun, 31 Aug 2025 01:47:00 +0800
Subject: [PATCH 22/23] =?UTF-8?q?=E8=AE=A9=E8=AD=A6=E5=91=8A=E6=B2=A1?=
=?UTF-8?q?=E9=82=A3=E4=B9=88=E5=A4=9A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
应该不会出问题的吧
---
Ink Canvas/Helpers/DeviceIdentifier.cs | 56 +++++++++----------
Ink Canvas/Helpers/InkFadeManager.cs | 6 +-
Ink Canvas/MainWindow.xaml.cs | 4 +-
.../Windows/HotkeySettingsWindow.xaml.cs | 2 +-
4 files changed, 32 insertions(+), 36 deletions(-)
diff --git a/Ink Canvas/Helpers/DeviceIdentifier.cs b/Ink Canvas/Helpers/DeviceIdentifier.cs
index 82bc3897..04736514 100644
--- a/Ink Canvas/Helpers/DeviceIdentifier.cs
+++ b/Ink Canvas/Helpers/DeviceIdentifier.cs
@@ -404,28 +404,28 @@ namespace Ink_Canvas.Helpers
try
{
// 如果新字段为空但旧字段有数据,进行迁移
- if (TotalUsageSeconds == 0 && TotalUsageMinutes > 0)
+ if (TotalUsageSeconds == 0 && TotalUsageSeconds > 0)
{
- TotalUsageSeconds = TotalUsageMinutes * 60;
- LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移总使用时长: {TotalUsageMinutes}分钟 -> {TotalUsageSeconds}秒");
+ TotalUsageSeconds = TotalUsageSeconds * 60;
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移总使用时长: {TotalUsageSeconds}分钟 -> {TotalUsageSeconds}秒");
}
- if (AverageSessionSeconds == 0 && AverageSessionMinutes > 0)
+ if (AverageSessionSeconds == 0 && AverageSessionSeconds > 0)
{
- AverageSessionSeconds = AverageSessionMinutes * 60;
- LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移平均会话时长: {AverageSessionMinutes}分钟 -> {AverageSessionSeconds}秒");
+ AverageSessionSeconds = AverageSessionSeconds * 60;
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移平均会话时长: {AverageSessionSeconds}分钟 -> {AverageSessionSeconds}秒");
}
- if (WeeklyUsageSeconds == 0 && WeeklyUsageMinutes > 0)
+ if (WeeklyUsageSeconds == 0 && WeeklyUsageSeconds > 0)
{
- WeeklyUsageSeconds = WeeklyUsageMinutes * 60;
- LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移每周使用时长: {WeeklyUsageMinutes}分钟 -> {WeeklyUsageSeconds}秒");
+ WeeklyUsageSeconds = WeeklyUsageSeconds * 60;
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移每周使用时长: {WeeklyUsageSeconds}分钟 -> {WeeklyUsageSeconds}秒");
}
- if (LastWeekUsageSeconds == 0 && LastWeekUsageMinutes > 0)
+ if (LastWeekUsageSeconds == 0 && LastWeekUsageSeconds > 0)
{
- LastWeekUsageSeconds = LastWeekUsageMinutes * 60;
- LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移上周使用时长: {LastWeekUsageMinutes}分钟 -> {LastWeekUsageSeconds}秒");
+ LastWeekUsageSeconds = LastWeekUsageSeconds * 60;
+ LogHelper.WriteLogToFile($"DeviceIdentifier | 迁移上周使用时长: {LastWeekUsageSeconds}分钟 -> {LastWeekUsageSeconds}秒");
}
}
catch (Exception ex)
@@ -450,12 +450,12 @@ namespace Ink_Canvas.Helpers
LastWeekUsageSeconds = WeeklyUsageSeconds;
// 同时更新旧字段以保持兼容性
- LastWeekUsageMinutes = LastWeekUsageSeconds / 60;
+ LastWeekUsageSeconds = LastWeekUsageSeconds / 60;
// 重置本周数据
WeeklyLaunchCount = 0;
WeeklyUsageSeconds = 0;
- WeeklyUsageMinutes = 0;
+ WeeklyUsageSeconds = 0;
WeekStartDate = currentWeekStart;
LogHelper.WriteLogToFile($"DeviceIdentifier | 每周统计重置 - 上周启动: {LastWeekLaunchCount}次, 上周使用: {FormatDuration(LastWeekUsageSeconds)}");
@@ -489,7 +489,7 @@ namespace Ink_Canvas.Helpers
CheckAndResetWeeklyStats();
WeeklyUsageSeconds += seconds;
// 同时更新旧字段以保持兼容性
- WeeklyUsageMinutes = WeeklyUsageSeconds / 60;
+ WeeklyUsageSeconds = WeeklyUsageSeconds / 60;
}
}
@@ -601,7 +601,7 @@ namespace Ink_Canvas.Helpers
stats.TotalUsageSeconds += sessionSeconds;
// 同时更新旧字段以保持兼容性
- stats.TotalUsageMinutes = stats.TotalUsageSeconds / 60;
+ stats.TotalUsageSeconds = stats.TotalUsageSeconds / 60;
// 记录每周使用时长(秒级精度)
stats.RecordWeeklyUsage(sessionSeconds);
@@ -611,7 +611,7 @@ namespace Ink_Canvas.Helpers
{
stats.AverageSessionSeconds = (double)stats.TotalUsageSeconds / stats.LaunchCount;
// 同时更新旧字段以保持兼容性
- stats.AverageSessionMinutes = stats.AverageSessionSeconds / 60;
+ stats.AverageSessionSeconds = stats.AverageSessionSeconds / 60;
}
}
@@ -650,9 +650,9 @@ namespace Ink_Canvas.Helpers
var currentWeekSeconds = stats.WeeklyUsageSeconds;
// 如果秒级数据为空但分钟数据存在,进行转换
- if (currentWeekSeconds == 0 && stats.WeeklyUsageMinutes > 0)
+ if (currentWeekSeconds == 0 && stats.WeeklyUsageSeconds > 0)
{
- currentWeekSeconds = stats.WeeklyUsageMinutes * 60;
+ currentWeekSeconds = stats.WeeklyUsageSeconds * 60;
}
// 如果本周数据不足,参考上周数据
@@ -660,9 +660,9 @@ namespace Ink_Canvas.Helpers
var weeklySeconds = currentWeekSeconds > 0 ? currentWeekSeconds : stats.LastWeekUsageSeconds;
// 如果秒级数据仍为空,使用分钟数据转换
- if (weeklySeconds == 0 && stats.LastWeekUsageMinutes > 0)
+ if (weeklySeconds == 0 && stats.LastWeekUsageSeconds > 0)
{
- weeklySeconds = stats.LastWeekUsageMinutes * 60;
+ weeklySeconds = stats.LastWeekUsageSeconds * 60;
}
// 综合评分系统(0-100分)
@@ -731,7 +731,7 @@ namespace Ink_Canvas.Helpers
else if (weeklySeconds >= 3600) score += 5; // 1-2小时:轻度使用
// 历史使用深度评分(10分)- 反映用户的长期使用习惯(秒级精度)
- var totalSeconds = stats.TotalUsageSeconds > 0 ? stats.TotalUsageSeconds : stats.TotalUsageMinutes * 60;
+ var totalSeconds = stats.TotalUsageSeconds > 0 ? stats.TotalUsageSeconds : stats.TotalUsageSeconds * 60;
if (totalSeconds >= 180000) score += 10; // 50小时以上:资深用户
else if (totalSeconds >= 72000) score += 7; // 20-50小时:中等用户
else if (totalSeconds >= 18000) score += 4; // 5-20小时:新手用户
@@ -854,10 +854,8 @@ namespace Ink_Canvas.Helpers
DeviceId = DeviceId,
LastLaunchTime = DateTime.Now,
LaunchCount = 0,
- TotalUsageSeconds = 0,
- AverageSessionSeconds = 0,
- TotalUsageMinutes = 0, // 保持兼容性
- AverageSessionMinutes = 0, // 保持兼容性
+ TotalUsageSeconds = 0, // 保持兼容性
+ AverageSessionSeconds = 0, // 保持兼容性
LastUpdateCheck = DateTime.MinValue,
UpdatePriority = UpdatePriority.Medium,
UsageFrequency = UsageFrequency.Medium
@@ -879,8 +877,6 @@ namespace Ink_Canvas.Helpers
LaunchCount = 0,
TotalUsageSeconds = 0,
AverageSessionSeconds = 0,
- TotalUsageMinutes = 0,
- AverageSessionMinutes = 0,
LastUpdateCheck = DateTime.MinValue,
UpdatePriority = UpdatePriority.Medium,
UsageFrequency = UsageFrequency.Medium
@@ -1316,7 +1312,7 @@ namespace Ink_Canvas.Helpers
double daysBetweenVersions, double daysSinceLastUse, UsageStats stats, UpdateType updateType)
{
// 考虑用户的总体使用模式
- var isHeavyUser = stats.TotalUsageMinutes > 3000; // 超过50小时的重度用户
+ var isHeavyUser = stats.TotalUsageSeconds > 3000; // 超过50小时的重度用户
var isFrequentUser = stats.LaunchCount > 100; // 启动超过100次的频繁用户
// 根据更新类型调整推送策略
@@ -1490,7 +1486,7 @@ namespace Ink_Canvas.Helpers
///
private static string GetUserTypeDescription(UsageStats stats)
{
- var isHeavyUser = stats.TotalUsageMinutes > 3000;
+ var isHeavyUser = stats.TotalUsageSeconds > 3000;
var isFrequentUser = stats.LaunchCount > 100;
var daysSinceLastUse = (DateTime.Now - stats.LastLaunchTime).TotalDays;
diff --git a/Ink Canvas/Helpers/InkFadeManager.cs b/Ink Canvas/Helpers/InkFadeManager.cs
index 1bd3d9c2..febc4662 100644
--- a/Ink Canvas/Helpers/InkFadeManager.cs
+++ b/Ink Canvas/Helpers/InkFadeManager.cs
@@ -335,7 +335,7 @@ namespace Ink_Canvas.Helpers
return path;
}
- catch (Exception ex)
+ catch (Exception)
{
return null;
}
@@ -497,7 +497,7 @@ namespace Ink_Canvas.Helpers
// 开始分段渐隐动画
StartSegmentedFadeAnimation(segments, stroke, originalVisual, duration);
}
- catch (Exception ex)
+ catch (Exception)
{
StartSimpleFadeAnimation(originalVisual, stroke, opacity, duration);
}
@@ -551,7 +551,7 @@ namespace Ink_Canvas.Helpers
return path;
}
- catch (Exception ex)
+ catch (Exception)
{
return null;
}
diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs
index 4de702cb..5e9794d2 100644
--- a/Ink Canvas/MainWindow.xaml.cs
+++ b/Ink Canvas/MainWindow.xaml.cs
@@ -1360,7 +1360,7 @@ namespace Ink_Canvas
// 直接设置滚动位置,不使用动画
SettingsPanelScrollViewer.ScrollToVerticalOffset(targetPosition);
}
- catch (Exception ex)
+ catch (Exception)
{
// 如果出现异常,恢复到原来的滚动位置
SettingsPanelScrollViewer.ScrollToVerticalOffset(originalOffset);
@@ -2106,7 +2106,7 @@ namespace Ink_Canvas
}
}
}
- catch (Exception ex)
+ catch (Exception)
{
// 如果直接发送失败,回退到原来的方法
if (isPrevious)
diff --git a/Ink Canvas/Windows/HotkeySettingsWindow.xaml.cs b/Ink Canvas/Windows/HotkeySettingsWindow.xaml.cs
index 5539d553..ea00e278 100644
--- a/Ink Canvas/Windows/HotkeySettingsWindow.xaml.cs
+++ b/Ink Canvas/Windows/HotkeySettingsWindow.xaml.cs
@@ -224,7 +224,7 @@ namespace Ink_Canvas.Windows
break;
}
}
- catch (Exception ex)
+ catch (Exception)
{
// 设置默认快捷键时出错,忽略
}
From d76195f7ae8c8408a4067289c71b4f0ceb1d5ad0 Mon Sep 17 00:00:00 2001
From: PrefacedCorg <1876568293@qq.com>
Date: Sun, 31 Aug 2025 01:55:17 +0800
Subject: [PATCH 23/23] =?UTF-8?q?fix:=E4=BA=8C=E7=BA=A7=E8=8F=9C=E5=8D=95?=
=?UTF-8?q?=E4=B8=8B=E4=B8=8D=E5=8E=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
index 3e90076a..ff1b66d6 100644
--- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
+++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
@@ -238,6 +238,9 @@ namespace Ink_Canvas
BoardBorderLeftPageListView.Visibility = Visibility.Collapsed;
BoardBorderRightPageListView.Visibility = Visibility.Collapsed;
BoardImageOptionsPanel.Visibility = Visibility.Collapsed;
+ // 添加隐藏图形工具的二级菜单面板
+ BorderDrawShape.Visibility = Visibility.Collapsed;
+ BoardBorderDrawShape.Visibility = Visibility.Collapsed;
}
///