Compare commits

..

372 Commits

Author SHA1 Message Date
CJK_mkp e20bf41cc7 Merge pull request #195 from InkCanvasForClass/beta
ICC CE 1.7.11.0
2025-09-13 23:05:18 +08:00
CJKmkp 0f7b9524f9 fix:收纳模式下进入PPT自动进入批注异常 2025-09-13 22:53:31 +08:00
CJKmkp 53a9e901ec fix:退出PPT时错误的快捷键状态 2025-09-13 22:44:29 +08:00
CJKmkp 9a79fc71ed improve:PPT墨迹 2025-09-13 22:20:41 +08:00
CJKmkp e7f7a8038f 优化代码 2025-09-13 21:41:34 +08:00
CJKmkp 5361f8ae6f improve:墨迹延迟 2025-09-13 21:38:03 +08:00
CJKmkp ec3bcddc9d 更新版本号 2025-09-13 21:15:58 +08:00
CJKmkp 326a9f1d75 优化代码 2025-09-13 21:10:36 +08:00
CJKmkp 8c07e9b8a3 优化代码 2025-09-13 19:54:21 +08:00
CJKmkp 406d01febf 优化代码 2025-09-13 19:35:36 +08:00
CJKmkp 98663422b1 优化代码 2025-09-13 19:29:17 +08:00
CJKmkp 879dfcea28 add:issue #190 2025-09-13 19:10:40 +08:00
CJKmkp 40edb4c8de Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-09-13 18:53:49 +08:00
CJKmkp 07b6d142ad add:issue #190 2025-09-13 18:52:23 +08:00
2,2,3-三甲基戊烷 5716b3e3cf docs&fix: 移除ssh还存在于readme贡献者名单的bug 2025-09-13 18:45:59 +08:00
CJKmkp 1b242a07e1 delete:ssh 2025-09-13 18:29:13 +08:00
CJK_mkp 1516a73228 Merge pull request #192 from ShihaoShen2025/patch-1
删除all contributors src中有关hydro的信息
2025-09-13 18:22:46 +08:00
CJKmkp eef00eb28f add:issue #190 2025-09-13 18:20:56 +08:00
CJKmkp a5ac282ab6 add:issue #190 2025-09-13 18:13:31 +08:00
CJKmkp dbb88d4999 add:issue #190 2025-09-13 18:07:42 +08:00
电教沈同学 39addad3a1 Delete myself from ac-src
是时候给一切画上一个句号了。
再见,ICC-CE。
2025-09-13 17:58:06 +08:00
电教沈同学 87fd9a4470 Update .all-contributorsrc 2025-09-13 17:52:52 +08:00
电教沈同学 7a9156449f Update .all-contributorsrc 2025-09-13 17:50:36 +08:00
CJKmkp 6e2938e9c1 add:issue #190 2025-09-13 17:37:11 +08:00
CJKmkp 39e1498b26 add:issue #180 2025-09-13 16:36:41 +08:00
CJKmkp cc2dc9c1a7 add:issue #180 2025-09-13 16:28:31 +08:00
CJKmkp fa87198131 add:issue #180 2025-09-13 16:26:23 +08:00
CJKmkp 9b70b952f6 add:issue #180 2025-09-13 16:13:22 +08:00
CJKmkp b29fca1cdb add:issue #180 2025-09-13 16:09:51 +08:00
CJKmkp 501af800ce add:issue #180 2025-09-13 16:06:19 +08:00
CJKmkp 979be117c6 add:issue #180 2025-09-13 16:02:39 +08:00
CJKmkp 13594371bb add:issue #180 2025-09-13 15:34:50 +08:00
CJKmkp 9628d1b27f add:issue #181 2025-09-13 14:59:47 +08:00
CJKmkp fd0bd0b343 fix:托盘功能不可用 2025-09-13 14:33:49 +08:00
CJKmkp 9ea58bfdad 优化代码 2025-09-13 14:21:07 +08:00
CJKmkp c54d140107 improve:截图 2025-09-13 14:16:11 +08:00
CJKmkp 98d4a4213c improve:截图 2025-09-13 13:59:54 +08:00
CJKmkp a7d1de5ee3 improve:截图 2025-09-13 13:50:41 +08:00
CJKmkp fab9e4b265 improve:截图 2025-09-13 13:37:51 +08:00
CJKmkp d5fa46033e 优化日志 2025-09-13 13:03:04 +08:00
CJKmkp 32c179bbf9 improve:弹窗 2025-09-13 12:22:08 +08:00
CJKmkp 99f9886876 improve:直接调用外部点名 2025-09-13 12:08:45 +08:00
CJKmkp c1fcaff28a fix:issue #175 简易实现(后续会改) 2025-09-13 12:05:13 +08:00
CJKmkp 2da5e8d71b fix:动画导致的收纳问题 2025-09-13 11:56:26 +08:00
CJKmkp e80e64a287 fix:手势开关状态显示错误 2025-09-13 11:46:49 +08:00
CJKmkp 0344e51ef7 fix:issue #185 2025-09-13 11:38:46 +08:00
CJKmkp 9fd31b0584 improve:描述性文字 2025-09-13 11:32:08 +08:00
CJKmkp c6a48f79da fix:issue #184 2025-09-13 11:29:48 +08:00
CJKmkp 110d050cc6 fix:issue #188 2025-09-13 11:08:56 +08:00
CJKmkp 18188ef235 improve:弹窗 2025-09-13 11:04:04 +08:00
CJKmkp 3e1c397132 fix:issue #160 2025-09-13 10:58:43 +08:00
CJKmkp cda1f0b77d fix:无焦点功能状态丢失 2025-09-13 10:55:27 +08:00
CJKmkp afd49f154c improve:模式切换 issue #179 2025-09-13 10:51:08 +08:00
CJKmkp 505c620103 improve:PowerPoint联动增强 2025-09-13 10:48:33 +08:00
CJKmkp 5d9e340d6d improve:模式切换 issue #179 2025-09-13 10:46:02 +08:00
CJKmkp f705c4575c Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-09-13 10:43:50 +08:00
CJKmkp 1508bd806f 优化日志 2025-09-13 10:43:22 +08:00
CJK_mkp 1c542e0615 Update bug_report.yml 2025-09-12 15:59:11 +08:00
CJK_mkp ed5bcbde18 Update feature_request.yml 2025-09-12 15:58:36 +08:00
PrefacedCorg 084cbcd362 代码清理 2025-09-07 13:30:46 +08:00
CJKmkp ad8369cfe9 更新版本号 2025-09-07 13:25:20 +08:00
CJKmkp 7d36b3993e 优化代码 2025-09-07 09:41:42 +08:00
CJKmkp ead0854c8e improve:墨迹选择 2025-09-07 07:59:21 +08:00
CJKmkp 60b5655574 improve:墨迹选择 2025-09-07 07:53:05 +08:00
CJKmkp 9037630990 fix:issue #175 2025-09-07 01:50:44 +08:00
CJKmkp ba23fc9506 add:issue #171 2025-09-07 01:45:50 +08:00
CJKmkp 938ca7c0ea improve:墨迹选择 2025-09-07 01:22:46 +08:00
CJKmkp a034f7a9a0 improve:墨迹选择 2025-09-07 01:16:27 +08:00
PrefacedCorg d4c4fcfd74 Update PluginSettingsWindow.xaml 2025-09-07 01:02:34 +08:00
PrefacedCorg 5b457f2a8a 将插件管理器的页面改为无边框 2025-09-07 00:59:24 +08:00
CJKmkp adc22441fc fix:issue #173 2025-09-07 00:57:48 +08:00
CJKmkp 1ba59136c6 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-09-07 00:49:58 +08:00
CJKmkp 77702b2ac9 fix:issue #170 2025-09-07 00:49:10 +08:00
PrefacedCorg 0d6c814908 好像改错了() 2025-09-07 00:49:01 +08:00
PrefacedCorg 42ce58db60 贡献名单add PrefacedCorg(2) 2025-09-07 00:47:30 +08:00
PrefacedCorg 1ef56033fb 贡献名单add PrefacedCorg(1) 2025-09-07 00:31:22 +08:00
CJKmkp 8c1d3c0248 fix:issue #168 2025-09-07 00:20:48 +08:00
CJKmkp a725a12d25 fix:issue #169 2025-09-07 00:14:59 +08:00
CJKmkp f67db9beed fix:issue #178 #177 2025-09-07 00:11:22 +08:00
CJKmkp f22fd7b5d1 fix:issue #172 2025-09-07 00:08:11 +08:00
CJKmkp 060664260b fix:issue #172 2025-09-07 00:03:15 +08:00
CJKmkp 9da5ec7413 撤销操作 2025-09-06 23:58:00 +08:00
CJKmkp 076240f7cd 测试构建 2025-09-06 23:55:54 +08:00
CJKmkp d97a4ad243 fix:墨迹识别 2025-09-06 23:21:05 +08:00
CJKmkp 56209d8491 fix:issue #159 2025-09-06 22:22:41 +08:00
CJK_mkp 61fe7197f5 Merge pull request #166 from InkCanvasForClass/beta
ICC CE 1.7.10.0
2025-09-06 21:40:14 +08:00
CJKmkp 1881bfd69e 更新版本号 2025-09-06 21:35:07 +08:00
CJKmkp 4287b7def5 improve:用户分级 2025-09-06 21:33:06 +08:00
CJKmkp 27d683a7cc improve:PPT及自动更新 2025-09-06 21:26:46 +08:00
CJKmkp 38902c8f88 撤回操作 2025-09-06 20:59:45 +08:00
CJKmkp d8e8142ff4 发行说明测试 2025-09-06 20:57:24 +08:00
CJKmkp fdaf9cb3ef 发行说明测试 2025-09-06 20:37:05 +08:00
CJKmkp b2fe091c88 发行说明测试 2025-09-06 20:27:52 +08:00
CJKmkp 8548244cef 发行说明测试 2025-09-06 20:24:56 +08:00
CJKmkp 91a5881600 发行说明测试 2025-09-06 20:22:59 +08:00
CJKmkp 93fd043b14 发行说明测试 2025-09-06 20:21:24 +08:00
CJKmkp 47948ef530 更新版本号 2025-09-06 20:08:26 +08:00
CJKmkp 61fa618673 improve:用户分级 2025-09-06 20:02:50 +08:00
CJKmkp 545aecc6ae 更新版本号 2025-09-06 19:31:14 +08:00
CJKmkp ee41f53286 add:issue #156 2025-09-06 19:27:17 +08:00
CJKmkp cc8036f736 improve:无焦点 2025-09-06 18:12:45 +08:00
CJKmkp 67a982e6e4 improve:用户分级 2025-09-06 18:03:23 +08:00
CJKmkp b16000f8e5 improve:窗口置顶 2025-09-06 17:51:25 +08:00
CJKmkp a142ce0542 回滚操作 2025-09-06 17:41:34 +08:00
CJKmkp c87b8b65fc improve:高光显示 2025-09-06 17:12:25 +08:00
CJKmkp 7332df1d56 fix:issue #165 #133 及插件窗口问题 2025-09-06 15:50:16 +08:00
CJKmkp de90c17ab1 fix:issue #159 2025-09-06 15:36:40 +08:00
CJKmkp 3d54edf25b fix:issue #161 2025-09-06 15:28:14 +08:00
CJKmkp 111819dbf3 fix:issue #164 #155 2025-09-06 15:16:03 +08:00
CJKmkp 3665753ba2 优化日志 2025-09-06 15:14:53 +08:00
CJKmkp 75e38aa8f3 improve:issue #160 2025-09-06 15:09:34 +08:00
CJKmkp 29dc6938c3 improve:窗口置顶 2025-09-06 15:07:26 +08:00
CJKmkp 304933b02b improve:快捷键 2025-09-06 14:54:49 +08:00
CJKmkp 49f268bb62 improve:快捷键 2025-09-06 14:48:36 +08:00
CJKmkp d0793c546d improve:快捷键 2025-09-06 14:36:07 +08:00
CJKmkp 375aec6f6c improve:按钮显示 2025-09-06 14:24:18 +08:00
CJKmkp 12ec527dcd improve:浮动栏 2025-09-06 14:19:33 +08:00
CJKmkp 52d95173d7 improve:PPT模块及浮动栏 2025-09-06 14:18:09 +08:00
CJKmkp 67e3d51106 improve:窗口置顶 2025-09-06 13:58:49 +08:00
CJKmkp 9081aea926 improve:墨迹渐隐 2025-09-06 13:48:44 +08:00
CJKmkp 02c6caf465 fix:issue #153 2025-09-06 13:30:32 +08:00
CJKmkp 1a9ddf1969 improve:墨迹渐隐 2025-09-06 13:16:10 +08:00
CJKmkp 41bcae9d05 fix:issue #153 2025-09-06 12:38:57 +08:00
CJKmkp 28109a0c97 improve:PPT联动 2025-09-06 12:09:06 +08:00
CJKmkp d8825f0a73 fix:issue #154 2025-09-06 10:20:28 +08:00
CJKmkp 82c045a243 fix:issue #152 2025-09-06 10:17:12 +08:00
CJKmkp 4d11f69282 fix:issue #151 2025-09-06 10:11:19 +08:00
CJKmkp 664e4ea048 add:新设置 2025-08-31 17:11:04 +08:00
CJKmkp 6d21c5e24e add:新设置 2025-08-31 17:07:40 +08:00
CJKmkp ba21b6884d add:新设置 2025-08-31 16:25:52 +08:00
CJKmkp 60065aab3e add:新设置 2025-08-31 16:24:57 +08:00
CJKmkp 4d978936ac add:新设置 2025-08-31 16:17:21 +08:00
CJKmkp 4ea585633b add:新设置 2025-08-31 16:06:36 +08:00
CJKmkp dee0bd7f4d add:新设置 2025-08-31 15:58:58 +08:00
CJKmkp edb206ffa5 add:新设置 2025-08-31 15:35:28 +08:00
CJKmkp 21932056f3 improve:插件 2025-08-31 13:14:05 +08:00
CJK_mkp 0c3809b789 Merge pull request #147 from InkCanvasForClass/beta
ICC CE 1.7.9.1
2025-08-31 12:39:00 +08:00
CJKmkp d5ae596ad6 更新版本号 2025-08-31 12:37:49 +08:00
CJKmkp 42db7e60f2 improve:截图 2025-08-31 12:32:15 +08:00
CJKmkp 85827820a4 Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-08-31 12:17:13 +08:00
CJKmkp fad2571edb fix:合并冲突 2025-08-31 12:16:29 +08:00
PrefacedCorg c53b6de68c Update ScreenshotSelectorWindow.xaml.cs 2025-08-31 12:14:46 +08:00
PrefacedCorg c86eb12168 Update YesOrNoNotificationWindow.xaml 2025-08-31 12:07:59 +08:00
CJKmkp fb31f8df11 优化代码 2025-08-31 12:05:26 +08:00
CJKmkp 147b4fc5ed improve:文件关联 2025-08-31 12:03:58 +08:00
CJKmkp cdcb2d2af0 add:文件关联 2025-08-31 11:49:00 +08:00
PrefacedCorg ff086e497c 代码清理 2025-08-31 11:43:52 +08:00
PrefacedCorg a2b711da05 改了个图标 2025-08-31 11:41:17 +08:00
CJKmkp 40ecfbefa3 improve:截图 2025-08-31 10:47:33 +08:00
CJK_mkp 2097a1250b Merge pull request #146 from InkCanvasForClass/beta
ICC CE 1.7.9.0
2025-08-31 10:24:44 +08:00
CJK_mkp 22025f9b00 Merge pull request #145 from InkCanvasForClass/beta
ICC CE 1.7.9.0
2025-08-31 10:22:57 +08:00
CJKmkp 80c1badba5 更新版本号 2025-08-31 10:08:49 +08:00
CJKmkp be308ba280 improve:截图 2025-08-31 10:06:11 +08:00
CJKmkp fa7f3d44e4 improve:截图及浮动栏 2025-08-31 09:59:49 +08:00
CJKmkp 9bb00489fe improve:截图及浮动栏及代码优化 2025-08-31 09:54:13 +08:00
CJKmkp 33948c604c improve:图片插入 2025-08-31 09:20:35 +08:00
CJKmkp b8fe5bbd66 improve:图片插入 2025-08-31 09:18:30 +08:00
CJKmkp 3fcc01c253 improve:图片插入 2025-08-31 09:15:53 +08:00
CJKmkp 658d48c17b improve:图片插入 2025-08-31 09:12:30 +08:00
CJKmkp 8d76c014c8 优化代码 2025-08-31 09:09:22 +08:00
CJKmkp 26cd125534 优化代码 2025-08-31 09:00:32 +08:00
CJKmkp 1bc23af61a 优化代码 2025-08-31 08:51:50 +08:00
CJKmkp a2aa6b48a8 improve:用户体验分级及图片插入 2025-08-31 08:04:46 +08:00
CJKmkp 5784c974f7 优化代码 2025-08-31 07:56:26 +08:00
CJKmkp 80503dc42e 优化代码 2025-08-31 07:54:43 +08:00
PrefacedCorg d76195f7ae fix:二级菜单下不去 2025-08-31 01:55:17 +08:00
PrefacedCorg 2fe482b802 让警告没那么多
应该不会出问题的吧
2025-08-31 01:47:00 +08:00
PrefacedCorg 16283f4643 添加空值检查 2025-08-31 01:00:59 +08:00
CJKmkp cd7a801400 还未完成的选择工具栏 2025-08-31 00:39:40 +08:00
CJKmkp f6d8558d07 improve:自动更新 2025-08-31 00:33:41 +08:00
CJKmkp de20b506f0 improve:用户体验分级 2025-08-31 00:25:22 +08:00
CJKmkp ea1d52292e improve:快捷键 2025-08-30 23:53:26 +08:00
CJKmkp 463e506ca3 improve:快捷键 2025-08-30 23:28:28 +08:00
CJKmkp 3bae64a2c7 improve:插入图片improve:插入图片 2025-08-30 23:19:53 +08:00
CJKmkp 5d13c8b543 improve:插入图片 2025-08-30 23:14:00 +08:00
CJKmkp c6c0789794 improve:自动更新 2025-08-30 23:10:02 +08:00
CJKmkp d7df39290f improve:插入图片及墨迹平滑 2025-08-30 22:59:12 +08:00
CJKmkp b64cefad46 improve:插入图片及墨迹平滑 2025-08-30 22:54:16 +08:00
CJKmkp 4690ab3c30 improve:插入图片 2025-08-30 22:14:39 +08:00
CJKmkp b61c7490b3 improve:截图 2025-08-30 21:23:36 +08:00
CJKmkp 9e511d29a6 delete:图片插入选中和移动相关 2025-08-30 21:18:34 +08:00
CJKmkp a9cdc36967 improve:长按翻页 2025-08-30 20:59:50 +08:00
CJKmkp 96c51cd7ff 优化代码 2025-08-30 19:40:14 +08:00
CJKmkp 636769c0ef fix:插入图片子面板折叠问题 2025-08-30 18:59:32 +08:00
CJKmkp 7beb2a3cc5 更新版本号 2025-08-30 18:49:31 +08:00
CJKmkp 52d318054c fix:部分情况高光错位 2025-08-30 18:48:46 +08:00
CJKmkp 5ee2ad6f3d improve:浮动栏动态调整 2025-08-30 18:36:20 +08:00
CJKmkp 097d414d0d fix:隐藏按钮导致的高光错位 2025-08-30 18:33:57 +08:00
CJK_mkp e0ce119374 Merge pull request #144 from InkCanvasForClass/beta
ICC CE 1.7.8.3
2025-08-30 18:24:09 +08:00
CJKmkp da9a5242cb 上传日志 2025-08-30 18:22:37 +08:00
CJKmkp 0211b38be9 更新版本号 2025-08-30 18:12:43 +08:00
CJKmkp 512e9b21cd improve:issue #143 2025-08-30 18:10:55 +08:00
CJKmkp 9181ad55ae fix:部分窗口无法点击 2025-08-30 16:25:10 +08:00
CJKmkp a1cb9e1b28 improve:自动更新 2025-08-30 16:10:20 +08:00
CJKmkp ea09a3e798 improve:自动更新 2025-08-30 15:49:37 +08:00
CJKmkp 21fa146f6f improve:自动更新 2025-08-30 15:28:51 +08:00
CJKmkp 1a3130041f improve:自动更新 2025-08-30 14:56:59 +08:00
CJKmkp 7095a5890c fix:issue #133 2025-08-30 14:53:50 +08:00
CJKmkp 4a86d1aa05 improve:自动更新 2025-08-30 14:11:39 +08:00
CJKmkp 19fe7223fb improve:快捷键 2025-08-30 11:34:29 +08:00
CJKmkp 8867fde3f2 fix:issue #141 2025-08-30 11:29:16 +08:00
CJKmkp 588d1822b1 fix:issue #141 2025-08-30 11:20:53 +08:00
CJK_mkp d2b3b38d9e fix:issue #141 2025-08-28 22:13:18 +08:00
CJK_mkp 206ae3d9ed fix:issue #141 2025-08-28 21:02:47 +08:00
CJK_mkp a67b7a2fd0 fix:issue #141 2025-08-28 20:13:43 +08:00
CJK_mkp f5a08b225c fix:issue #141 2025-08-28 18:35:17 +08:00
CJK_mkp c807f97c29 fix:issue #141 2025-08-28 18:32:47 +08:00
CJK_mkp 8382413137 fix:issue #141 2025-08-28 18:31:59 +08:00
CJK_mkp c6acafd3a6 fix:issue #141 2025-08-28 18:28:17 +08:00
CJK_mkp 9d43056361 fix:issue #141 2025-08-28 18:25:04 +08:00
CJK_mkp 52f8e24954 fix:issue #141 2025-08-28 18:24:12 +08:00
CJK_mkp dac05fec84 fix:issue #141 2025-08-28 18:20:44 +08:00
CJK_mkp cb4ed77572 fix:issue #141 2025-08-28 18:17:29 +08:00
CJK_mkp 3427cbdc2e fix:issue #141 2025-08-28 18:11:26 +08:00
CJK_mkp ec0fb0c3cf fix:issue #141 2025-08-28 18:10:52 +08:00
CJK_mkp 357983179c fix:issue #141 2025-08-28 18:07:39 +08:00
CJK_mkp 78b66c141e fix:issue #141 2025-08-28 18:05:20 +08:00
CJK_mkp 3ac88bb400 fix:issue #141 2025-08-28 18:02:44 +08:00
CJK_mkp 27493b857c 撤销更改 2025-08-28 17:41:29 +08:00
CJK_mkp e5a976abcf fix:issue #141 2025-08-28 17:37:08 +08:00
CJK_mkp 9e89bf5303 Merge pull request #142 from InkCanvasForClass/all-contributors/add-Jursin
docs: add Jursin as a contributor for design
2025-08-28 09:01:25 +08:00
allcontributors[bot] 54eb330711 docs: update .all-contributorsrc 2025-08-28 01:01:08 +00:00
allcontributors[bot] e9014c6f5d docs: update README.md 2025-08-28 01:01:07 +00:00
CJK_mkp 620bcf6fab fix:issue #140 2025-08-27 17:43:04 +08:00
CJK_mkp c03bbd9e13 fix:issue #140 2025-08-27 17:41:58 +08:00
CJK_mkp b428b4ec5b 回滚等待重构截图功能 2025-08-27 15:57:06 +08:00
CJK_mkp 876cc116ea fix:issue #140 2025-08-27 12:23:05 +08:00
CJK_mkp 0ba00c08ef fix:更新日志显示问题 2025-08-25 09:53:01 +08:00
CJK_mkp d41ecd9d55 fix:修正错误地址 2025-08-25 09:00:45 +08:00
CJK_mkp 35d351ce6b Merge pull request #138 from InkCanvasForClass/beta
ICC CE 1.7.8.0
2025-08-24 18:02:28 +08:00
CJKmkp 5d3af58361 fix:浮动栏动画异常 2025-08-24 18:01:04 +08:00
CJKmkp 8554b92f42 improve:外部点名 2025-08-24 17:26:26 +08:00
CJKmkp 76363b4263 更新版本号 2025-08-24 16:29:51 +08:00
CJKmkp b4250b9161 fix:开关状态显示错误 2025-08-24 16:24:29 +08:00
CJKmkp 441f40886f fix:issue #133 2025-08-24 16:09:14 +08:00
CJKmkp e8e8ad5d63 更新版本号 2025-08-24 12:13:55 +08:00
CJKmkp 454078ec82 add:更多插件支持 2025-08-24 12:02:29 +08:00
CJK_mkp 63f29c2686 Merge pull request #137 from InkCanvasForClass/beta
ICC CE 1.7.7.7
2025-08-24 11:58:06 +08:00
CJKmkp 280445f613 fix:浮动栏动画异常 2025-08-24 11:47:39 +08:00
CJKmkp d7f6433b53 更新版本号 2025-08-24 11:09:41 +08:00
CJKmkp 22da4cc408 fix:无焦点无法翻页 2025-08-24 11:08:13 +08:00
CJKmkp 4e185ef584 improve:长按翻页 2025-08-24 10:39:24 +08:00
CJKmkp fd9b4b4ba6 improve:墨迹渐隐开关 2025-08-24 10:17:21 +08:00
CJKmkp 2f79bbcb0f fix:错误的唤起URL 2025-08-24 09:46:52 +08:00
CJKmkp 2c70d243df improve:墨迹渐隐开关 2025-08-24 09:33:27 +08:00
CJKmkp 7710b77255 improve:快捷键和墨迹渐隐 2025-08-24 09:17:58 +08:00
CJKmkp d1eed23399 improve:快捷键 2025-08-24 09:09:48 +08:00
CJKmkp ff9ce4df44 imrpove:快捷键 2025-08-24 08:45:53 +08:00
CJKmkp b7c52842f2 improve:快捷键 2025-08-24 02:30:38 +08:00
CJKmkp 365459f649 improve:快捷键 2025-08-24 02:27:57 +08:00
CJKmkp e6354f724f 优化日志 2025-08-24 02:03:34 +08:00
CJKmkp 14eedca939 fix:时间戳问题 2025-08-24 01:46:48 +08:00
CJKmkp 83529cfe09 add:issue #135 #136 2025-08-24 00:05:26 +08:00
CJKmkp bd4e1c1810 更新版本号 2025-08-23 23:32:44 +08:00
CJKmkp 5665fcc823 add:issue #131 2025-08-23 23:13:39 +08:00
CJKmkp 710a9014dd improve:issue #112 2025-08-23 21:39:00 +08:00
CJKmkp 108c6b2b17 improve:使用数据保存 2025-08-23 20:24:48 +08:00
CJKmkp 70735943c3 add:长按翻页 2025-08-23 19:43:09 +08:00
CJKmkp d01a24f879 add:底部翻页控件预览 2025-08-23 19:32:13 +08:00
CJKmkp ec2d5043ff add:底部PPT翻页按钮调节 2025-08-23 19:27:30 +08:00
CJKmkp 15082c2c52 fix:墨迹出现在不对应的PPT文档上 2025-08-23 19:16:19 +08:00
CJKmkp 44278d68b4 fix:翻页控件不合理的显示时机 2025-08-23 19:10:53 +08:00
CJKmkp 40eeb9db66 fix:退出放映模式后浮动栏异常问题 2025-08-23 19:04:46 +08:00
CJKmkp a5eb1dfca7 fix:开启部分功能后手势面板显示异常 2025-08-23 18:58:57 +08:00
CJKmkp 8719677f11 fix:开启部分功能后手势面板显示异常 2025-08-23 18:55:36 +08:00
CJKmkp 8f01b6c5fe fix:切换时荧光笔状态异常 2025-08-23 18:51:16 +08:00
CJKmkp 9e63a3f49b fix:快捷调色板不支持荧光笔 2025-08-23 18:43:47 +08:00
CJKmkp 84edb7bbe6 fix:快捷调色板不支持荧光笔 2025-08-23 18:39:12 +08:00
CJKmkp c86ce00a17 fix:快捷调色板不支持荧光笔 2025-08-23 18:29:24 +08:00
CJKmkp aba6c18a25 fix:issue #133 2025-08-23 18:24:24 +08:00
2,2,3-三甲基戊烷 62c81e6d44 Merge pull request #134 from InkCanvasForClass/all-contributors/add-PrefacedCorg 2025-08-23 10:44:32 +08:00
allcontributors[bot] 68ea323855 docs: update .all-contributorsrc 2025-08-23 02:44:15 +00:00
allcontributors[bot] f94d81ad20 docs: update README.md 2025-08-23 02:44:14 +00:00
CJK_mkp 063f8b1751 优化日志 2025-08-19 18:04:45 +08:00
CJK_mkp 35cfd26ece fix:退出不完全时更新 2025-08-19 17:52:02 +08:00
CJK_mkp f331cb1b4d fix:issue #110 2025-08-19 17:41:42 +08:00
CJK_mkp 502e205071 fix:关机时不保存使用数据 2025-08-18 18:01:23 +08:00
CJK_mkp 975b563b8d fix:关机时不保存使用数据 2025-08-18 17:57:56 +08:00
CJK_mkp cc88630859 fix:关机时不保存使用数据 2025-08-18 17:54:41 +08:00
CJK_mkp cce0b930ec fix:关机时不保存使用数据 2025-08-18 17:49:33 +08:00
CJK_mkp 723c825df2 fix:关机时不保存使用数据 2025-08-18 17:42:36 +08:00
CJK_mkp 5b4c966354 优化日志 2025-08-13 22:03:18 +08:00
CJK_mkp 69023f98d9 优化日志 2025-08-13 22:00:53 +08:00
CJK_mkp dce5722e96 优化日志 2025-08-13 21:59:35 +08:00
CJK_mkp cbdbc93274 Merge pull request #127 from InkCanvasForClass/beta
ICC CE 1.7.7.5
2025-08-13 14:29:53 +08:00
CJKmkp 042b153684 improve:窗口置顶 2025-08-13 12:52:56 +08:00
CJKmkp 4054423721 更新版本号 2025-08-13 12:37:27 +08:00
CJKmkp 2e63a6eaca improve:面积擦子面板显示位置 2025-08-13 12:31:58 +08:00
CJKmkp 9eca7eb2ee fix:插入图片面板点击空白不折叠 2025-08-13 12:16:55 +08:00
CJKmkp 1489fb645e add:橡皮子面板中的清空按钮,更符合操作逻辑 2025-08-13 12:11:44 +08:00
CJKmkp 5c0ca841d7 fix:调色盘导致的按钮高光显示错位 2025-08-13 11:54:36 +08:00
CJKmkp a4d3d3ff9c 删除无用文件 2025-08-13 10:28:54 +08:00
CJKmkp c3b67f4a4b 更新版本号 2025-08-12 15:51:26 +08:00
CJKmkp cde5daf19a add:issue #126 2025-08-12 15:45:33 +08:00
CJKmkp 904b2c0988 add:issue #124 2025-08-12 15:33:19 +08:00
CJKmkp f9907e2ec6 improve:按钮显示控制 2025-08-12 12:50:29 +08:00
CJKmkp 7329c0097c improve:快捷调色盘 2025-08-12 12:30:48 +08:00
CJKmkp 0478949305 improve:快捷调色盘 2025-08-12 12:19:55 +08:00
CJKmkp 34172a54fe 更新版本号 2025-08-12 11:14:24 +08:00
CJKmkp 6dd629eda5 improve:快捷调色盘 2025-08-12 11:13:35 +08:00
CJKmkp 636dd2b8d5 add:快捷调色盘 2025-08-12 11:08:24 +08:00
CJKmkp b20dbc5202 更新版本号 2025-08-11 22:48:25 +08:00
CJKmkp ee45104eb9 improve:快捷调色盘 2025-08-11 22:47:43 +08:00
CJKmkp 152be89860 add:按钮显示控制 2025-08-11 22:42:36 +08:00
CJKmkp 6b5a375542 更新版本号 2025-08-11 21:49:29 +08:00
CJKmkp 03d049846d 更新版本号 2025-08-11 20:32:12 +08:00
CJKmkp 6ed084bb94 add:快捷调色盘 2025-08-11 20:28:33 +08:00
CJKmkp f5332b63a9 add:快捷调色盘 2025-08-11 20:23:14 +08:00
CJKmkp 87356215c3 add:快捷调色盘 2025-08-11 20:19:01 +08:00
CJKmkp 07c7acc37a add:按钮显示控制 2025-08-11 19:18:21 +08:00
CJKmkp 4a6d9dee67 add:按钮显示控制 2025-08-11 19:08:05 +08:00
CJKmkp 52eb93e59c 更新版本号 2025-08-11 11:38:35 +08:00
CJKmkp 2245a018e6 fix:触摸线擦 2025-08-11 09:13:12 +08:00
CJKmkp 8b327fd715 fix:几何触摸绘制 2025-08-10 14:08:07 +08:00
CJKmkp 7a7289a4c8 更新版本号 2025-08-10 12:21:24 +08:00
CJKmkp ceb259819f fix:几何触摸绘制 2025-08-10 12:20:51 +08:00
CJKmkp fdfbaedbd7 fix:几何触摸绘制 2025-08-10 11:58:58 +08:00
PrefacedCorg 9591fbf146 我也不知道我改了啥 2025-08-09 13:16:22 +08:00
CJKmkp b2a09dbf6d fix:几何墨迹问题 2025-08-04 20:25:42 +08:00
PrefacedCorg 11a5a7fdbe 代码清理 2025-08-03 16:46:33 +08:00
PrefacedCorg 745b798d89 Update MainWindow.xaml 2025-08-03 15:23:46 +08:00
PrefacedCorg 7bac32e3c4 Update MainWindow.xaml 2025-08-01 02:56:53 +08:00
PrefacedCorg 880ca08571 fix time 2025-08-01 02:56:24 +08:00
PrefacedCorg 85d4d8a71e 修复二级菜单移位
修复二级菜单移位(和优化江陵写的屎山)
2025-07-31 20:38:30 +08:00
CJKmkp abb8ed0bcc fix:插入图片 2025-07-31 16:49:53 +08:00
CJKmkp bea0d10a6c Merge branch 'beta' of https://github.com/InkCanvasForClass/community into beta 2025-07-31 16:36:42 +08:00
CJK_mkp 80d943af23 Merge pull request #123 from InkCanvasForClass/beta-PrefacedCorg
合并分支
2025-07-31 15:22:18 +08:00
PrefacedCorg 90b85e469d Update MainWindow.xaml 2025-07-31 15:07:39 +08:00
PrefacedCorg 0d419c1323 fix 二级菜单移位 2025-07-31 14:52:44 +08:00
PrefacedCorg f7a2ecc2e0 给二级菜单加了个x :( 2025-07-31 14:12:49 +08:00
PrefacedCorg 5fdbf83f32 新增图片选择选项 2025-07-31 13:23:33 +08:00
CJKmkp c513ad9291 improve:插入图片 2025-07-31 10:55:01 +08:00
CJKmkp a2dfa55dbd improve:插入图片 2025-07-31 10:27:19 +08:00
CJKmkp 78f8d9a271 fix:退出确认按钮 2025-07-31 10:03:29 +08:00
CJK_mkp b25a5aac8c Merge pull request #122 from MKStoler1024/beta
fix: gitignore
2025-07-31 09:46:24 +08:00
MKStoler1024 25cc70035e fix: gitignore 2025-07-31 09:39:46 +08:00
MKStoler1024 e5d70d5a0e del: unneeded files 2025-07-31 09:38:07 +08:00
MKStoler1024 066b0d1f8a fix: gitignore 2025-07-31 09:35:00 +08:00
CJKmkp 71b2e56187 improve:截图功能 2025-07-30 20:06:43 +08:00
CJKmkp 457832fbbe improve:截图功能 2025-07-30 20:06:29 +08:00
CJKmkp 00554e066b add:截图功能 2025-07-30 19:56:22 +08:00
CJKmkp 9f9775e585 fix:图片插入 2025-07-30 14:18:45 +08:00
CJKmkp 9cf70ae74e fix:图片插入 2025-07-30 14:12:10 +08:00
CJKmkp 4f046987c6 fix:线擦切换至批注的面板弹出及高光显示问题 2025-07-30 13:24:52 +08:00
CJKmkp 699666c23b fix:issue #120 2025-07-29 23:14:20 +08:00
CJKmkp 6a8cdd0155 fix:issue #118 2025-07-29 19:07:59 +08:00
CJKmkp ce61f0e2b7 improve:IACore 2025-07-29 19:00:45 +08:00
CJKmkp b3cb0bf93f improve:IACore 2025-07-29 19:00:32 +08:00
CJKmkp 07328eea2d Revert "delete:IACore释放"
This reverts commit bfb6346812.
2025-07-29 18:37:00 +08:00
CJK_mkp 5f7a6f2e07 Merge pull request #119 from InkCanvasForClass/beta
ICC CE 1.7.6.0
2025-07-29 18:19:26 +08:00
CJKmkp 39d54cc493 更新版本号 2025-07-29 18:06:59 +08:00
CJKmkp d31f40408b fix:图片墨迹保存问题 2025-07-29 17:59:41 +08:00
CJKmkp 7f88d9ae27 fix:图片墨迹保存问题 2025-07-29 17:44:15 +08:00
CJKmkp 1c1dd81474 fix:图片墨迹保存问题 2025-07-29 17:38:58 +08:00
CJKmkp 53a498c581 fix:IACore 2025-07-29 16:28:22 +08:00
CJKmkp bfb6346812 delete:IACore释放 2025-07-29 16:08:24 +08:00
CJKmkp bd2fa1e427 improve:PPT模块 2025-07-29 09:43:18 +08:00
CJKmkp 6c4bfeff29 improve:PPT模块 2025-07-29 09:35:47 +08:00
CJKmkp 299a77eea8 improve:PPT模块 2025-07-29 09:19:27 +08:00
CJKmkp 351a00fb1e 更新版本号 2025-07-29 09:08:39 +08:00
CJKmkp 2140e1ebe1 improve:墨迹平滑 2025-07-29 09:02:03 +08:00
CJKmkp 497a820ba2 improve:PPT模块 2025-07-29 08:44:17 +08:00
CJKmkp 7182f3554a Revert "fix:issue #110"
This reverts commit f8e4732dcd.
2025-07-29 02:33:19 +08:00
CJKmkp 506ba52502 improve:直线拉直 2025-07-29 01:40:35 +08:00
CJKmkp 90c1630af4 improve:PPT模块 2025-07-29 01:34:51 +08:00
CJKmkp 5934abd448 fix:进退白板墨迹消失 2025-07-29 01:31:21 +08:00
CJKmkp 86f432ef01 improve:PPT模块 2025-07-29 01:24:42 +08:00
CJKmkp 4913019c5c 优化PPT模块 2025-07-29 01:15:32 +08:00
CJKmkp a6316797e6 更新软件信息 2025-07-29 00:01:47 +08:00
CJKmkp 00d7549bde 更新互斥锁 2025-07-28 23:57:50 +08:00
CJKmkp 8fc33f5649 更新软件名称 2025-07-28 23:51:14 +08:00
CJKmkp 729b544675 improve:PPT模块 2025-07-28 22:36:02 +08:00
184 changed files with 37605 additions and 10502 deletions
+11 -10
View File
@@ -19,15 +19,6 @@
"code"
]
},
{
"login": "Hydro11451",
"name": "Hydrogen",
"avatar_url": "https://avatars.githubusercontent.com/u/214308559?v=4",
"profile": "http://hydro11451.qzz.io",
"contributions": [
"code"
]
},
{
"login": "CreeperAWA",
"name": "CreeperAWA",
@@ -86,7 +77,17 @@
"avatar_url": "https://avatars.githubusercontent.com/u/129855423?v=4",
"profile": "https://github.com/PrefacedCorg",
"contributions": [
"code"
"code",
"design"
]
},
{
"login": "Jursin",
"name": "Jursin",
"avatar_url": "https://avatars.githubusercontent.com/u/127487914?v=4",
"profile": "http://blog.jursin.top",
"contributions": [
"design"
]
}
]
+2 -2
View File
@@ -39,14 +39,14 @@ body:
2.
3.
validations:
required: false
required: true
- type: textarea
id: expected
attributes:
label: 期望结果 | Expected Behavior
description: 你期望的正确行为或结果 | What did you expect to happen?
validations:
required: false
required: true
- type: textarea
id: extra
attributes:
+1 -1
View File
@@ -20,7 +20,7 @@ body:
label: 需求动机 | Motivation
description: 为什么需要这个功能?| Why do you need this feature?
validations:
required: false
required: true
- type: textarea
id: design
attributes:
+2 -1
View File
@@ -1,3 +1,4 @@
obj/
bin/
.vs
.vs/
/Ink Canvas/obj
+1
View File
@@ -2,5 +2,6 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../alpha" vcs="Git" />
</component>
</project>
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,398 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\ink canvas\\mainwindow_cs\\mw_floatingbaricons.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow_cs\\mw_floatingbaricons.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\privacy.txt||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:privacy.txt||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Manual.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Manual.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\LICENSE||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:LICENSE||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas.sln.DotSettings.user||{FA3CD31E-987B-443A-9B81-186104E8DAC1}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Ink Canvas.sln.DotSettings.user||{FA3CD31E-987B-443A-9B81-186104E8DAC1}"
},
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\ink canvas\\mainwindow_cs\\mw_ppt.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow_cs\\mw_ppt.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\ink canvas\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 210,
"SelectedChildIndex": 48,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{e506b91c-c606-466a-90a9-123d1d1e12b3}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{387cb18d-6153-4156-9257-9ac3f9207bbe}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{e8b06f52-6d01-11d2-aa7d-00c04f990343}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{46c87f81-5a06-43a8-9e25-85d33bac49f8}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{269a02dc-6af8-11d3-bdc4-00c04f688e50}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{99b8fa2f-ab90-4f57-9c32-949f146f1914}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{c79b74ff-f1d7-4c94-aefa-4d22bfe1b1f9}"
},
{
"$type": "Bookmark",
"Name": "ST:128:0:{13b12e3e-c1b4-4539-9371-4fe9a0d523fc}"
},
{
"$type": "Bookmark",
"Name": "ST:2:0:{34c7837f-3b3a-449c-bdf0-bdad86cbaf4a}"
},
{
"$type": "Bookmark",
"Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{37aba9be-445a-11d3-9949-00c04f68fd0a}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{dcc4ea97-1c0c-482b-b205-e541c0df9728}"
},
{
"$type": "Bookmark",
"Name": "ST:128:0:{75188d03-9892-4ae2-abf1-207126247ce5}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
},
{
"$type": "Bookmark",
"Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:129:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{eefa5220-e298-11d0-8f78-00a0c9110057}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{4a9b7e51-aa16-11d0-a8c5-00a0c921a4d2}"
},
{
"$type": "Bookmark",
"Name": "ST:132:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Bookmark",
"Name": "ST:133:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:3:0:{34c7837f-3b3a-449c-bdf0-bdad86cbaf4a}"
},
{
"$type": "Bookmark",
"Name": "ST:153:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:154:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:152:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:151:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:150:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:148:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:149:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:136:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:144:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:147:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:145:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:146:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:143:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:142:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:141:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:140:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:139:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:138:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:137:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Bookmark",
"Name": "ST:135:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:134:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:129:0:{13b12e3e-c1b4-4539-9371-4fe9a0d523fc}"
},
{
"$type": "Bookmark",
"Name": "ST:130:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Bookmark",
"Name": "ST:129:0:{75188d03-9892-4ae2-abf1-207126247ce5}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{cce594b6-0c39-4442-ba28-10c64ac7e89f}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{b1e99781-ab81-11d0-b683-00aa00a3ee26}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "MW_FloatingBarIcons.cs",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"ViewState": "AgIAAOgCAAAAAAAAAAAuwEoGAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-31T10:49:24.719Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "README.md",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\README.md",
"RelativeDocumentMoniker": "README.md",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\README.md",
"RelativeToolTip": "README.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-31T10:48:22.883Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "MainWindow.xaml",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow.xaml",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow.xaml",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow.xaml",
"RelativeToolTip": "Ink Canvas\\MainWindow.xaml",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|",
"WhenOpened": "2025-05-24T13:22:56.715Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "Microsoft.Common.CurrentVersion.targets",
"DocumentMoniker": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"RelativeDocumentMoniker": "..\\..\\..\\..\\..\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"ToolTip": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"RelativeToolTip": "..\\..\\..\\..\\..\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"ViewState": "AgIAAGsJAAAAAAAAAAAQwIEJAAAEAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003801|",
"WhenOpened": "2025-05-24T13:06:01.053Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "MW_PPT.cs",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"ViewState": "AgIAAFgAAAAAAAAAAAAUwHQAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-24T13:04:47.205Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "README.md",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"RelativeDocumentMoniker": "..\\..\\..\\..\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"RelativeToolTip": "..\\..\\..\\..\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-24T13:04:01.407Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "privacy.txt",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\privacy.txt",
"RelativeDocumentMoniker": "privacy.txt",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\privacy.txt",
"RelativeToolTip": "privacy.txt",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
"WhenOpened": "2025-05-24T13:04:01.337Z"
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Manual.md",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Manual.md",
"RelativeDocumentMoniker": "Manual.md",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Manual.md",
"RelativeToolTip": "Manual.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-24T13:04:00.986Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "LICENSE",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\LICENSE",
"RelativeDocumentMoniker": "LICENSE",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\LICENSE",
"RelativeToolTip": "LICENSE",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
"WhenOpened": "2025-05-24T13:04:00.902Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "Ink Canvas.sln.DotSettings.user",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas.sln.DotSettings.user",
"RelativeDocumentMoniker": "Ink Canvas.sln.DotSettings.user",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas.sln.DotSettings.user",
"RelativeToolTip": "Ink Canvas.sln.DotSettings.user",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003464|",
"WhenOpened": "2025-05-24T13:04:00.792Z"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{d84ee353-0bef-5a41-a649-8f89aca5d84d}"
}
]
},
{
"DockedWidth": 204,
"SelectedChildIndex": -1,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:1:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{590a070c-4fcd-52d9-87da-dfaa11710261}"
}
]
},
{
"DockedWidth": 190,
"SelectedChildIndex": -1,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{f4fc5ff1-28ef-4b04-ad02-6b298310ccc7}"
}
]
}
]
}
]
}
-398
View File
@@ -1,398 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\ink canvas\\mainwindow_cs\\mw_floatingbaricons.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow_cs\\mw_floatingbaricons.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\privacy.txt||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:privacy.txt||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Manual.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Manual.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\LICENSE||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:LICENSE||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas.sln.DotSettings.user||{FA3CD31E-987B-443A-9B81-186104E8DAC1}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Ink Canvas.sln.DotSettings.user||{FA3CD31E-987B-443A-9B81-186104E8DAC1}"
},
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\ink canvas\\mainwindow_cs\\mw_ppt.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow_cs\\mw_ppt.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|c:\\users\\dubi906w\\source\\repos\\icc-ce\\ink canvas\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}",
"RelativeMoniker": "D:0:0:{8D0EDFC7-F974-4571-BC49-6F3A6653FE81}|Ink Canvas\\InkCanvasForClass.csproj|solutionrelative:ink canvas\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 210,
"SelectedChildIndex": 48,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{e506b91c-c606-466a-90a9-123d1d1e12b3}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{387cb18d-6153-4156-9257-9ac3f9207bbe}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{e8b06f52-6d01-11d2-aa7d-00c04f990343}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{46c87f81-5a06-43a8-9e25-85d33bac49f8}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{269a02dc-6af8-11d3-bdc4-00c04f688e50}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{99b8fa2f-ab90-4f57-9c32-949f146f1914}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{c79b74ff-f1d7-4c94-aefa-4d22bfe1b1f9}"
},
{
"$type": "Bookmark",
"Name": "ST:128:0:{13b12e3e-c1b4-4539-9371-4fe9a0d523fc}"
},
{
"$type": "Bookmark",
"Name": "ST:2:0:{34c7837f-3b3a-449c-bdf0-bdad86cbaf4a}"
},
{
"$type": "Bookmark",
"Name": "ST:128:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{37aba9be-445a-11d3-9949-00c04f68fd0a}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{dcc4ea97-1c0c-482b-b205-e541c0df9728}"
},
{
"$type": "Bookmark",
"Name": "ST:128:0:{75188d03-9892-4ae2-abf1-207126247ce5}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
},
{
"$type": "Bookmark",
"Name": "ST:128:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:129:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{eefa5220-e298-11d0-8f78-00a0c9110057}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{4a9b7e51-aa16-11d0-a8c5-00a0c921a4d2}"
},
{
"$type": "Bookmark",
"Name": "ST:132:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Bookmark",
"Name": "ST:133:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:3:0:{34c7837f-3b3a-449c-bdf0-bdad86cbaf4a}"
},
{
"$type": "Bookmark",
"Name": "ST:153:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:154:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:152:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:151:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:150:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:148:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:149:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:136:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:144:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:147:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:145:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:146:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:143:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:142:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:141:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:140:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:139:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:138:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:137:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Bookmark",
"Name": "ST:135:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:134:0:{116d2292-e37d-41cd-a077-ebacac4c8cc4}"
},
{
"$type": "Bookmark",
"Name": "ST:129:0:{13b12e3e-c1b4-4539-9371-4fe9a0d523fc}"
},
{
"$type": "Bookmark",
"Name": "ST:130:0:{1fc202d4-d401-403c-9834-5b218574bb67}"
},
{
"$type": "Bookmark",
"Name": "ST:129:0:{75188d03-9892-4ae2-abf1-207126247ce5}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{cce594b6-0c39-4442-ba28-10c64ac7e89f}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{b1e99781-ab81-11d0-b683-00aa00a3ee26}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "MW_FloatingBarIcons.cs",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_FloatingBarIcons.cs",
"ViewState": "AgIAAOgCAAAAAAAAAAAuwEoGAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-31T10:49:24.719Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "README.md",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\README.md",
"RelativeDocumentMoniker": "README.md",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\README.md",
"RelativeToolTip": "README.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-31T10:48:22.883Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "MainWindow.xaml",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow.xaml",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow.xaml",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow.xaml",
"RelativeToolTip": "Ink Canvas\\MainWindow.xaml",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|",
"WhenOpened": "2025-05-24T13:22:56.715Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "Microsoft.Common.CurrentVersion.targets",
"DocumentMoniker": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"RelativeDocumentMoniker": "..\\..\\..\\..\\..\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"ToolTip": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"RelativeToolTip": "..\\..\\..\\..\\..\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\amd64\\Microsoft.Common.CurrentVersion.targets",
"ViewState": "AgIAAGsJAAAAAAAAAAAQwIEJAAAEAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003801|",
"WhenOpened": "2025-05-24T13:06:01.053Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "MW_PPT.cs",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"RelativeDocumentMoniker": "Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"RelativeToolTip": "Ink Canvas\\MainWindow_cs\\MW_PPT.cs",
"ViewState": "AgIAAFgAAAAAAAAAAAAUwHQAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-24T13:04:47.205Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "README.md",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"RelativeDocumentMoniker": "..\\..\\..\\..\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"RelativeToolTip": "..\\..\\..\\..\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\README.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-24T13:04:01.407Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "privacy.txt",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\privacy.txt",
"RelativeDocumentMoniker": "privacy.txt",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\privacy.txt",
"RelativeToolTip": "privacy.txt",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
"WhenOpened": "2025-05-24T13:04:01.337Z"
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Manual.md",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Manual.md",
"RelativeDocumentMoniker": "Manual.md",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Manual.md",
"RelativeToolTip": "Manual.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-05-24T13:04:00.986Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "LICENSE",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\LICENSE",
"RelativeDocumentMoniker": "LICENSE",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\LICENSE",
"RelativeToolTip": "LICENSE",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
"WhenOpened": "2025-05-24T13:04:00.902Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "Ink Canvas.sln.DotSettings.user",
"DocumentMoniker": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas.sln.DotSettings.user",
"RelativeDocumentMoniker": "Ink Canvas.sln.DotSettings.user",
"ToolTip": "C:\\Users\\dubi906w\\source\\repos\\icc-ce\\Ink Canvas.sln.DotSettings.user",
"RelativeToolTip": "Ink Canvas.sln.DotSettings.user",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003464|",
"WhenOpened": "2025-05-24T13:04:00.792Z"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{d84ee353-0bef-5a41-a649-8f89aca5d84d}"
}
]
},
{
"DockedWidth": 204,
"SelectedChildIndex": -1,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:1:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}"
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{590a070c-4fcd-52d9-87da-dfaa11710261}"
}
]
},
{
"DockedWidth": 190,
"SelectedChildIndex": -1,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{f4fc5ff1-28ef-4b04-ad02-6b298310ccc7}"
}
]
}
]
}
]
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
-7
View File
@@ -1,7 +0,0 @@
{
"ExpandedNodes": [
""
],
"SelectedNode": "\\Ink Canvas.sln",
"PreviewInSolutionExplorer": false
}
Binary file not shown.
@@ -1,290 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config||{FA3CD31E-987B-443A-9B81-186104E8DAC1}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "System.ValueTuple.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\System.ValueTuple.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\System.ValueTuple.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:48.138Z"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "Settings.json",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Settings.json",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Settings.json",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
"WhenOpened": "2025-05-24T13:02:44.878Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "OSVersionExt.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\OSVersionExt.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\OSVersionExt.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.837Z"
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Office.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Office.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Office.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.774Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "NHotkey.Wpf.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\NHotkey.Wpf.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\NHotkey.Wpf.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.718Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "NHotkey.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\NHotkey.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\NHotkey.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.662Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "Newtonsoft.Json.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Newtonsoft.Json.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Newtonsoft.Json.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.589Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "Microsoft.Office.Interop.PowerPoint.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.932Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "MdXaml.Plugins.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\MdXaml.Plugins.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\MdXaml.Plugins.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.838Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "MdXaml.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\MdXaml.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\MdXaml.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.776Z"
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "iNKORE.UI.WPF.Modern.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.573Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "iNKORE.UI.WPF.Modern.Controls.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.432Z"
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "iNKORE.UI.WPF.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:42.807Z"
},
{
"$type": "Document",
"DocumentIndex": 13,
"Title": "InkCanvasForClass.exe.config",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000601|",
"WhenOpened": "2025-05-24T13:02:27.288Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 14,
"Title": "ICSharpCode.AvalonEdit.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.847Z"
},
{
"$type": "Document",
"DocumentIndex": 15,
"Title": "IAWinFX.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IAWinFX.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IAWinFX.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.816Z"
},
{
"$type": "Document",
"DocumentIndex": 16,
"Title": "IALoader.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IALoader.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IALoader.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.784Z"
},
{
"$type": "Document",
"DocumentIndex": 17,
"Title": "IACore.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IACore.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IACore.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.753Z"
},
{
"$type": "Document",
"DocumentIndex": 18,
"Title": "Hardcodet.NotifyIcon.Wpf.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.113Z"
}
]
}
]
}
]
}
-338
View File
@@ -1,338 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config||{FA3CD31E-987B-443A-9B81-186104E8DAC1}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll||{177559E0-D141-11D0-92DF-00A0C9138C45}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "icc.png - PNG [256x256, 32 \u4F4D, PNG]",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\icc.png",
"RelativeDocumentMoniker": "icc.png",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\icc.png - PNG [256x256, 32 \u4F4D, PNG]",
"RelativeToolTip": "icc.png - PNG [256x256, 32 \u4F4D, PNG]",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2025-05-24T13:03:45.63Z",
"EditorCaption": " - PNG [256x256, 32 \u4F4D, PNG]"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "AutomaticUpdateVersionControl.txt",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\AutomaticUpdateVersionControl.txt",
"RelativeDocumentMoniker": "AutomaticUpdateVersionControl.txt",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\AutomaticUpdateVersionControl.txt",
"RelativeToolTip": "AutomaticUpdateVersionControl.txt",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
"WhenOpened": "2025-05-24T13:03:45.517Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": ".gitignore",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\.gitignore",
"RelativeDocumentMoniker": ".gitignore",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.2\\.gitignore",
"RelativeToolTip": ".gitignore",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
"WhenOpened": "2025-05-24T13:03:43.13Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "System.ValueTuple.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\System.ValueTuple.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\System.ValueTuple.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\System.ValueTuple.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:48.138Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "Settings.json",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Settings.json",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Settings.json",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Settings.json",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
"WhenOpened": "2025-05-24T13:02:44.878Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "OSVersionExt.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\OSVersionExt.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\OSVersionExt.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\OSVersionExt.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.837Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "Office.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Office.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Office.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Office.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.774Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "NHotkey.Wpf.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\NHotkey.Wpf.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.Wpf.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\NHotkey.Wpf.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.718Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "NHotkey.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\NHotkey.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\NHotkey.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\NHotkey.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.662Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "Newtonsoft.Json.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Newtonsoft.Json.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Newtonsoft.Json.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Newtonsoft.Json.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:44.589Z"
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "Microsoft.Office.Interop.PowerPoint.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Microsoft.Office.Interop.PowerPoint.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.932Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "MdXaml.Plugins.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\MdXaml.Plugins.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.Plugins.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\MdXaml.Plugins.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.838Z"
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "MdXaml.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\MdXaml.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\MdXaml.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\MdXaml.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.776Z"
},
{
"$type": "Document",
"DocumentIndex": 13,
"Title": "iNKORE.UI.WPF.Modern.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.573Z"
},
{
"$type": "Document",
"DocumentIndex": 14,
"Title": "iNKORE.UI.WPF.Modern.Controls.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.Modern.Controls.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:43.432Z"
},
{
"$type": "Document",
"DocumentIndex": 15,
"Title": "iNKORE.UI.WPF.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\iNKORE.UI.WPF.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:42.807Z"
},
{
"$type": "Document",
"DocumentIndex": 16,
"Title": "InkCanvasForClass.exe.config",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\InkCanvasForClass.exe.config",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000601|",
"WhenOpened": "2025-05-24T13:02:27.288Z"
},
{
"$type": "Document",
"DocumentIndex": 17,
"Title": "ICSharpCode.AvalonEdit.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\ICSharpCode.AvalonEdit.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.847Z"
},
{
"$type": "Document",
"DocumentIndex": 18,
"Title": "IAWinFX.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IAWinFX.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IAWinFX.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IAWinFX.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.816Z"
},
{
"$type": "Document",
"DocumentIndex": 19,
"Title": "IALoader.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IALoader.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IALoader.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IALoader.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.784Z"
},
{
"$type": "Document",
"DocumentIndex": 20,
"Title": "IACore.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\IACore.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\IACore.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\IACore.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.753Z"
},
{
"$type": "Document",
"DocumentIndex": 21,
"Title": "Hardcodet.NotifyIcon.Wpf.dll",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"RelativeDocumentMoniker": "..\\..\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"RelativeToolTip": "..\\..\\InkCanvasForClass\\Hardcodet.NotifyIcon.Wpf.dll",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001697|",
"WhenOpened": "2025-05-24T13:02:22.113Z"
}
]
}
]
}
]
}
Binary file not shown.
Binary file not shown.
@@ -1,67 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "icc.png",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png",
"RelativeDocumentMoniker": "icc.png",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png",
"RelativeToolTip": "icc.png",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2025-05-24T13:12:49.619Z"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "AutomaticUpdateVersionControl.txt",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt",
"RelativeDocumentMoniker": "AutomaticUpdateVersionControl.txt",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt",
"RelativeToolTip": "AutomaticUpdateVersionControl.txt",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
"WhenOpened": "2025-05-24T13:12:49.575Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": ".gitignore",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore",
"RelativeDocumentMoniker": ".gitignore",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore",
"RelativeToolTip": ".gitignore",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
"WhenOpened": "2025-05-24T13:12:49.025Z"
}
]
}
]
}
]
}
-72
View File
@@ -1,72 +0,0 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:icc.png||{177559E0-D141-11D0-92DF-00A0C9138C45}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:AutomaticUpdateVersionControl.txt||{8B382828-6202-11D1-8870-0000F87579D2}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}",
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:.gitignore||{3B902123-F8A7-4915-9F01-361F908088D0}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 1,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "icc.png - PNG [1328x1328, 32 \u4F4D, PNG]",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png",
"RelativeDocumentMoniker": "icc.png",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\icc.png - PNG [1328x1328, 32 \u4F4D, PNG]",
"RelativeToolTip": "icc.png - PNG [1328x1328, 32 \u4F4D, PNG]",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2025-05-24T13:12:49.619Z",
"EditorCaption": " - PNG [1328x1328, 32 \u4F4D, PNG]"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "AutomaticUpdateVersionControl.txt",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt",
"RelativeDocumentMoniker": "AutomaticUpdateVersionControl.txt",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\AutomaticUpdateVersionControl.txt",
"RelativeToolTip": "AutomaticUpdateVersionControl.txt",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|",
"WhenOpened": "2025-05-24T13:12:49.575Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": ".gitignore",
"DocumentMoniker": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore",
"RelativeDocumentMoniker": ".gitignore",
"ToolTip": "C:\\Users\\Administrator\\Desktop\\ICC CE\\icc-0610.2.3\\.gitignore",
"RelativeToolTip": ".gitignore",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|",
"WhenOpened": "2025-05-24T13:12:49.025Z"
}
]
}
]
}
]
}
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
1.7.5.0
1.7.11.0
+3 -1
View File
@@ -1,2 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/PencilsConfiguration/ActualSeverity/@EntryValue">WARNING</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/CodeInspection/PencilsConfiguration/ActualSeverity/@EntryValue">WARNING</s:String>
<s:String x:Key="/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue">C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\amd64\MSBuild.exe</s:String>
<s:Int64 x:Key="/Default/Environment/Hierarchy/Build/BuildTool/MsbuildVersion/@EntryValue">1114112</s:Int64></wpf:ResourceDictionary>
+3 -3
View File
@@ -4,13 +4,13 @@
xmlns:local="clr-namespace:Ink_Canvas"
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
StartupUri="MainWindow.xaml">
>
<Application.Resources>
<ResourceDictionary>
<Style TargetType="ui:ScrollViewerEx">
<EventSetter Event="PreviewMouseWheel" Handler="ScrollViewer_PreviewMouseWheel"/>
</Style>
<ContextMenu Opened="SysTrayMenu_Opened" x:Shared="false" x:Key="SysTrayMenu" Padding="6" ui:ThemeManager.RequestedTheme="Light">
<ContextMenu Opened="SysTrayMenu_Opened" Closed="SysTrayMenu_Closed" x:Shared="false" x:Key="SysTrayMenu" Padding="6" ui:ThemeManager.RequestedTheme="Light">
<MenuItem IsCheckable="True" IsChecked="False" Checked="HideICCMainWindowTrayIconMenuItem_Checked" Unchecked="HideICCMainWindowTrayIconMenuItem_UnChecked" Name="HideICCMainWindowTrayIconMenuItem">
<MenuItem.Header>
<ui:SimpleStackPanel Orientation="Horizontal" Margin="-4,0,0,0">
@@ -32,7 +32,7 @@
</MenuItem.Icon>
</MenuItem>
<Separator Margin="0,3" />
<MenuItem>
<MenuItem Name="DisableAllHotkeysMenuItem" Click="DisableAllHotkeysMenuItem_Clicked">
<MenuItem.Header>
<ui:SimpleStackPanel Orientation="Horizontal" Margin="-4,0,0,0">
<TextBlock FontSize="14" VerticalAlignment="Center" Foreground="#18181b" Text="禁用所有快捷键" />
+400 -103
View File
@@ -1,3 +1,8 @@
using Hardcodet.Wpf.TaskbarNotification;
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern.Controls;
using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -13,11 +18,6 @@ using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Threading;
using Hardcodet.Wpf.TaskbarNotification;
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern.Controls;
using Microsoft.Win32;
using Newtonsoft.Json;
using Application = System.Windows.Application;
using MessageBox = System.Windows.MessageBox;
using Timer = System.Threading.Timer;
@@ -33,7 +33,9 @@ namespace Ink_Canvas
public static string[] StartArgs;
public static string RootPath = Environment.GetEnvironmentVariable("APPDATA") + "\\Ink Canvas\\";
// 新增:标记是否通过--board参数启动
public static bool StartWithBoardMode = false;
// 新增:保存看门狗进程对象
private static Process watchdogProcess;
// 新增:标记是否为软件内主动退出
@@ -45,7 +47,7 @@ namespace Ink_Canvas
// 新增:进程ID
private static int currentProcessId = Process.GetCurrentProcess().Id;
// 新增:应用启动时间
private static DateTime appStartTime = DateTime.Now;
internal static DateTime appStartTime { get; private set; }
// 新增:最后一次错误信息
private static string lastErrorMessage = string.Empty;
// 新增:是否已初始化崩溃监听器
@@ -76,7 +78,12 @@ namespace Ink_Canvas
InitializeCrashListeners();
// 仅在崩溃后操作为静默重启时才启动看门狗
if (CrashAction == CrashActionType.SilentRestart)
// 在更新模式下不启动看门狗,避免干扰更新流程
args = Environment.GetCommandLineArgs();
bool isUpdateMode = args.Contains("--update-mode");
bool isFinalApp = args.Contains("--final-app");
if (CrashAction == CrashActionType.SilentRestart && !isUpdateMode && !isFinalApp)
{
StartWatchdogIfNeeded();
}
@@ -91,19 +98,19 @@ namespace Ink_Canvas
// 检测操作系统版本
var osVersion = Environment.OSVersion;
bool isWindows7 = osVersion.Version.Major == 6 && osVersion.Version.Minor == 1;
if (isWindows7)
{
LogHelper.WriteLogToFile("检测到Windows 7系统,配置TLS协议支持");
// 启用所有TLS版本以支持Windows 7
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
// 配置ServicePointManager以支持Windows 7
ServicePointManager.DefaultConnectionLimit = 10;
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
LogHelper.WriteLogToFile("TLS协议配置完成,已启用TLS 1.2/1.1/1.0支持");
}
else
@@ -122,7 +129,7 @@ namespace Ink_Canvas
private void InitializeCrashListeners()
{
if (crashListenersInitialized) return;
try
{
// 确保崩溃日志目录存在
@@ -130,22 +137,22 @@ namespace Ink_Canvas
{
Directory.CreateDirectory(crashLogFile);
}
// 注册非UI线程未处理异常处理程序
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
// 注册控制台Ctrl+C等终止信号处理
Console.CancelKeyPress += Console_CancelKeyPress;
// 注册系统会话结束事件(关机、注销等)
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
// 注册进程退出处理程序
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
// 尝试注册Windows关闭消息监听
SetConsoleCtrlHandler(ConsoleCtrlHandler, true);
// 如果系统支持,添加Windows Management Instrumentation监听器
try
{
@@ -156,7 +163,7 @@ namespace Ink_Canvas
{
LogHelper.WriteLogToFile($"设置WMI进程监控失败: {wmiEx.Message}", LogHelper.LogType.Warning);
}
crashListenersInitialized = true;
LogHelper.WriteLogToFile("已初始化崩溃监听器");
}
@@ -165,7 +172,7 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile($"初始化崩溃监听器失败: {ex.Message}", LogHelper.LogType.Error);
}
}
// 新增:动态加载WMI监控(避免直接引用System.Management
private void TrySetupWmiMonitoring()
{
@@ -179,7 +186,7 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile("未找到System.Management程序集,跳过WMI监控", LogHelper.LogType.Warning);
return;
}
// 使用反射创建WMI查询
var watcherType = assembly.GetType("System.Management.ManagementEventWatcher");
if (watcherType == null)
@@ -187,13 +194,13 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile("未找到ManagementEventWatcher类型,跳过WMI监控", LogHelper.LogType.Warning);
return;
}
// 构建WMI查询字符串
string queryString = $"SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.ProcessId = {currentProcessId}";
// 创建ManagementEventWatcher实例
object watcher = Activator.CreateInstance(watcherType, queryString);
// 获取EventArrived事件信息
var eventInfo = watcherType.GetEvent("EventArrived");
if (eventInfo == null)
@@ -201,16 +208,16 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile("未找到EventArrived事件,跳过WMI监控", LogHelper.LogType.Warning);
return;
}
// 创建委托并订阅事件
Type delegateType = eventInfo.EventHandlerType;
var handler = Delegate.CreateDelegate(delegateType, this, GetType().GetMethod("WmiEventHandler", BindingFlags.NonPublic | BindingFlags.Instance));
eventInfo.AddEventHandler(watcher, handler);
// 启动监听
var startMethod = watcherType.GetMethod("Start");
startMethod.Invoke(watcher, null);
LogHelper.WriteLogToFile("已成功启动WMI进程监控");
}
catch (Exception ex)
@@ -218,7 +225,7 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile($"动态加载WMI监控失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
// WMI事件处理方法(通过反射调用)
private void WmiEventHandler(object sender, EventArgs e)
{
@@ -246,13 +253,13 @@ namespace Ink_Canvas
// 新增:Windows控制台控制处理程序
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handler, bool add);
private delegate bool ConsoleCtrlDelegate(int ctrlType);
private static bool ConsoleCtrlHandler(int ctrlType)
{
string eventType = "未知控制类型";
// 使用传统switch语句替代switch表达式
switch (ctrlType)
{
@@ -275,27 +282,49 @@ namespace Ink_Canvas
eventType = $"未知控制类型({ctrlType})";
break;
}
WriteCrashLog($"接收到系统控制信号: {eventType}");
// 返回true表示已处理该事件
return false;
}
// 新增:系统会话结束事件处理
private void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
string reason = e.Reason == SessionEndReasons.Logoff ? "用户注销" : "系统关机";
WriteCrashLog($"系统会话即将结束: {reason}");
// 清理PowerPoint进程守护
try
{
// 获取主窗口实例并清理PowerPoint进程守护
var mainWindow = Current.MainWindow as MainWindow;
if (mainWindow != null)
{
// 通过反射调用StopPowerPointProcessMonitoring方法
var method = mainWindow.GetType().GetMethod("StopPowerPointProcessMonitoring",
BindingFlags.NonPublic | BindingFlags.Instance);
method?.Invoke(mainWindow, null);
WriteCrashLog("PowerPoint进程守护已在系统关机时清理");
}
}
catch (Exception ex)
{
WriteCrashLog($"清理PowerPoint进程守护失败: {ex.Message}");
}
DeviceIdentifier.SaveUsageStatsOnShutdown();
}
// 新增:控制台取消事件处理
private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
WriteCrashLog($"接收到控制台中断信号: {e.SpecialKey}");
e.Cancel = true; // 取消默认处理
}
// 新增:处理非UI线程的未处理异常
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
@@ -304,9 +333,9 @@ namespace Ink_Canvas
var exception = e.ExceptionObject as Exception;
string errorMessage = exception?.ToString() ?? "未知异常";
lastErrorMessage = errorMessage;
WriteCrashLog($"捕获到未处理的异常: {errorMessage}");
if (e.IsTerminating)
{
WriteCrashLog("应用程序即将终止");
@@ -325,20 +354,42 @@ namespace Ink_Canvas
catch { }
}
}
// 新增:处理进程退出事件
private void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
TimeSpan runDuration = DateTime.Now - appStartTime;
WriteCrashLog($"应用程序退出,运行时长: {runDuration}");
string durationText = FormatTimeSpan(runDuration);
WriteCrashLog($"应用程序退出,运行时长: {durationText}");
// 如果有最后错误消息,记录到日志
if (!string.IsNullOrEmpty(lastErrorMessage))
{
WriteCrashLog($"最后错误信息: {lastErrorMessage}");
}
}
// 新增:格式化时间跨度
private static string FormatTimeSpan(TimeSpan timeSpan)
{
if (timeSpan.TotalDays >= 1)
{
return $"{timeSpan.Days}天 {timeSpan.Hours}小时 {timeSpan.Minutes}分钟";
}
if (timeSpan.TotalHours >= 1)
{
return $"{timeSpan.Hours}小时 {timeSpan.Minutes}分钟";
}
if (timeSpan.TotalMinutes >= 1)
{
return $"{timeSpan.Minutes}分钟 {timeSpan.Seconds}秒";
}
return $"{timeSpan.Seconds}秒";
}
// 新增:记录崩溃日志
private static void WriteCrashLog(string message)
{
@@ -349,22 +400,22 @@ namespace Ink_Canvas
{
Directory.CreateDirectory(crashLogFile);
}
string logFileName = Path.Combine(crashLogFile, $"crash_{DateTime.Now:yyyyMMdd}.log");
// 收集系统状态信息
string memoryUsage = (Process.GetCurrentProcess().WorkingSet64 / (1024 * 1024)) + " MB";
string cpuTime = Process.GetCurrentProcess().TotalProcessorTime.ToString();
string processUptime = (DateTime.Now - Process.GetCurrentProcess().StartTime).ToString();
string processUptime = FormatTimeSpan(DateTime.Now - Process.GetCurrentProcess().StartTime);
string statusInfo = $"[内存: {memoryUsage}, CPU时间: {cpuTime}, 运行时长: {processUptime}]";
// 写入日志
File.AppendAllText(
logFileName,
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [PID:{currentProcessId}] {message}\r\n{statusInfo}\r\n\r\n"
);
// 同时记录到主日志
LogHelper.WriteLogToFile(message, LogHelper.LogType.Error);
}
@@ -380,7 +431,7 @@ namespace Ink_Canvas
try
{
// 优先从 Settings.json 直接读取
var settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Settings.json");
var settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs", "Settings.json");
if (File.Exists(settingsPath))
{
var json = File.ReadAllText(settingsPath);
@@ -402,11 +453,11 @@ namespace Ink_Canvas
{
Ink_Canvas.MainWindow.ShowNewMessage("抱歉,出现未预期的异常,可能导致 InkCanvasForClass 运行不稳定。\n建议保存墨迹后重启应用。");
LogHelper.NewLog(e.Exception.ToString());
// 新增:记录到崩溃日志
lastErrorMessage = e.Exception.ToString();
WriteCrashLog($"UI线程未处理异常: {e.Exception}");
e.Handled = true;
SyncCrashActionFromSettings(); // 新增:崩溃时同步最新设置
@@ -423,7 +474,7 @@ namespace Ink_Canvas
try
{
string exePath = Process.GetCurrentProcess().MainModule.FileName;
Process.Start(exePath, "-m");
Process.Start(exePath);
}
catch { }
Environment.Exit(1);
@@ -435,14 +486,36 @@ namespace Ink_Canvas
void App_Startup(object sender, StartupEventArgs e)
{
/*if (!StoreHelper.IsStoreApp) */RootPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
// 初始化应用启动时间
appStartTime = DateTime.Now;
/*if (!StoreHelper.IsStoreApp) */
RootPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
LogHelper.NewLog(string.Format("Ink Canvas Starting (Version: {0})", Assembly.GetExecutingAssembly().GetName().Version));
// 检查是否为最终应用启动(更新后的应用)
bool isFinalApp = e.Args.Contains("--final-app");
bool skipMutexCheck = e.Args.Contains("--skip-mutex-check");
// 检查是否通过--board参数启动
bool hasBoardArg = e.Args.Contains("--board");
if (hasBoardArg)
{
StartWithBoardMode = true;
LogHelper.WriteLogToFile("App | 检测到--board参数,将直接进入白板模式");
}
// 记录最终应用启动状态
if (isFinalApp)
{
LogHelper.WriteLogToFile("App | 检测到最终应用启动(更新后的应用)");
}
// 在应用启动时自动释放IACore相关DLL
try
{
Helpers.IACoreDllExtractor.ExtractIACoreDlls();
IACoreDllExtractor.ExtractIACoreDlls();
}
catch (Exception ex)
{
@@ -455,36 +528,260 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile($"App | 使用频率: {DeviceIdentifier.GetUsageFrequency()}");
LogHelper.WriteLogToFile($"App | 更新优先级: {DeviceIdentifier.GetUpdatePriority()}");
bool ret;
mutex = new Mutex(true, "InkCanvasForClass", out ret);
// 处理更新模式启动
bool isUpdateMode = AutoUpdateHelper.HandleUpdateModeStartup(e.Args);
if (!ret && !e.Args.Contains("-m")) //-m multiple
// 如果是更新模式,不显示主窗口但保持应用运行
if (isUpdateMode)
{
LogHelper.NewLog("Detected existing instance");
MessageBox.Show("已有一个程序实例正在运行");
LogHelper.NewLog("Ink Canvas automatically closed");
IsAppExitByUser = true; // 多开时标记为用户主动退出
// 写入退出信号,确保看门狗不会重启
try {
StartupCount.Reset();
File.WriteAllText(watchdogExitSignalFile, "exit");
if (watchdogProcess != null && !watchdogProcess.HasExited)
LogHelper.WriteLogToFile("App | 检测到更新模式,跳过主窗口显示,保持应用运行");
return;
}
// 检查是否存在更新标记文件
string updateMarkerFile = Path.Combine(RootPath, "update_in_progress.tmp");
bool isUpdateInProgress = false;
// 检查是否以更新模式启动
isUpdateMode = e.Args.Contains("--update-mode");
// 如果是最终应用启动,立即清理更新标记文件
if (isFinalApp)
{
try
{
if (File.Exists(updateMarkerFile))
{
watchdogProcess.Kill();
File.Delete(updateMarkerFile);
LogHelper.WriteLogToFile("App | 最终应用启动,清理更新标记文件");
}
} catch { }
Environment.Exit(0);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"App | 清理更新标记文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
// 如果不是最终应用启动,才检查更新标记文件
if (!isFinalApp && File.Exists(updateMarkerFile))
{
try
{
string updateProcessIdStr = File.ReadAllText(updateMarkerFile).Trim();
if (int.TryParse(updateProcessIdStr, out int updateProcessId))
{
LogHelper.WriteLogToFile($"App | 检测到更新标记文件,更新进程ID: {updateProcessId}");
// 检查更新进程是否还在运行
try
{
Process updateProcess = Process.GetProcessById(updateProcessId);
if (!updateProcess.HasExited)
{
LogHelper.WriteLogToFile("App | 更新进程仍在运行,等待更新完成");
isUpdateInProgress = true;
// 等待更新进程完成
int waitCount = 0;
const int maxWaitCount = 10; // 减少等待时间到10秒
while (waitCount < maxWaitCount && !updateProcess.HasExited)
{
Thread.Sleep(500); // 减少等待间隔到500ms
waitCount++;
LogHelper.WriteLogToFile($"App | 等待更新进程完成... ({waitCount}/{maxWaitCount})");
}
if (updateProcess.HasExited)
{
LogHelper.WriteLogToFile("App | 更新进程已结束");
}
else
{
LogHelper.WriteLogToFile("App | 等待更新进程超时,强制清理", LogHelper.LogType.Warning);
// 超时后强制清理标记文件
try
{
if (File.Exists(updateMarkerFile))
{
File.Delete(updateMarkerFile);
LogHelper.WriteLogToFile("App | 强制清理更新标记文件");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"App | 强制清理更新标记文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
}
else
{
LogHelper.WriteLogToFile("App | 更新进程已结束");
}
}
catch (ArgumentException)
{
LogHelper.WriteLogToFile("App | 更新进程已不存在");
}
// 无论更新进程是否还在运行,都清理标记文件
try
{
if (File.Exists(updateMarkerFile))
{
File.Delete(updateMarkerFile);
LogHelper.WriteLogToFile("App | 清理更新标记文件");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"App | 清理更新标记文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"App | 读取更新标记文件失败: {ex.Message}", LogHelper.LogType.Warning);
// 如果读取失败,也尝试删除标记文件
try
{
if (File.Exists(updateMarkerFile))
{
File.Delete(updateMarkerFile);
LogHelper.WriteLogToFile("App | 清理损坏的更新标记文件");
}
}
catch { }
}
}
// 如果是更新过程、更新模式、最终应用或跳过Mutex检查,跳过Mutex检查
if (!isUpdateInProgress && !isUpdateMode && !isFinalApp && !skipMutexCheck)
{
bool ret;
mutex = new Mutex(true, "InkCanvasForClass CE", out ret);
if (!ret && !e.Args.Contains("-m")) //-m multiple
{
LogHelper.NewLog("Detected existing instance");
// 检查是否有.icstk文件参数
string icstkFile = FileAssociationManager.GetIcstkFileFromArgs(e.Args);
if (!string.IsNullOrEmpty(icstkFile))
{
LogHelper.WriteLogToFile($"检测到已运行实例,尝试通过IPC发送文件: {icstkFile}", LogHelper.LogType.Event);
// 尝试通过IPC发送文件路径给已运行实例
if (FileAssociationManager.TrySendFileToExistingInstance(icstkFile))
{
LogHelper.WriteLogToFile("文件路径已通过IPC发送给已运行实例", LogHelper.LogType.Event);
}
else
{
LogHelper.WriteLogToFile("通过IPC发送文件路径失败", LogHelper.LogType.Warning);
}
}
// 检查是否有--board参数
else if (hasBoardArg)
{
LogHelper.WriteLogToFile("检测到已运行实例且有--board参数,尝试通过IPC发送白板模式命令", LogHelper.LogType.Event);
// 尝试通过IPC发送白板模式命令给已运行实例
if (FileAssociationManager.TrySendBoardModeCommandToExistingInstance())
{
LogHelper.WriteLogToFile("白板模式命令已通过IPC发送给已运行实例", LogHelper.LogType.Event);
}
else
{
LogHelper.WriteLogToFile("通过IPC发送白板模式命令失败", LogHelper.LogType.Warning);
}
}
else
{
LogHelper.WriteLogToFile("检测到已运行实例,但无文件参数", LogHelper.LogType.Event);
}
LogHelper.NewLog("Ink Canvas automatically closed");
IsAppExitByUser = true; // 多开时标记为用户主动退出
// 写入退出信号,确保看门狗不会重启
try
{
StartupCount.Reset();
File.WriteAllText(watchdogExitSignalFile, "exit");
if (watchdogProcess != null && !watchdogProcess.HasExited)
{
watchdogProcess.Kill();
}
}
catch { }
Environment.Exit(0);
}
}
else
{
if (isUpdateMode)
{
LogHelper.WriteLogToFile("App | 更新模式启动,跳过重复运行检测");
}
else if (isFinalApp)
{
LogHelper.WriteLogToFile("App | 最终应用启动,跳过重复运行检测");
}
else if (skipMutexCheck)
{
LogHelper.WriteLogToFile("App | 跳过Mutex检查模式启动,跳过重复运行检测");
}
else
{
LogHelper.WriteLogToFile("App | 更新过程中,跳过重复运行检测");
}
// 在特殊模式下,创建一个临时的Mutex以避免其他检查出错
string mutexName = isFinalApp ? "InkCanvasForClass CE Final" : "InkCanvasForClass CE Update";
mutex = new Mutex(true, mutexName, out bool tempRet);
// 额外等待一小段时间确保更新进程完全退出
Thread.Sleep(1000);
LogHelper.WriteLogToFile("App | 特殊模式等待完成,继续启动");
}
_taskbar = (TaskbarIcon)FindResource("TaskbarTrayIcon");
StartArgs = e.Args;
// 在非更新模式下创建主窗口
var mainWindow = new MainWindow();
MainWindow = mainWindow;
mainWindow.Show();
// 新增:注册.icstk文件关联
try
{
LogHelper.WriteLogToFile("开始注册.icstk文件关联");
FileAssociationManager.RegisterFileAssociation();
FileAssociationManager.ShowFileAssociationStatus();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注册文件关联时出错: {ex.Message}", LogHelper.LogType.Error);
}
// 新增:启动IPC监听器
try
{
LogHelper.WriteLogToFile("启动IPC监听器");
FileAssociationManager.StartIpcListener();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动IPC监听器时出错: {ex.Message}", LogHelper.LogType.Error);
}
// 新增:Office注册表检测
try
{
LogHelper.WriteLogToFile("开始Office注册表检测");
// 检查Office安装
if (!IsOfficeInstalled())
{
@@ -513,7 +810,7 @@ namespace Ink_Canvas
{
LogHelper.WriteLogToFile($"注册表路径不存在: {regPath}", LogHelper.LogType.Warning);
// 尝试创建路径
try
try
{
using (RegistryKey createKey = Registry.CurrentUser.CreateSubKey(regPath, true))
{
@@ -532,25 +829,25 @@ namespace Ink_Canvas
}
// 备份路径更改为软件根目录下的saves/RegistryBackups文件夹
string backupPath = Path.Combine(RootPath, "saves", "RegistryBackups");
string backupPath = Path.Combine(RootPath, "Saves", "RegistryBackups");
LogHelper.WriteLogToFile($"备份路径: {backupPath}");
if (!Directory.Exists(backupPath))
if (!Directory.Exists(backupPath))
{
Directory.CreateDirectory(backupPath);
LogHelper.WriteLogToFile($"创建备份目录: {backupPath}");
}
string backupFile = Path.Combine(backupPath, $"SecurityBackup_{version}_{DateTime.Now:yyyyMMddHHmmss}.reg");
LogHelper.WriteLogToFile($"创建备份文件: {backupFile}");
// 使用UTF8编码写入注册表文件
using (StreamWriter sw = new StreamWriter(backupFile, false, Encoding.UTF8))
{
sw.WriteLine("Windows Registry Editor Version 5.00\n");
sw.WriteLine();
sw.WriteLine($"[{Registry.CurrentUser.Name}\\{regPath}]");
foreach (string valueName in baseKey.GetValueNames())
{
object value = baseKey.GetValue(valueName);
@@ -614,9 +911,9 @@ namespace Ink_Canvas
SenderScrollViewer.ScrollToVerticalOffset(SenderScrollViewer.VerticalOffset - e.Delta * 10 * SystemInformation.MouseWheelScrollLines / (double)120);
e.Handled = true;
}
catch { }
catch { }
}
catch { }
catch { }
}
// 新增:用于设置崩溃后操作类型
@@ -654,7 +951,7 @@ namespace Ink_Canvas
try
{
string exePath = Process.GetCurrentProcess().MainModule.FileName;
Process.Start(exePath, "-m");
Process.Start(exePath);
}
catch { }
Environment.Exit(1);
@@ -714,7 +1011,7 @@ namespace Ink_Canvas
Environment.Exit(1);
}
string exePath = Process.GetCurrentProcess().MainModule.FileName;
Process.Start(exePath, "-m");
Process.Start(exePath);
}
// CrashActionType.NoAction 时不重启,直接退出
}
@@ -731,7 +1028,7 @@ namespace Ink_Canvas
// 新增:记录应用退出状态
string exitType = IsAppExitByUser ? "用户主动退出" : "应用程序退出";
WriteCrashLog($"{exitType},退出代码: {e.ApplicationExitCode}");
// 记录应用退出(设备标识符)
try
{
@@ -742,7 +1039,7 @@ namespace Ink_Canvas
{
LogHelper.WriteLogToFile($"记录设备标识符退出信息失败: {deviceEx.Message}", LogHelper.LogType.Error);
}
if (IsAppExitByUser)
{
// 写入退出信号文件,通知看门狗正常退出
@@ -754,7 +1051,7 @@ namespace Ink_Canvas
}
}
}
catch (Exception ex)
catch (Exception ex)
{
// 尝试记录最后的错误
try
@@ -782,7 +1079,7 @@ namespace Ink_Canvas
return true;
}
}
// 2. 检查64位注册表中的Office
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Wow6432Node\\Microsoft\\Office"))
{
@@ -792,7 +1089,7 @@ namespace Ink_Canvas
return true;
}
}
// 3. 检查Office 365/Click-to-Run安装
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\ClickToRun"))
{
@@ -802,7 +1099,7 @@ namespace Ink_Canvas
return true;
}
}
// 4. 检查Office 365部署配置
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\15.0\\ClickToRun"))
{
@@ -812,7 +1109,7 @@ namespace Ink_Canvas
return true;
}
}
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Office\\16.0\\ClickToRun"))
{
if (key != null)
@@ -841,7 +1138,7 @@ namespace Ink_Canvas
return false;
}
}
/// <summary>
/// 显示权限不足的错误提示
/// </summary>
@@ -995,7 +1292,7 @@ namespace Ink_Canvas
{
var platformVersion = key.GetValue("Platform") as string;
var clickToRunVersion = key.GetValue("VersionToReport") as string;
if (!string.IsNullOrEmpty(platformVersion))
{
var majorVersion = platformVersion.Split('.').FirstOrDefault();
@@ -1046,7 +1343,7 @@ namespace Ink_Canvas
try
{
// 准备备份目录
string backupPath = Path.Combine(RootPath, "saves", "RegistryBackups");
string backupPath = Path.Combine(RootPath, "Saves", "RegistryBackups");
if (!Directory.Exists(backupPath))
{
Directory.CreateDirectory(backupPath);
@@ -1055,13 +1352,13 @@ namespace Ink_Canvas
// 检查Office 365 Outlook和PowerPoint的特定路径
string[] apps = { "outlook", "powerpoint" };
foreach (var app in apps)
{
// 检查用户级别的注册表
string regPath = $"Software\\Microsoft\\Office\\16.0\\{app}\\Security";
LogHelper.WriteLogToFile($"检查Office 365特定应用注册表: {regPath}");
try
{
// 先检查是否存在该路径
@@ -1072,14 +1369,14 @@ namespace Ink_Canvas
{
string backupFile = Path.Combine(backupPath, $"SecurityBackup_365_{app}_{DateTime.Now:yyyyMMddHHmmss}.reg");
LogHelper.WriteLogToFile($"创建Office 365 {app}备份文件: {backupFile}");
// 使用UTF8编码写入注册表文件
using (StreamWriter sw = new StreamWriter(backupFile, false, Encoding.UTF8))
{
sw.WriteLine("Windows Registry Editor Version 5.00\n");
sw.WriteLine();
sw.WriteLine($"[{Registry.CurrentUser.Name}\\{regPath}]");
foreach (string valueName in baseKey.GetValueNames())
{
object value = baseKey.GetValue(valueName);
@@ -1113,11 +1410,11 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile($"修改 {app} 注册表时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
// 尝试通过Office信任中心路径修改
string trustCenterPath = "Software\\Microsoft\\Office\\16.0\\Common\\Security\\FileValidation";
LogHelper.WriteLogToFile($"检查信任中心路径: {trustCenterPath}");
try
{
// 先检查是否存在该路径
@@ -1128,14 +1425,14 @@ namespace Ink_Canvas
{
string backupFile = Path.Combine(backupPath, $"SecurityBackup_365_TrustCenter_{DateTime.Now:yyyyMMddHHmmss}.reg");
LogHelper.WriteLogToFile($"创建信任中心备份文件: {backupFile}");
// 使用UTF8编码写入注册表文件
using (StreamWriter sw = new StreamWriter(backupFile, false, Encoding.UTF8))
{
sw.WriteLine("Windows Registry Editor Version 5.00\n");
sw.WriteLine();
sw.WriteLine($"[{Registry.CurrentUser.Name}\\{trustCenterPath}]");
foreach (string valueName in baseKey.GetValueNames())
{
object value = baseKey.GetValue(valueName);
@@ -1159,7 +1456,7 @@ namespace Ink_Canvas
{
LogHelper.WriteLogToFile($"修改信任中心路径时出错: {ex.Message}", LogHelper.LogType.Error);
}
// 尝试修改EnableEditWhileViewingPolicy
string policyPath = "Software\\Policies\\Microsoft\\Office\\16.0\\Common\\Security";
try
@@ -1172,14 +1469,14 @@ namespace Ink_Canvas
{
string backupFile = Path.Combine(backupPath, $"SecurityBackup_365_Policy_{DateTime.Now:yyyyMMddHHmmss}.reg");
LogHelper.WriteLogToFile($"创建策略备份文件: {backupFile}");
// 使用UTF8编码写入注册表文件
using (StreamWriter sw = new StreamWriter(backupFile, false, Encoding.UTF8))
{
sw.WriteLine("Windows Registry Editor Version 5.00\n");
sw.WriteLine();
sw.WriteLine($"[{Registry.CurrentUser.Name}\\{policyPath}]");
foreach (string valueName in baseKey.GetValueNames())
{
object value = baseKey.GetValue(valueName);
+3 -3
View File
@@ -8,7 +8,7 @@ using System.Windows;
[assembly: AssemblyTitle("InkCanvasForClass")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Dubi906w")]
[assembly: AssemblyCompany("CJK_mkp")]
[assembly: AssemblyProduct("InkCanvasForClass")]
[assembly: AssemblyCopyright("Copyright © HARKOTEK Studio 2024")]
[assembly: AssemblyTrademark("")]
@@ -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.5.0")]
[assembly: AssemblyFileVersion("1.7.5.0")]
[assembly: AssemblyVersion("1.7.11.0")]
[assembly: AssemblyFileVersion("1.7.11.0")]
@@ -0,0 +1,368 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Ink_Canvas.Helpers;
namespace Ink_Canvas
{
/// <summary>
/// 悬浮窗拦截管理器
/// </summary>
public class FloatingWindowInterceptorManager : IDisposable
{
#region
private FloatingWindowInterceptor _interceptor;
private bool _isInitialized;
private bool _disposed;
private FloatingWindowInterceptorSettings _settings;
#endregion
#region
public event EventHandler<FloatingWindowInterceptor.WindowInterceptedEventArgs> WindowIntercepted;
public event EventHandler<FloatingWindowInterceptor.WindowRestoredEventArgs> WindowRestored;
#endregion
#region
public bool IsEnabled => _interceptor != null && _settings != null && _settings.IsEnabled;
public bool IsRunning => _interceptor != null && _interceptor.IsRunning;
#endregion
#region
/// <summary>
/// 初始化拦截器
/// </summary>
public void Initialize(FloatingWindowInterceptorSettings settings)
{
if (_isInitialized) return;
try
{
_settings = settings ?? new FloatingWindowInterceptorSettings();
_interceptor = new FloatingWindowInterceptor();
// 订阅事件
_interceptor.WindowIntercepted += OnWindowIntercepted;
_interceptor.WindowRestored += OnWindowRestored;
// 应用配置
ApplySettings();
_isInitialized = true;
// 如果设置了自动启动,则启动拦截器
if (_settings.AutoStart && _settings.IsEnabled)
{
Start();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 启动拦截器
/// </summary>
public void Start()
{
if (!_isInitialized || _settings == null) return;
if (_interceptor == null) return;
try
{
_interceptor.Start(_settings.ScanIntervalMs);
LogHelper.WriteLogToFile("悬浮窗拦截器已启动", LogHelper.LogType.Event);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 停止拦截器
/// </summary>
public void Stop()
{
if (_interceptor == null) return;
try
{
_interceptor.Stop();
LogHelper.WriteLogToFile("悬浮窗拦截器已停止", LogHelper.LogType.Event);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"停止悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 设置拦截规则
/// </summary>
public void SetInterceptRule(FloatingWindowInterceptor.InterceptType type, bool enabled)
{
if (_interceptor == null || _settings == null) return;
try
{
_interceptor.SetInterceptRule(type, enabled);
// 更新设置
var ruleName = type.ToString();
if (_settings.InterceptRules.ContainsKey(ruleName))
{
_settings.InterceptRules[ruleName] = enabled;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"设置拦截规则失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 获取拦截规则
/// </summary>
public FloatingWindowInterceptor.InterceptRule GetInterceptRule(FloatingWindowInterceptor.InterceptType type)
{
return _interceptor?.GetInterceptRule(type);
}
/// <summary>
/// 获取所有拦截规则
/// </summary>
public Dictionary<FloatingWindowInterceptor.InterceptType, FloatingWindowInterceptor.InterceptRule> GetAllRules()
{
return _interceptor?.GetAllRules() ?? new Dictionary<FloatingWindowInterceptor.InterceptType, FloatingWindowInterceptor.InterceptRule>();
}
/// <summary>
/// 手动扫描一次
/// </summary>
public void ScanOnce()
{
if (_interceptor == null) return;
try
{
_interceptor.ScanOnce();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"手动扫描失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 恢复所有被拦截的窗口
/// </summary>
public void RestoreAllWindows()
{
if (_interceptor == null) return;
try
{
_interceptor.RestoreAllWindows();
LogHelper.WriteLogToFile("已恢复所有被拦截的窗口", LogHelper.LogType.Event);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"恢复窗口失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 应用设置
/// </summary>
public void ApplySettings()
{
if (_interceptor == null || _settings == null) return;
try
{
// 应用拦截规则设置
foreach (var kvp in _settings.InterceptRules)
{
if (Enum.TryParse<FloatingWindowInterceptor.InterceptType>(kvp.Key, out var type))
{
_interceptor.SetInterceptRule(type, kvp.Value);
}
}
// 如果启用了拦截器,则启动
if (_settings.IsEnabled && !IsRunning)
{
Start();
}
// 如果禁用了拦截器,则停止
else if (!_settings.IsEnabled && IsRunning)
{
Stop();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用设置失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新扫描间隔
/// </summary>
public void UpdateScanInterval(int intervalMs)
{
if (_interceptor == null || _settings == null) return;
try
{
_settings.ScanIntervalMs = intervalMs;
// 如果正在运行,重启以应用新间隔
if (IsRunning)
{
Stop();
Start();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新扫描间隔失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 获取拦截统计信息
/// </summary>
public InterceptStatistics GetStatistics()
{
if (_interceptor == null || _settings == null) return new InterceptStatistics();
try
{
var rules = GetAllRules();
var enabledRules = rules.Count(r => r.Value.IsEnabled);
var totalRules = rules.Count;
return new InterceptStatistics
{
TotalRules = totalRules,
EnabledRules = enabledRules,
IsRunning = IsRunning,
ScanIntervalMs = _settings.ScanIntervalMs
};
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取统计信息失败: {ex.Message}", LogHelper.LogType.Error);
return new InterceptStatistics();
}
}
#endregion
#region
private void OnWindowIntercepted(object sender, FloatingWindowInterceptor.WindowInterceptedEventArgs e)
{
try
{
// 记录日志
LogHelper.WriteLogToFile($"拦截窗口: {e.WindowTitle} ({e.InterceptType})", LogHelper.LogType.Event);
// 显示通知(如果启用)
if (_settings != null && _settings.ShowNotifications)
{
ShowNotification($"已拦截悬浮窗: {e.Rule.Description}");
}
// 触发事件
WindowIntercepted?.Invoke(this, e);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理窗口拦截事件失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void OnWindowRestored(object sender, FloatingWindowInterceptor.WindowRestoredEventArgs e)
{
try
{
// 记录日志
LogHelper.WriteLogToFile($"恢复窗口: {e.InterceptType}", LogHelper.LogType.Event);
// 触发事件
WindowRestored?.Invoke(this, e);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理窗口恢复事件失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void ShowNotification(string message)
{
try
{
// 这里可以集成系统通知或自定义通知
// 暂时使用调试输出
System.Diagnostics.Debug.WriteLine($"通知: {message}");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"显示通知失败: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region
public class InterceptStatistics
{
public int TotalRules { get; set; }
public int EnabledRules { get; set; }
public bool IsRunning { get; set; }
public int ScanIntervalMs { get; set; }
}
#endregion
#region IDisposable
public void Dispose()
{
if (_disposed) return;
try
{
Stop();
_interceptor?.Dispose();
_interceptor = null;
_isInitialized = false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"释放悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error);
}
finally
{
_disposed = true;
}
}
#endregion
}
}
+189 -25
View File
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Threading;
@@ -11,7 +12,7 @@ using System.Windows.Threading;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 异步硬件加速墨迹平滑处理器
/// 改进的异步硬件加速墨迹平滑处理器,使用优化的三次贝塞尔曲线拟合
/// </summary>
public class AsyncAdvancedBezierSmoothing
{
@@ -26,11 +27,13 @@ namespace Ink_Canvas.Helpers
_processingTasks = new ConcurrentDictionary<Stroke, CancellationTokenSource>();
}
public double SmoothingStrength { get; set; } = 0.3; // 大幅降低强度
public double ResampleInterval { get; set; } = 3.0; // 大幅增加间隔减少点数
public int InterpolationSteps { get; set; } = 4; // 极大减少插值步数
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; // 曲线张力参数
/// <summary>
/// 异步平滑笔画
@@ -89,15 +92,22 @@ namespace Ink_Canvas.Helpers
cancellationToken.ThrowIfCancellationRequested();
// 简化处理:只进行轻度平滑,避免点数爆炸
var smoothedPoints = ApplyLightSmoothing(originalPoints);
// 使用改进的贝塞尔曲线拟合
var smoothedPoints = ApplyImprovedBezierSmoothing(originalPoints);
cancellationToken.ThrowIfCancellationRequested();
// 确保点数不会过多
// 严格控制点数,避免产生过多
if (smoothedPoints.Length > originalPoints.Length * 2)
{
// 如果点数增加太多,回退到原始笔画
// 如果点数增加太多,进行重采样
smoothedPoints = ResampleEquidistantOptimized(smoothedPoints, ResampleInterval);
}
// 最终检查:确保点数不会过多
if (smoothedPoints.Length > originalPoints.Length * 1.5)
{
// 如果仍然太多点,使用原始笔画
return stroke;
}
@@ -111,34 +121,188 @@ namespace Ink_Canvas.Helpers
}
/// <summary>
/// 轻度平滑处理,避免点数爆炸
/// 改进的贝塞尔曲线平滑处理
/// </summary>
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<StylusPoint>();
result.Add(points[0]); // 保持第一个点
// 简单的3点平均平滑
for (int i = 1; i < points.Length - 1; i++)
// 添加第一个点
result.Add(points[0]);
// 使用非重叠的窗口进行贝塞尔曲线拟合
for (int i = 0; i < points.Length - 3; i += 3) // 每次移动3个点,避免重叠
{
var prev = points[i - 1];
var curr = points[i];
var next = points[i + 1];
var p0 = points[i];
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)];
// 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);
result.Add(new StylusPoint(x, y, Math.Max(pressure, 0.1f)));
// 限制插值步数,避免点数爆炸
int steps = Math.Min(UseAdaptiveInterpolation ?
CalculateAdaptiveSteps(p0, p1, p2, p3) : InterpolationSteps, 16);
// 生成贝塞尔曲线点,但跳过第一个点避免重复
for (int j = 1; j <= steps; j++)
{
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 RemoveDuplicatePoints(result.ToArray());
}
/// <summary>
/// 计算改进的控制点
/// </summary>
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);
}
/// <summary>
/// 自适应插值步数计算
/// </summary>
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));
}
/// <summary>
/// 计算曲率(简化版本)
/// </summary>
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
}
/// <summary>
/// 去除重复和过近的点
/// </summary>
private StylusPoint[] RemoveDuplicatePoints(StylusPoint[] points)
{
if (points.Length < 2) return points;
var result = new List<StylusPoint>();
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();
}
/// <summary>
/// 使用控制点的三次贝塞尔曲线计算
/// </summary>
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);
}
/// <summary>
/// 硬件加速的向量化指数平滑
/// </summary>
@@ -354,7 +518,7 @@ namespace Ink_Canvas.Helpers
{
public double SmoothingStrength { get; set; } = 0.3;
public double ResampleInterval { get; set; } = 3.0;
public int InterpolationSteps { get; set; } = 4;
public int InterpolationSteps { get; set; } = 8;
public Stroke SmoothStroke(Stroke stroke)
{
@@ -457,7 +621,7 @@ namespace Ink_Canvas.Helpers
return result;
}
private List<StylusPoint> SlidingBezierFit(List<StylusPoint> points, int window = 4, int steps = 24)
private List<StylusPoint> SlidingBezierFit(List<StylusPoint> points, int window = 4, int steps = 48) // 从24增加到48
{
var result = new List<StylusPoint>();
if (points.Count < window) return points;
File diff suppressed because it is too large Load Diff
+14 -14
View File
@@ -13,8 +13,8 @@ namespace Ink_Canvas.Helpers
{
private static readonly DependencyProperty IsAvoidFullScreenEnabledProperty =
DependencyProperty.RegisterAttached(
"IsAvoidFullScreenEnabled",
typeof(bool),
"IsAvoidFullScreenEnabled",
typeof(bool),
typeof(AvoidFullScreenHelper));
private static bool _isBoardMode;
@@ -122,19 +122,19 @@ namespace Ink_Canvas.Helpers
{
// 获取所有显示器
var screens = Screen.AllScreens;
// 确定窗口主要位于哪个显示器上
Screen targetScreen = null;
double maxIntersection = 0;
foreach (var screen in screens)
{
var screenRect = new Rect(
screen.WorkingArea.X,
screen.WorkingArea.Y,
screen.WorkingArea.Width,
screen.WorkingArea.X,
screen.WorkingArea.Y,
screen.WorkingArea.Width,
screen.WorkingArea.Height);
var intersection = Rect.Intersect(windowRect, screenRect);
if (intersection.Width * intersection.Height > maxIntersection)
{
@@ -142,11 +142,11 @@ namespace Ink_Canvas.Helpers
targetScreen = screen;
}
}
// 如果没找到,使用主显示器
if (targetScreen == null)
targetScreen = Screen.PrimaryScreen;
return new Rect(
targetScreen.WorkingArea.X,
targetScreen.WorkingArea.Y,
@@ -159,21 +159,21 @@ namespace Ink_Canvas.Helpers
// 调整尺寸以适应工作区域
if (windowRect.Width > workingArea.Width)
windowRect.Width = workingArea.Width;
if (windowRect.Height > workingArea.Height)
windowRect.Height = workingArea.Height;
// 调整位置以确保窗口完全在工作区域内
if (windowRect.Left < workingArea.Left)
windowRect.X = workingArea.Left;
else if (windowRect.Right > workingArea.Right)
windowRect.X = workingArea.Right - windowRect.Width;
if (windowRect.Top < workingArea.Top)
windowRect.Y = workingArea.Top;
else if (windowRect.Bottom > workingArea.Bottom)
windowRect.Y = workingArea.Bottom - windowRect.Height;
return windowRect;
}
}
+315
View File
@@ -0,0 +1,315 @@
using AForge.Video;
using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
namespace Ink_Canvas.Helpers
{
public class CameraService : IDisposable
{
private VideoCaptureDevice _videoSource;
private bool _isCapturing;
private Bitmap _currentFrame;
private readonly object _frameLock = new object();
private Dispatcher _dispatcher;
public event EventHandler<Bitmap> FrameReceived;
public event EventHandler<string> ErrorOccurred;
public bool IsCapturing => _isCapturing;
public List<FilterInfo> AvailableCameras { get; private set; }
public FilterInfo CurrentCamera { get; private set; }
public CameraService()
{
_dispatcher = Dispatcher.CurrentDispatcher;
AvailableCameras = new List<FilterInfo>();
RefreshCameraList();
}
/// <summary>
/// 刷新可用摄像头列表
/// </summary>
public void RefreshCameraList()
{
try
{
AvailableCameras.Clear();
var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
foreach (FilterInfo device in videoDevices)
{
AvailableCameras.Add(device);
}
LogHelper.WriteLogToFile($"发现 {AvailableCameras.Count} 个摄像头设备");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"刷新摄像头列表失败: {ex.Message}", LogHelper.LogType.Error);
ErrorOccurred?.Invoke(this, $"刷新摄像头列表失败: {ex.Message}");
}
}
/// <summary>
/// 开始摄像头预览
/// </summary>
/// <param name="cameraIndex">摄像头索引</param>
public bool StartPreview(int cameraIndex = 0)
{
try
{
if (AvailableCameras.Count == 0)
{
RefreshCameraList();
if (AvailableCameras.Count == 0)
{
ErrorOccurred?.Invoke(this, "未找到可用的摄像头设备");
return false;
}
}
if (cameraIndex < 0 || cameraIndex >= AvailableCameras.Count)
{
ErrorOccurred?.Invoke(this, "摄像头索引超出范围");
return false;
}
// 停止当前预览
StopPreview();
CurrentCamera = AvailableCameras[cameraIndex];
_videoSource = new VideoCaptureDevice(CurrentCamera.MonikerString);
// 设置视频源事件处理
_videoSource.NewFrame += VideoSource_NewFrame;
// 启动视频源
_videoSource.Start();
_isCapturing = true;
LogHelper.WriteLogToFile($"开始摄像头预览: {CurrentCamera.Name}");
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动摄像头预览失败: {ex.Message}", LogHelper.LogType.Error);
ErrorOccurred?.Invoke(this, $"启动摄像头预览失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 停止摄像头预览
/// </summary>
public void StopPreview()
{
try
{
if (_videoSource != null && _videoSource.IsRunning)
{
_videoSource.SignalToStop();
_videoSource.WaitForStop();
_videoSource.NewFrame -= VideoSource_NewFrame;
_videoSource = null;
}
_isCapturing = false;
LogHelper.WriteLogToFile("摄像头预览已停止");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"停止摄像头预览失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 切换到指定摄像头
/// </summary>
/// <param name="cameraIndex">摄像头索引</param>
public bool SwitchCamera(int cameraIndex)
{
try
{
if (cameraIndex < 0 || cameraIndex >= AvailableCameras.Count)
{
ErrorOccurred?.Invoke(this, "摄像头索引超出范围");
return false;
}
return StartPreview(cameraIndex);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换摄像头失败: {ex.Message}", LogHelper.LogType.Error);
ErrorOccurred?.Invoke(this, $"切换摄像头失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 获取当前帧的BitmapSource(WPF格式),直接返回可用的WPF位图
/// </summary>
public BitmapSource GetCurrentFrameAsBitmapSource()
{
lock (_frameLock)
{
if (_currentFrame == null)
return null;
try
{
// 验证位图有效性
if (_currentFrame.Width <= 0 || _currentFrame.Height <= 0)
return null;
// 使用更安全的方法转换位图
var bitmapData = _currentFrame.LockBits(
new Rectangle(0, 0, _currentFrame.Width, _currentFrame.Height),
ImageLockMode.ReadOnly,
_currentFrame.PixelFormat);
try
{
// 根据像素格式选择合适的WPF像素格式
System.Windows.Media.PixelFormat wpfPixelFormat;
switch (_currentFrame.PixelFormat)
{
case PixelFormat.Format24bppRgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr24;
break;
case PixelFormat.Format32bppArgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgra32;
break;
case PixelFormat.Format32bppRgb:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr32;
break;
default:
wpfPixelFormat = System.Windows.Media.PixelFormats.Bgr24;
break;
}
var bitmapSource = BitmapSource.Create(
bitmapData.Width,
bitmapData.Height,
_currentFrame.HorizontalResolution,
_currentFrame.VerticalResolution,
wpfPixelFormat,
null,
bitmapData.Scan0,
bitmapData.Stride * bitmapData.Height,
bitmapData.Stride);
bitmapSource.Freeze();
return bitmapSource;
}
finally
{
_currentFrame.UnlockBits(bitmapData);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"转换帧为BitmapSource失败: {ex.Message}", LogHelper.LogType.Error);
return null;
}
}
}
/// <summary>
/// 视频源新帧事件处理
/// </summary>
private void VideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
lock (_frameLock)
{
// 释放之前的帧
_currentFrame?.Dispose();
// 创建新的位图,避免Clone的问题
var sourceFrame = eventArgs.Frame;
if (sourceFrame != null)
{
try
{
var width = sourceFrame.Width;
var height = sourceFrame.Height;
if (width > 0 && height > 0)
{
_currentFrame = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (var graphics = Graphics.FromImage(_currentFrame))
{
graphics.DrawImage(sourceFrame, 0, 0);
}
}
else
{
_currentFrame = null;
}
}
catch (Exception frameEx)
{
LogHelper.WriteLogToFile($"处理源帧失败: {frameEx.Message}", LogHelper.LogType.Error);
_currentFrame = null;
}
}
else
{
_currentFrame = null;
}
}
// 在UI线程中触发事件
_dispatcher.BeginInvoke(new Action(() =>
{
FrameReceived?.Invoke(this, _currentFrame);
}));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理新帧失败: {ex.Message}", LogHelper.LogType.Error);
ErrorOccurred?.Invoke(this, $"处理新帧失败: {ex.Message}");
}
}
/// <summary>
/// 获取摄像头名称列表
/// </summary>
public List<string> GetCameraNames()
{
return AvailableCameras.Select(camera => camera.Name).ToList();
}
/// <summary>
/// 检查是否有可用摄像头
/// </summary>
public bool HasAvailableCameras()
{
if (AvailableCameras.Count == 0)
{
RefreshCameraList();
}
return AvailableCameras.Count > 0;
}
public void Dispose()
{
StopPreview();
lock (_frameLock)
{
_currentFrame?.Dispose();
}
}
}
}
+32 -15
View File
@@ -1,47 +1,64 @@
using System;
using System.IO;
namespace Ink_Canvas.Helpers {
internal class DelAutoSavedFiles {
public static void DeleteFilesOlder(string directoryPath, int daysThreshold) {
namespace Ink_Canvas.Helpers
{
internal class DelAutoSavedFiles
{
public static void DeleteFilesOlder(string directoryPath, int daysThreshold)
{
string[] extensionsToDel = { ".icstk", ".png" };
if (Directory.Exists(directoryPath)) {
if (Directory.Exists(directoryPath))
{
// 获取目录中的所有子目录
string[] subDirectories = Directory.GetDirectories(directoryPath, "*", SearchOption.AllDirectories);
foreach (string subDirectory in subDirectories) {
try {
foreach (string subDirectory in subDirectories)
{
try
{
// 获取子目录下的所有文件
string[] files = Directory.GetFiles(subDirectory);
foreach (string filePath in files) {
foreach (string filePath in files)
{
// 获取文件的创建日期
DateTime creationDate = File.GetCreationTime(filePath);
// 获取文件的扩展名
string fileExtension = Path.GetExtension(filePath);
// 如果文件的创建日期早于指定天数且是要删除的扩展名,则删除文件
if (creationDate < DateTime.Now.AddDays(-daysThreshold)) {
if (creationDate < DateTime.Now.AddDays(-daysThreshold))
{
if (Array.Exists(extensionsToDel, ext => ext.Equals(fileExtension, StringComparison.OrdinalIgnoreCase))
|| Path.GetFileName(filePath).Equals("Position", StringComparison.OrdinalIgnoreCase)) {
|| Path.GetFileName(filePath).Equals("Position", StringComparison.OrdinalIgnoreCase))
{
File.Delete(filePath);
}
}
}
} catch (Exception ex) {
}
catch (Exception ex)
{
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex, LogHelper.LogType.Error);
}
}
try { // 递归删除空文件夹
try
{ // 递归删除空文件夹
DeleteEmptyFolders(directoryPath);
} catch (Exception ex) {
}
catch (Exception ex)
{
LogHelper.WriteLogToFile("DelAutoSavedFiles | 处理文件时出错: " + ex, LogHelper.LogType.Error);
}
}
}
private static void DeleteEmptyFolders(string directoryPath) {
foreach (string dir in Directory.GetDirectories(directoryPath)) {
private static void DeleteEmptyFolders(string directoryPath)
{
foreach (string dir in Directory.GetDirectories(directoryPath))
{
DeleteEmptyFolders(dir);
if (Directory.GetFiles(dir).Length == 0 && Directory.GetDirectories(dir).Length == 0) {
if (Directory.GetFiles(dir).Length == 0 && Directory.GetDirectories(dir).Length == 0)
{
Directory.Delete(dir, false);
}
}
+6 -3
View File
@@ -14,10 +14,13 @@ namespace Ink_Canvas.Helpers
/// <param name="inv">同步的對象,一般傳入控件,不需要可null</param>
public void DebounceAction(int timeMs, ISynchronizeInvoke inv, Action action)
{
lock (this) {
if (_timerDebounce == null) {
lock (this)
{
if (_timerDebounce == null)
{
_timerDebounce = new Timer(timeMs) { AutoReset = false };
_timerDebounce.Elapsed += (o, e) => {
_timerDebounce.Elapsed += (o, e) =>
{
_timerDebounce.Stop(); _timerDebounce.Close(); _timerDebounce = null;
InvokeAction(action, inv);
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,483 @@
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading;
using System.Windows;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 文件关联管理器,用于注册和处理.icstk文件的关联
/// </summary>
public static class FileAssociationManager
{
private const string FileExtension = ".icstk";
private const string FileTypeName = "InkCanvasStrokesFile";
private const string AppName = "Ink Canvas";
private const string AppDescription = "Ink Canvas Strokes File";
// IPC相关常量
private const string IpcMutexName = "InkCanvasFileAssociationIpc";
private const string IpcEventName = "InkCanvasFileAssociationEvent";
private const string IpcFilePrefix = "InkCanvasFileAssociation_";
private const string IpcBoardModePrefix = "InkCanvasBoardMode_";
private const int IpcTimeout = 5000; // 5秒超时
/// <summary>
/// 注册.icstk文件关联
/// </summary>
public static bool RegisterFileAssociation()
{
try
{
string exePath = Process.GetCurrentProcess().MainModule.FileName;
// 注册文件类型
using (RegistryKey fileTypeKey = Registry.ClassesRoot.CreateSubKey(FileTypeName))
{
fileTypeKey.SetValue("", AppDescription);
fileTypeKey.SetValue("FriendlyTypeName", AppDescription);
// 设置默认图标
using (RegistryKey defaultIconKey = fileTypeKey.CreateSubKey("DefaultIcon"))
{
defaultIconKey.SetValue("", $"\"{exePath}\",0");
}
// 设置打开命令
using (RegistryKey shellKey = fileTypeKey.CreateSubKey("shell"))
using (RegistryKey openKey = shellKey.CreateSubKey("open"))
using (RegistryKey commandKey = openKey.CreateSubKey("command"))
{
commandKey.SetValue("", $"\"{exePath}\" \"%1\"");
}
}
// 注册文件扩展名
using (RegistryKey extensionKey = Registry.ClassesRoot.CreateSubKey(FileExtension))
{
extensionKey.SetValue("", FileTypeName);
}
// 刷新系统文件关联缓存
RefreshSystemFileAssociations();
LogHelper.WriteLogToFile($"成功注册{FileExtension}文件关联", LogHelper.LogType.Event);
return true;
}
catch (SecurityException ex)
{
LogHelper.WriteLogToFile($"注册文件关联时权限不足: {ex.Message}", LogHelper.LogType.Error);
return false;
}
catch (UnauthorizedAccessException ex)
{
LogHelper.WriteLogToFile($"注册文件关联时访问被拒绝: {ex.Message}", LogHelper.LogType.Error);
return false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注册文件关联时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 注销.icstk文件关联
/// </summary>
public static bool UnregisterFileAssociation()
{
try
{
// 删除文件扩展名关联
Registry.ClassesRoot.DeleteSubKeyTree(FileExtension, false);
// 删除文件类型定义
Registry.ClassesRoot.DeleteSubKeyTree(FileTypeName, false);
// 刷新系统文件关联缓存
RefreshSystemFileAssociations();
LogHelper.WriteLogToFile($"成功注销{FileExtension}文件关联", LogHelper.LogType.Event);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注销文件关联时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 检查文件关联是否已注册
/// </summary>
public static bool IsFileAssociationRegistered()
{
try
{
using (RegistryKey extensionKey = Registry.ClassesRoot.OpenSubKey(FileExtension))
{
if (extensionKey == null) return false;
string fileType = extensionKey.GetValue("") as string;
if (string.IsNullOrEmpty(fileType)) return false;
using (RegistryKey fileTypeKey = Registry.ClassesRoot.OpenSubKey(fileType))
{
if (fileTypeKey == null) return false;
using (RegistryKey shellKey = fileTypeKey.OpenSubKey("shell\\open\\command"))
{
if (shellKey == null) return false;
string command = shellKey.GetValue("") as string;
if (string.IsNullOrEmpty(command)) return false;
// 检查命令是否指向当前应用程序
string currentExePath = Process.GetCurrentProcess().MainModule.FileName;
return command.Contains(currentExePath);
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检查文件关联状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
return false;
}
/// <summary>
/// 显示文件关联状态
/// </summary>
public static void ShowFileAssociationStatus()
{
bool isRegistered = IsFileAssociationRegistered();
LogHelper.WriteLogToFile($"{FileExtension}文件关联状态: {(isRegistered ? "" : "")}", LogHelper.LogType.Event);
}
/// <summary>
/// 刷新系统文件关联缓存
/// </summary>
private static void RefreshSystemFileAssociations()
{
try
{
// 通知系统文件关联已更改
SHChangeNotify(0x08000000, 0, IntPtr.Zero, IntPtr.Zero);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"刷新文件关联缓存时出错: {ex.Message}", LogHelper.LogType.Warning);
}
}
/// <summary>
/// 处理命令行参数中的文件路径
/// </summary>
/// <param name="args">命令行参数</param>
/// <returns>找到的.icstk文件路径,如果没有找到则返回null</returns>
public static string GetIcstkFileFromArgs(string[] args)
{
if (args == null || args.Length == 0) return null;
foreach (string arg in args)
{
if (string.IsNullOrEmpty(arg)) continue;
// 检查是否为.icstk文件
if (Path.GetExtension(arg).Equals(FileExtension, StringComparison.OrdinalIgnoreCase))
{
// 检查文件是否存在
if (File.Exists(arg))
{
LogHelper.WriteLogToFile($"从命令行参数中找到.icstk文件: {arg}", LogHelper.LogType.Event);
return arg;
}
else
{
LogHelper.WriteLogToFile($"命令行参数中的.icstk文件不存在: {arg}", LogHelper.LogType.Warning);
}
}
}
return null;
}
/// <summary>
/// 尝试通过IPC将文件路径发送给已运行的实例
/// </summary>
/// <param name="filePath">要打开的文件路径</param>
/// <returns>是否成功发送</returns>
public static bool TrySendFileToExistingInstance(string filePath)
{
try
{
LogHelper.WriteLogToFile($"尝试通过IPC发送文件路径给已运行实例: {filePath}", LogHelper.LogType.Event);
// 创建IPC文件
string tempDir = Path.GetTempPath();
string ipcFileName = IpcFilePrefix + Guid.NewGuid().ToString("N") + ".tmp";
string ipcFilePath = Path.Combine(tempDir, ipcFileName);
// 写入文件路径到IPC文件
File.WriteAllText(ipcFilePath, filePath, Encoding.UTF8);
// 创建事件通知已运行实例
using (EventWaitHandle ipcEvent = new EventWaitHandle(false, EventResetMode.ManualReset, IpcEventName))
{
ipcEvent.Set();
}
// 等待一段时间让已运行实例处理文件
Thread.Sleep(1000);
// 清理IPC文件
try
{
if (File.Exists(ipcFilePath))
{
File.Delete(ipcFilePath);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
LogHelper.WriteLogToFile("IPC文件路径发送完成", LogHelper.LogType.Event);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"通过IPC发送文件路径失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 尝试通过IPC将白板模式命令发送给已运行的实例
/// </summary>
/// <returns>是否成功发送</returns>
public static bool TrySendBoardModeCommandToExistingInstance()
{
try
{
LogHelper.WriteLogToFile("尝试通过IPC发送白板模式命令给已运行实例", LogHelper.LogType.Event);
// 创建IPC文件
string tempDir = Path.GetTempPath();
string ipcFileName = IpcBoardModePrefix + Guid.NewGuid().ToString("N") + ".tmp";
string ipcFilePath = Path.Combine(tempDir, ipcFileName);
// 写入白板模式命令到IPC文件
File.WriteAllText(ipcFilePath, "BOARD_MODE", Encoding.UTF8);
// 创建事件通知已运行实例
using (EventWaitHandle ipcEvent = new EventWaitHandle(false, EventResetMode.ManualReset, IpcEventName))
{
ipcEvent.Set();
}
// 等待一段时间让已运行实例处理命令
Thread.Sleep(1000);
// 清理IPC文件
try
{
if (File.Exists(ipcFilePath))
{
File.Delete(ipcFilePath);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
}
LogHelper.WriteLogToFile("IPC白板模式命令发送完成", LogHelper.LogType.Event);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"通过IPC发送白板模式命令失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 启动IPC监听器,等待其他实例发送文件路径
/// </summary>
public static void StartIpcListener()
{
try
{
Thread ipcThread = new Thread(() =>
{
try
{
LogHelper.WriteLogToFile("启动IPC监听器", LogHelper.LogType.Event);
using (EventWaitHandle ipcEvent = new EventWaitHandle(false, EventResetMode.ManualReset, IpcEventName))
{
while (true)
{
// 等待IPC事件
if (ipcEvent.WaitOne(IpcTimeout))
{
// 处理IPC文件
ProcessIpcFiles();
// 重置事件
ipcEvent.Reset();
}
// 检查应用是否还在运行
if (Application.Current == null || Application.Current.Dispatcher == null)
{
break;
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"IPC监听器出错: {ex.Message}", LogHelper.LogType.Error);
}
});
ipcThread.IsBackground = true;
ipcThread.Start();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动IPC监听器失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 处理IPC文件
/// </summary>
private static void ProcessIpcFiles()
{
try
{
string tempDir = Path.GetTempPath();
// 处理文件路径IPC文件
string[] ipcFiles = Directory.GetFiles(tempDir, IpcFilePrefix + "*.tmp");
foreach (string ipcFile in ipcFiles)
{
try
{
// 读取文件路径
string filePath = File.ReadAllText(ipcFile, Encoding.UTF8);
if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
{
LogHelper.WriteLogToFile($"IPC接收到文件路径: {filePath}", LogHelper.LogType.Event);
// 在UI线程中处理文件打开
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 获取主窗口并打开文件
if (Application.Current.MainWindow is MainWindow mainWindow)
{
mainWindow.OpenSingleStrokeFile(filePath);
mainWindow.ShowNotification($"已加载墨迹文件: {Path.GetFileName(filePath)}");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"IPC处理文件打开失败: {ex.Message}", LogHelper.LogType.Error);
}
}));
}
// 删除IPC文件
File.Delete(ipcFile);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
// 尝试删除损坏的IPC文件
try
{
if (File.Exists(ipcFile))
{
File.Delete(ipcFile);
}
}
catch { }
}
}
// 处理白板模式命令IPC文件
string[] boardModeFiles = Directory.GetFiles(tempDir, IpcBoardModePrefix + "*.tmp");
foreach (string ipcFile in boardModeFiles)
{
try
{
// 读取命令内容
string command = File.ReadAllText(ipcFile, Encoding.UTF8);
if (command == "BOARD_MODE")
{
LogHelper.WriteLogToFile("IPC接收到白板模式命令", LogHelper.LogType.Event);
// 在UI线程中处理白板模式切换
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// 获取主窗口并切换到白板模式
if (Application.Current.MainWindow is MainWindow mainWindow)
{
mainWindow.SwitchToBoardMode();
mainWindow.ShowNotification("已切换到白板模式");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"IPC处理白板模式切换失败: {ex.Message}", LogHelper.LogType.Error);
}
}));
}
// 删除IPC文件
File.Delete(ipcFile);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理白板模式IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
// 尝试删除损坏的IPC文件
try
{
if (File.Exists(ipcFile))
{
File.Delete(ipcFile);
}
}
catch { }
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理IPC文件时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
[DllImport("shell32.dll")]
private static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
}
}
@@ -0,0 +1,762 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 悬浮窗拦截器 - 检测和隐藏指定的悬浮窗
/// </summary>
public class FloatingWindowInterceptor : IDisposable
{
#region Windows API Declarations
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll")]
private static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
private static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out ForegroundWindowInfo.RECT lpRect);
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
private static extern int GetProcessImageFileName(IntPtr hProcess, StringBuilder lpImageFileName, int nSize);
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
private const int SW_HIDE = 0;
private const int SW_SHOW = 1;
private const int SW_MINIMIZE = 6;
private const int SW_RESTORE = 9;
private const int GWL_EXSTYLE = -20;
private const uint WS_EX_TOOLWINDOW = 0x00000080;
private const uint WS_EX_APPWINDOW = 0x00040000;
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_HIDEWINDOW = 0x0080;
private const uint PROCESS_QUERY_INFORMATION = 0x0400;
private const uint PROCESS_VM_READ = 0x0010;
#endregion
#region
/// <summary>
/// 拦截规则类型
/// </summary>
public enum InterceptType
{
/// <summary>
/// 希沃白板3 桌面悬浮窗
/// </summary>
SeewoWhiteboard3Floating,
/// <summary>
/// 希沃白板5 桌面悬浮窗
/// </summary>
SeewoWhiteboard5Floating,
/// <summary>
/// 希沃白板5C 桌面悬浮窗
/// </summary>
SeewoWhiteboard5CFloating,
/// <summary>
/// 希沃品课教师端 桌面悬浮窗
/// </summary>
SeewoPincoSideBarFloating,
/// <summary>
/// 希沃品课教师端 画笔悬浮窗(包括PPT控件)
/// </summary>
SeewoPincoDrawingFloating,
/// <summary>
/// 希沃PPT小工具
/// </summary>
SeewoPPTFloating,
/// <summary>
/// AiClass 桌面悬浮窗
/// </summary>
AiClassFloating,
/// <summary>
/// 鸿合屏幕书写
/// </summary>
HiteAnnotationFloating,
/// <summary>
/// 畅言智慧课堂 桌面悬浮窗
/// </summary>
ChangYanFloating,
/// <summary>
/// 畅言智慧课堂 PPT悬浮窗
/// </summary>
ChangYanPptFloating,
/// <summary>
/// 天喻教育云互动课堂 桌面悬浮窗(包括PPT控件)
/// </summary>
IntelligentClassFloating,
/// <summary>
/// 希沃桌面 画笔悬浮窗
/// </summary>
SeewoDesktopAnnotationFloating,
/// <summary>
/// 希沃桌面 侧栏悬浮窗
/// </summary>
SeewoDesktopSideBarFloating
}
/// <summary>
/// 拦截规则
/// </summary>
public class InterceptRule
{
public InterceptType Type { get; set; }
public string ProcessName { get; set; }
public string WindowTitlePattern { get; set; }
public string ClassNamePattern { get; set; }
public bool IsEnabled { get; set; }
public bool RequiresAdmin { get; set; }
public string Description { get; set; }
}
#endregion
#region
private readonly Dictionary<InterceptType, InterceptRule> _interceptRules;
private readonly Dictionary<IntPtr, InterceptType> _interceptedWindows;
private readonly Timer _scanTimer;
private readonly Dispatcher _dispatcher;
private bool _isRunning;
private bool _disposed;
#endregion
#region
public bool IsRunning => _isRunning;
#endregion
#region
public event EventHandler<WindowInterceptedEventArgs> WindowIntercepted;
public event EventHandler<WindowRestoredEventArgs> WindowRestored;
#endregion
#region
public FloatingWindowInterceptor()
{
_interceptRules = new Dictionary<InterceptType, InterceptRule>();
_interceptedWindows = new Dictionary<IntPtr, InterceptType>();
_dispatcher = Dispatcher.CurrentDispatcher;
InitializeRules();
_scanTimer = new Timer(ScanForWindows, null, Timeout.Infinite, Timeout.Infinite);
}
#endregion
#region
private void InitializeRules()
{
// 希沃白板3 桌面悬浮窗
_interceptRules[InterceptType.SeewoWhiteboard3Floating] = new InterceptRule
{
Type = InterceptType.SeewoWhiteboard3Floating,
ProcessName = "EasiNote",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = false,
Description = "希沃白板3 桌面悬浮窗"
};
// 希沃白板5 桌面悬浮窗
_interceptRules[InterceptType.SeewoWhiteboard5Floating] = new InterceptRule
{
Type = InterceptType.SeewoWhiteboard5Floating,
ProcessName = "EasiNote",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = false,
Description = "希沃白板5 桌面悬浮窗"
};
// 希沃白板5C 桌面悬浮窗
_interceptRules[InterceptType.SeewoWhiteboard5CFloating] = new InterceptRule
{
Type = InterceptType.SeewoWhiteboard5CFloating,
ProcessName = "EasiNote5C",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = false,
Description = "希沃白板5C 桌面悬浮窗"
};
// 希沃品课教师端 桌面悬浮窗
_interceptRules[InterceptType.SeewoPincoSideBarFloating] = new InterceptRule
{
Type = InterceptType.SeewoPincoSideBarFloating,
ProcessName = "ClassIn",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = false,
Description = "希沃品课教师端 桌面悬浮窗"
};
// 希沃品课教师端 画笔悬浮窗
_interceptRules[InterceptType.SeewoPincoDrawingFloating] = new InterceptRule
{
Type = InterceptType.SeewoPincoDrawingFloating,
ProcessName = "ClassIn",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = false,
Description = "希沃品课教师端 画笔悬浮窗(包括PPT控件)"
};
// 希沃PPT小工具
_interceptRules[InterceptType.SeewoPPTFloating] = new InterceptRule
{
Type = InterceptType.SeewoPPTFloating,
ProcessName = "SeewoPPT",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = false,
Description = "希沃PPT小工具"
};
// AiClass 桌面悬浮窗
_interceptRules[InterceptType.AiClassFloating] = new InterceptRule
{
Type = InterceptType.AiClassFloating,
ProcessName = "ClassIn",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = false,
Description = "AiClass 桌面悬浮窗"
};
// 鸿合屏幕书写
_interceptRules[InterceptType.HiteAnnotationFloating] = new InterceptRule
{
Type = InterceptType.HiteAnnotationFloating,
ProcessName = "HiteVision",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = false,
Description = "鸿合屏幕书写"
};
// 畅言智慧课堂 桌面悬浮窗
_interceptRules[InterceptType.ChangYanFloating] = new InterceptRule
{
Type = InterceptType.ChangYanFloating,
ProcessName = "ClassIn",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = true,
Description = "畅言智慧课堂 桌面悬浮窗"
};
// 畅言智慧课堂 PPT悬浮窗
_interceptRules[InterceptType.ChangYanPptFloating] = new InterceptRule
{
Type = InterceptType.ChangYanPptFloating,
ProcessName = "ClassIn",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = true,
Description = "畅言智慧课堂 PPT悬浮窗"
};
// 天喻教育云互动课堂 桌面悬浮窗
_interceptRules[InterceptType.IntelligentClassFloating] = new InterceptRule
{
Type = InterceptType.IntelligentClassFloating,
ProcessName = "ClassIn",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = false,
Description = "天喻教育云互动课堂 桌面悬浮窗(包括PPT控件)"
};
// 希沃桌面 画笔悬浮窗
_interceptRules[InterceptType.SeewoDesktopAnnotationFloating] = new InterceptRule
{
Type = InterceptType.SeewoDesktopAnnotationFloating,
ProcessName = "Seewo",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = false,
Description = "希沃桌面 画笔悬浮窗"
};
// 希沃桌面 侧栏悬浮窗
_interceptRules[InterceptType.SeewoDesktopSideBarFloating] = new InterceptRule
{
Type = InterceptType.SeewoDesktopSideBarFloating,
ProcessName = "Seewo",
WindowTitlePattern = "",
ClassNamePattern = "",
IsEnabled = true,
RequiresAdmin = true,
Description = "希沃桌面 侧栏悬浮窗"
};
}
#endregion
#region
/// <summary>
/// 启动拦截器
/// </summary>
public void Start(int scanIntervalMs = 5000)
{
if (_isRunning) return;
_isRunning = true;
_scanTimer.Change(0, scanIntervalMs);
}
/// <summary>
/// 停止拦截器
/// </summary>
public void Stop()
{
if (!_isRunning) return;
_isRunning = false;
_scanTimer.Change(Timeout.Infinite, Timeout.Infinite);
// 恢复所有被拦截的窗口
RestoreAllWindows();
}
/// <summary>
/// 设置拦截规则
/// </summary>
public void SetInterceptRule(InterceptType type, bool enabled)
{
if (_interceptRules.ContainsKey(type))
{
_interceptRules[type].IsEnabled = enabled;
}
}
/// <summary>
/// 获取拦截规则
/// </summary>
public InterceptRule GetInterceptRule(InterceptType type)
{
return _interceptRules.ContainsKey(type) ? _interceptRules[type] : null;
}
/// <summary>
/// 获取所有拦截规则
/// </summary>
public Dictionary<InterceptType, InterceptRule> GetAllRules()
{
return new Dictionary<InterceptType, InterceptRule>(_interceptRules);
}
/// <summary>
/// 手动扫描一次
/// </summary>
public void ScanOnce()
{
ScanForWindows(null);
}
/// <summary>
/// 恢复所有被拦截的窗口
/// </summary>
public void RestoreAllWindows()
{
var windowsToRestore = new List<IntPtr>(_interceptedWindows.Keys);
foreach (var hWnd in windowsToRestore)
{
RestoreWindow(hWnd);
}
}
/// <summary>
/// 恢复指定窗口
/// </summary>
public bool RestoreWindow(IntPtr hWnd)
{
if (!_interceptedWindows.ContainsKey(hWnd)) return false;
if (IsWindow(hWnd))
{
ShowWindow(hWnd, SW_RESTORE);
_interceptedWindows.Remove(hWnd);
WindowRestored?.Invoke(this, new WindowRestoredEventArgs
{
WindowHandle = hWnd,
InterceptType = _interceptedWindows[hWnd]
});
return true;
}
_interceptedWindows.Remove(hWnd);
return false;
}
#endregion
#region
private void ScanForWindows(object state)
{
if (!_isRunning) return;
try
{
EnumWindows(EnumWindowsCallback, IntPtr.Zero);
}
catch (Exception ex)
{
// 记录错误但不中断扫描
LogHelper.WriteLogToFile($"扫描窗口时发生错误: {ex.Message}", LogHelper.LogType.Error);
}
}
private bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam)
{
try
{
// 基本检查
if (!IsWindow(hWnd) || !IsWindowVisible(hWnd)) return true;
// 检查是否已经被拦截
if (_interceptedWindows.ContainsKey(hWnd)) return true;
// 获取窗口信息
var windowInfo = GetWindowInfo(hWnd);
if (windowInfo == null) return true;
// 检查窗口样式,过滤掉系统窗口和主窗口
var exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
var style = GetWindowLong(hWnd, -16); // GWL_STYLE
// 跳过工具窗口
if ((exStyle & WS_EX_TOOLWINDOW) != 0)
{
return true;
}
// 跳过主窗口(有标题栏和系统菜单的窗口)
const uint WS_CAPTION = 0x00C00000;
const uint WS_SYSMENU = 0x00080000;
if ((style & WS_CAPTION) != 0 && (style & WS_SYSMENU) != 0)
{
return true;
}
// 检查窗口大小,跳过过大的窗口
var rect = new ForegroundWindowInfo.RECT();
GetWindowRect(hWnd, out rect);
var width = rect.Right - rect.Left;
var height = rect.Bottom - rect.Top;
if (width > 600 || height > 400)
{
return true;
}
// 检查是否匹配拦截规则
foreach (var rule in _interceptRules.Values)
{
if (!rule.IsEnabled) continue;
if (MatchesRule(windowInfo, rule))
{
InterceptWindow(hWnd, rule);
break;
}
}
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理窗口时发生错误: {ex.Message}", LogHelper.LogType.Error);
return true;
}
}
private WindowInfo GetWindowInfo(IntPtr hWnd)
{
try
{
// 获取进程ID
GetWindowThreadProcessId(hWnd, out uint processId);
if (processId == 0) return null;
// 获取进程信息
var process = Process.GetProcessById((int)processId);
if (process == null) return null;
// 获取窗口标题
var titleBuilder = new StringBuilder(256);
GetWindowText(hWnd, titleBuilder, titleBuilder.Capacity);
// 获取窗口类名
var classBuilder = new StringBuilder(256);
GetClassName(hWnd, classBuilder, classBuilder.Capacity);
return new WindowInfo
{
Handle = hWnd,
ProcessId = processId,
ProcessName = process.ProcessName,
WindowTitle = titleBuilder.ToString(),
ClassName = classBuilder.ToString(),
Process = process
};
}
catch
{
return null;
}
}
private bool MatchesRule(WindowInfo windowInfo, InterceptRule rule)
{
try
{
// 检查进程名(如果指定了进程名)
if (!string.IsNullOrEmpty(rule.ProcessName))
{
if (!windowInfo.ProcessName.ToLower().Contains(rule.ProcessName.ToLower()))
{
return false;
}
}
// 检查窗口标题(如果指定了模式)
if (!string.IsNullOrEmpty(rule.WindowTitlePattern))
{
if (!windowInfo.WindowTitle.ToLower().Contains(rule.WindowTitlePattern.ToLower()))
{
return false;
}
}
// 检查类名(如果指定了模式)
if (!string.IsNullOrEmpty(rule.ClassNamePattern))
{
if (!windowInfo.ClassName.ToLower().Contains(rule.ClassNamePattern.ToLower()))
{
return false;
}
}
// 如果所有检查都通过,就认为是目标窗口
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"匹配规则时发生错误: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
private void InterceptWindow(IntPtr hWnd, InterceptRule rule)
{
try
{
// 使用多种方法隐藏窗口
// 方法1:移动到屏幕外
SetWindowPos(hWnd, IntPtr.Zero, -2000, -2000, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW);
// 方法2:最小化窗口
ShowWindow(hWnd, SW_MINIMIZE);
// 方法3:隐藏窗口
ShowWindow(hWnd, SW_HIDE);
// 记录拦截的窗口
_interceptedWindows[hWnd] = rule.Type;
// 触发事件
WindowIntercepted?.Invoke(this, new WindowInterceptedEventArgs
{
WindowHandle = hWnd,
InterceptType = rule.Type,
Rule = rule,
WindowTitle = GetWindowTitle(hWnd)
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"拦截窗口时发生错误: {ex.Message}", LogHelper.LogType.Error);
}
}
private string GetWindowTitle(IntPtr hWnd)
{
try
{
var titleBuilder = new StringBuilder(256);
GetWindowText(hWnd, titleBuilder, titleBuilder.Capacity);
return titleBuilder.ToString();
}
catch
{
return "Unknown";
}
}
private bool IsMainWindow(IntPtr hWnd)
{
try
{
// 检查是否有父窗口
var parent = GetWindow(hWnd, 4); // GW_OWNER
if (parent != IntPtr.Zero) return false;
// 检查窗口样式
var style = GetWindowLong(hWnd, -16); // GWL_STYLE
var exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
// 主窗口通常有 WS_CAPTION 和 WS_SYSMENU
const uint WS_CAPTION = 0x00C00000;
const uint WS_SYSMENU = 0x00080000;
if ((style & WS_CAPTION) != 0 && (style & WS_SYSMENU) != 0)
{
return true; // 这可能是主窗口
}
// 检查窗口大小,主窗口通常比较大
var rect = new ForegroundWindowInfo.RECT();
GetWindowRect(hWnd, out rect);
var width = rect.Right - rect.Left;
var height = rect.Bottom - rect.Top;
// 如果窗口很大,可能是主窗口
if (width > 800 && height > 600)
{
return true;
}
return false;
}
catch
{
return false;
}
}
#endregion
#region
private class WindowInfo
{
public IntPtr Handle { get; set; }
public uint ProcessId { get; set; }
public string ProcessName { get; set; }
public string WindowTitle { get; set; }
public string ClassName { get; set; }
public Process Process { get; set; }
}
#endregion
#region
public class WindowInterceptedEventArgs : EventArgs
{
public IntPtr WindowHandle { get; set; }
public InterceptType InterceptType { get; set; }
public InterceptRule Rule { get; set; }
public string WindowTitle { get; set; }
}
public class WindowRestoredEventArgs : EventArgs
{
public IntPtr WindowHandle { get; set; }
public InterceptType InterceptType { get; set; }
}
#endregion
#region IDisposable
public void Dispose()
{
if (_disposed) return;
Stop();
_scanTimer?.Dispose();
// 恢复所有被拦截的窗口
RestoreAllWindows();
_disposed = true;
}
#endregion
}
}
+17 -8
View File
@@ -25,7 +25,8 @@ namespace Ink_Canvas.Helpers
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public struct RECT
{
public int Left;
public int Top;
public int Right;
@@ -35,7 +36,8 @@ namespace Ink_Canvas.Helpers
public int Height => Bottom - Top;
}
public static string WindowTitle() {
public static string WindowTitle()
{
IntPtr foregroundWindowHandle = GetForegroundWindow();
const int nChars = 256;
@@ -45,7 +47,8 @@ namespace Ink_Canvas.Helpers
return windowTitle.ToString();
}
public static string WindowClassName() {
public static string WindowClassName()
{
IntPtr foregroundWindowHandle = GetForegroundWindow();
const int nChars = 256;
@@ -55,7 +58,8 @@ namespace Ink_Canvas.Helpers
return className.ToString();
}
public static RECT WindowRect() {
public static RECT WindowRect()
{
IntPtr foregroundWindowHandle = GetForegroundWindow();
RECT windowRect;
@@ -64,15 +68,19 @@ namespace Ink_Canvas.Helpers
return windowRect;
}
public static string ProcessName() {
public static string ProcessName()
{
IntPtr foregroundWindowHandle = GetForegroundWindow();
uint processId;
GetWindowThreadProcessId(foregroundWindowHandle, out processId);
try {
try
{
Process process = Process.GetProcessById((int)processId);
return process.ProcessName;
} catch (ArgumentException) {
}
catch (ArgumentException)
{
// Process with the given ID not found
return "Unknown";
}
@@ -89,7 +97,8 @@ namespace Ink_Canvas.Helpers
Process process = Process.GetProcessById((int)processId);
return process.MainModule.FileName;
}
catch {
catch
{
// Process with the given ID not found
return "Unknown";
}
+2 -2
View File
@@ -81,7 +81,7 @@ namespace Ink_Canvas.Helpers
public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
public static IntPtr GetWindowLongPtr(IntPtr hWnd, GetWindowLongFields nIndex) =>
GetWindowLongPtr(hWnd, (int) nIndex);
GetWindowLongPtr(hWnd, (int)nIndex);
public static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
{
@@ -99,7 +99,7 @@ namespace Ink_Canvas.Helpers
public static extern IntPtr GetWindowLongPtr_x64(IntPtr hWnd, int nIndex);
public static IntPtr SetWindowLongPtr(IntPtr hWnd, GetWindowLongFields nIndex, IntPtr dwNewLong) =>
SetWindowLongPtr(hWnd, (int) nIndex, dwNewLong);
SetWindowLongPtr(hWnd, (int)nIndex, dwNewLong);
public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
+8 -8
View File
@@ -67,12 +67,12 @@ namespace Ink_Canvas.Helpers
//获取当前窗口的位置大小状态并保存
var placement = new WINDOWPLACEMENT();
placement.Size = (uint) Marshal.SizeOf(placement);
placement.Size = (uint)Marshal.SizeOf(placement);
Win32.User32.GetWindowPlacement(hwnd, ref placement);
window.SetValue(BeforeFullScreenWindowPlacementProperty, placement);
//修改窗口样式
var style = (WindowStyles) Win32.User32.GetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE);
var style = (WindowStyles)Win32.User32.GetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE);
window.SetValue(BeforeFullScreenWindowStyleProperty, style);
//将窗口恢复到还原模式,在有标题栏的情况下最大化模式下无法全屏,
//这里采用还原,不修改标题栏的方式
@@ -81,7 +81,7 @@ namespace Ink_Canvas.Helpers
//去掉WS_MAXIMIZEBOX,禁用最大化,如果最大化会退出全屏
//去掉WS_MAXIMIZE,使窗口变成还原状态,不使用ShowWindow(hwnd, ShowWindowCommands.SW_RESTORE),避免看到窗口变成还原状态这一过程(也避免影响窗口的Visible状态)
style &= (~(WindowStyles.WS_THICKFRAME | WindowStyles.WS_MAXIMIZEBOX | WindowStyles.WS_MAXIMIZE));
Win32.User32.SetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE, (IntPtr) style);
Win32.User32.SetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE, (IntPtr)style);
//禁用 DWM 过渡动画 忽略返回值,若DWM关闭不做处理
Win32.Dwmapi.DwmSetWindowAttribute(hwnd, DWMWINDOWATTRIBUTE.DWMWA_TRANSITIONS_FORCEDISABLED, 1,
@@ -95,8 +95,8 @@ namespace Ink_Canvas.Helpers
//不能用 placement 的坐标,placement是工作区坐标,不是屏幕坐标。
//使用窗口当前的矩形调用下设置窗口位置和尺寸的方法,让Hook来进行调整窗口位置和尺寸到全屏模式
Win32.User32.SetWindowPos(hwnd, (IntPtr) HwndZOrder.HWND_TOPMOST, rect.Left, rect.Top, rect.Width,
rect.Height, (int) WindowPositionFlags.SWP_NOZORDER);
Win32.User32.SetWindowPos(hwnd, (IntPtr)HwndZOrder.HWND_TOPMOST, rect.Left, rect.Top, rect.Width,
rect.Height, (int)WindowPositionFlags.SWP_NOZORDER);
}
}
}
@@ -139,7 +139,7 @@ namespace Ink_Canvas.Helpers
//不要改变Style里的WS_MAXIMIZE,否则会使窗口变成最大化状态,但是尺寸不对
//也不要设置回Style里的WS_MINIMIZE,否则会导致窗口最小化按钮显示成还原按钮
Win32.User32.SetWindowLongPtr(hwnd, GetWindowLongFields.GWL_STYLE,
(IntPtr) (style & (~(WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE))));
(IntPtr)(style & (~(WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE))));
if ((style & WindowStyles.WS_MINIMIZE) != 0)
{
@@ -201,7 +201,7 @@ namespace Ink_Canvas.Helpers
try
{
//得到WINDOWPOS结构体
var pos = (WindowPosition) Marshal.PtrToStructure(lParam, typeof(WindowPosition));
var pos = (WindowPosition)Marshal.PtrToStructure(lParam, typeof(WindowPosition));
if ((pos.Flags & WindowPositionFlags.SWP_NOMOVE) != 0 &&
(pos.Flags & WindowPositionFlags.SWP_NOSIZE) != 0)
@@ -245,7 +245,7 @@ namespace Ink_Canvas.Helpers
//使用目标矩形获取显示器信息
var monitor = Win32.User32.MonitorFromRect(targetRect, MonitorFlag.MONITOR_DEFAULTTOPRIMARY);
var info = new MonitorInfo();
info.Size = (uint) Marshal.SizeOf(info);
info.Size = (uint)Marshal.SizeOf(info);
if (Win32.User32.GetMonitorInfo(monitor, ref info))
{
//基于显示器信息设置窗口尺寸位置
+956
View File
@@ -0,0 +1,956 @@
using Newtonsoft.Json;
using NHotkey.Wpf;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Input;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 全局快捷键管理器 - 使用NHotkey库实现全局快捷键功能
/// </summary>
public class GlobalHotkeyManager : IDisposable
{
#region Private Fields
private readonly Dictionary<string, HotkeyInfo> _registeredHotkeys;
private readonly MainWindow _mainWindow;
private bool _isDisposed;
private bool _hotkeysShouldBeRegistered = true; // 启动时注册热键
// 配置文件路径
private static readonly string HotkeyConfigFile = Path.Combine(App.RootPath, "Configs", "HotkeyConfig.json");
#endregion
#region Constructor
public GlobalHotkeyManager(MainWindow mainWindow)
{
_mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow));
_registeredHotkeys = new Dictionary<string, HotkeyInfo>();
_hotkeysShouldBeRegistered = true; // 启动时注册热键
// 启动时确保配置文件存在
EnsureConfigFileExists();
}
#endregion
#region Public Methods
/// <summary>
/// 注册全局快捷键
/// </summary>
/// <param name="hotkeyName">快捷键名称</param>
/// <param name="key">按键</param>
/// <param name="modifiers">修饰键</param>
/// <param name="action">执行动作</param>
/// <returns>是否注册成功</returns>
public bool RegisterHotkey(string hotkeyName, Key key, ModifierKeys modifiers, Action action)
{
try
{
if (_isDisposed)
return false;
// 如果快捷键已存在,先注销
if (_registeredHotkeys.ContainsKey(hotkeyName))
{
UnregisterHotkey(hotkeyName);
}
// 创建快捷键信息
var hotkeyInfo = new HotkeyInfo
{
Name = hotkeyName,
Key = key,
Modifiers = modifiers,
Action = action
};
// 注册快捷键
HotkeyManager.Current.AddOrReplace(hotkeyName, key, modifiers, (sender, e) =>
{
try
{
// 确保在主线程中执行
_mainWindow.Dispatcher.Invoke(() =>
{
action?.Invoke();
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"执行快捷键 {hotkeyName} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
});
_registeredHotkeys[hotkeyName] = hotkeyInfo;
// 成功注册全局快捷键
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注册全局快捷键 {hotkeyName} 失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 注销指定快捷键
/// </summary>
/// <param name="hotkeyName">快捷键名称</param>
/// <returns>是否注销成功</returns>
public bool UnregisterHotkey(string hotkeyName)
{
try
{
if (_isDisposed || !_registeredHotkeys.ContainsKey(hotkeyName))
return false;
HotkeyManager.Current.Remove(hotkeyName);
_registeredHotkeys.Remove(hotkeyName);
// 成功注销全局快捷键
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注销全局快捷键 {hotkeyName} 失败: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 注销所有快捷键
/// </summary>
public void UnregisterAllHotkeys()
{
try
{
if (_isDisposed)
return;
foreach (var hotkeyName in _registeredHotkeys.Keys)
{
try
{
HotkeyManager.Current.Remove(hotkeyName);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注销快捷键 {hotkeyName} 时出错: {ex.Message}", LogHelper.LogType.Warning);
}
}
_registeredHotkeys.Clear();
// 已注销所有全局快捷键,集合已清空
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注销所有快捷键时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 检查快捷键是否已注册
/// </summary>
/// <param name="hotkeyName">快捷键名称</param>
/// <returns>是否已注册</returns>
public bool IsHotkeyRegistered(string hotkeyName)
{
return _registeredHotkeys.ContainsKey(hotkeyName);
}
/// <summary>
/// 获取已注册的快捷键列表
/// </summary>
/// <returns>快捷键信息列表</returns>
public List<HotkeyInfo> GetRegisteredHotkeys()
{
return new List<HotkeyInfo>(_registeredHotkeys.Values);
}
/// <summary>
/// 获取配置文件中的快捷键信息(不注册,仅用于显示)
/// </summary>
/// <returns>配置文件中的快捷键列表</returns>
public List<HotkeyInfo> GetHotkeysFromConfigFile()
{
try
{
if (!File.Exists(HotkeyConfigFile))
{
LogHelper.WriteLogToFile("快捷键配置文件不存在");
return new List<HotkeyInfo>();
}
// 读取配置文件内容
string jsonContent = File.ReadAllText(HotkeyConfigFile, Encoding.UTF8);
if (string.IsNullOrEmpty(jsonContent))
{
LogHelper.WriteLogToFile("快捷键配置文件为空", LogHelper.LogType.Warning);
return new List<HotkeyInfo>();
}
// 反序列化配置
var config = JsonConvert.DeserializeObject<HotkeyConfig>(jsonContent);
if (config?.Hotkeys == null || config.Hotkeys.Count == 0)
{
LogHelper.WriteLogToFile("快捷键配置为空或格式错误", LogHelper.LogType.Warning);
return new List<HotkeyInfo>();
}
// 转换为HotkeyInfo列表(不注册,仅用于显示)
var hotkeyList = new List<HotkeyInfo>();
foreach (var hotkeyConfig in config.Hotkeys)
{
hotkeyList.Add(new HotkeyInfo
{
Name = hotkeyConfig.Name,
Key = hotkeyConfig.Key,
Modifiers = hotkeyConfig.Modifiers,
Action = null // 不设置动作,仅用于显示
});
}
LogHelper.WriteLogToFile($"从配置文件读取到 {hotkeyList.Count} 个快捷键信息");
return hotkeyList;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从配置文件读取快捷键信息时出错: {ex.Message}", LogHelper.LogType.Error);
return new List<HotkeyInfo>();
}
}
/// <summary>
/// 注册默认快捷键集合
/// </summary>
public void RegisterDefaultHotkeys()
{
try
{
// 开始注册默认快捷键集合
// 基本操作快捷键
RegisterHotkey("Undo", Key.Z, ModifierKeys.Control, () => _mainWindow.SymbolIconUndo_MouseUp(null, null));
RegisterHotkey("Redo", Key.Y, ModifierKeys.Control, () => _mainWindow.SymbolIconRedo_MouseUp(null, null));
RegisterHotkey("Clear", Key.E, ModifierKeys.Control, () => _mainWindow.SymbolIconDelete_MouseUp(null, null));
RegisterHotkey("Paste", Key.V, ModifierKeys.Control, () => _mainWindow.HandleGlobalPaste(null, null));
// 工具切换快捷键
RegisterHotkey("SelectTool", Key.S, ModifierKeys.Alt, () => _mainWindow.SymbolIconSelect_MouseUp(null, null));
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.KeyChangeToQuitDrawTool(null, null));
// 画笔快捷键 - 使用反射访问penType字段
RegisterHotkey("Pen1", Key.D1, ModifierKeys.Alt, () => SwitchToPenType(0));
RegisterHotkey("Pen2", Key.D2, ModifierKeys.Alt, () => SwitchToPenType(1));
RegisterHotkey("Pen3", Key.D3, ModifierKeys.Alt, () => SwitchToPenType(2));
RegisterHotkey("Pen4", Key.D4, ModifierKeys.Alt, () => SwitchToPenType(3));
RegisterHotkey("Pen5", Key.D5, ModifierKeys.Alt, () => SwitchToPenType(4));
// 功能快捷键
RegisterHotkey("DrawLine", Key.L, ModifierKeys.Alt, () => _mainWindow.BtnDrawLine_Click(null, null));
RegisterHotkey("Screenshot", Key.C, ModifierKeys.Alt, () => _mainWindow.SaveScreenShotToDesktop());
RegisterHotkey("Hide", Key.V, ModifierKeys.Alt, () => _mainWindow.SymbolIconEmoji_MouseUp(null, null));
// 退出快捷键
RegisterHotkey("Exit", Key.Escape, ModifierKeys.None, () => _mainWindow.KeyExit(null, null));
// 已注册默认全局快捷键集合
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注册默认快捷键时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 从配置文件加载快捷键
/// </summary>
public void LoadHotkeysFromSettings()
{
try
{
// 开始从配置文件加载快捷键设置
// 检查是否应该注册快捷键
if (!_hotkeysShouldBeRegistered)
{
// 当前状态不允许注册快捷键,跳过加载
return;
}
// 如果配置文件不存在,先创建默认配置文件
if (!File.Exists(HotkeyConfigFile))
{
LogHelper.WriteLogToFile($"快捷键配置文件不存在: {HotkeyConfigFile}", LogHelper.LogType.Warning);
LogHelper.WriteLogToFile("配置文件不存在,创建默认配置文件并注册默认快捷键");
CreateDefaultConfigFile();
RegisterDefaultHotkeys();
_hotkeysShouldBeRegistered = true;
return;
}
// 尝试从配置文件加载
if (LoadHotkeysFromConfigFile())
{
// 成功从配置文件加载快捷键设置
_hotkeysShouldBeRegistered = true;
LogHelper.WriteLogToFile("成功从配置文件加载快捷键设置");
}
else
{
LogHelper.WriteLogToFile("配置文件存在但加载失败,回退到默认快捷键", LogHelper.LogType.Warning);
RegisterDefaultHotkeys();
_hotkeysShouldBeRegistered = true;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从设置加载快捷键时出错: {ex.Message}", LogHelper.LogType.Error);
// 出错时不自动使用默认快捷键,保持当前状态
}
}
/// <summary>
/// 保存快捷键配置到设置
/// </summary>
public void SaveHotkeysToSettings()
{
try
{
LogHelper.WriteLogToFile("开始保存快捷键配置到配置文件", LogHelper.LogType.Event);
if (SaveHotkeysToConfigFile())
{
LogHelper.WriteLogToFile("快捷键配置已成功保存到配置文件", LogHelper.LogType.Event);
}
else
{
LogHelper.WriteLogToFile("保存快捷键配置失败", LogHelper.LogType.Error);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存快捷键配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 启用快捷键注册功能
/// 调用此方法后,快捷键将被允许注册
/// </summary>
public void EnableHotkeyRegistration()
{
try
{
if (!_hotkeysShouldBeRegistered)
{
_hotkeysShouldBeRegistered = true;
LogHelper.WriteLogToFile("启用快捷键注册功能");
// 立即加载快捷键设置
LoadHotkeysFromSettings();
}
else
{
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启用快捷键注册功能时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 禁用快捷键注册功能
/// 调用此方法后,快捷键将被注销
/// </summary>
public void DisableHotkeyRegistration()
{
try
{
if (_hotkeysShouldBeRegistered)
{
_hotkeysShouldBeRegistered = false;
// 注销所有快捷键
UnregisterAllHotkeys();
}
else
{
LogHelper.WriteLogToFile("快捷键注册功能已经禁用");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"禁用快捷键注册功能时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 根据当前工具模式更新快捷键状态
/// 在工具切换时调用此方法
/// </summary>
/// <param name="isMouseMode">是否为鼠标模式(选择模式)</param>
public void UpdateHotkeyStateForToolMode(bool isMouseMode)
{
try
{
if (isMouseMode)
{
// 检查设置中是否允许在鼠标模式下启用快捷键
if (MainWindow.Settings.Appearance.EnableHotkeysInMouseMode)
{
// 如果设置允许,则在鼠标模式下也启用快捷键
EnableHotkeyRegistration();
LogHelper.WriteLogToFile("切换到鼠标模式,但根据设置保持快捷键启用");
}
else
{
// 鼠标模式下禁用快捷键,让键盘操作放行
DisableHotkeyRegistration();
}
}
else
{
// 非鼠标模式下启用快捷键
EnableHotkeyRegistration();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新快捷键状态时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新快捷键配置
/// </summary>
/// <param name="hotkeyName">快捷键名称</param>
/// <param name="key">新按键</param>
/// <param name="modifiers">新修饰键</param>
/// <returns>是否更新成功</returns>
public bool UpdateHotkey(string hotkeyName, Key key, ModifierKeys modifiers)
{
try
{
if (!_registeredHotkeys.ContainsKey(hotkeyName))
{
LogHelper.WriteLogToFile($"快捷键 {hotkeyName} 不存在,无法更新", LogHelper.LogType.Warning);
return false;
}
// 获取原有的动作
var originalAction = _registeredHotkeys[hotkeyName].Action;
// 注销原有快捷键
UnregisterHotkey(hotkeyName);
// 注册新的快捷键
var success = RegisterHotkey(hotkeyName, key, modifiers, originalAction);
if (success)
{
LogHelper.WriteLogToFile($"成功更新快捷键 {hotkeyName}: {modifiers}+{key}", LogHelper.LogType.Event);
// 自动保存配置
SaveHotkeysToSettings();
}
return success;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新快捷键 {hotkeyName} 时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
#endregion
#region Private Helper Methods
/// <summary>
/// 切换到指定笔类型
/// </summary>
/// <param name="penTypeIndex">笔类型索引</param>
private void SwitchToPenType(int penTypeIndex)
{
try
{
// 通过反射访问主窗口的penType字段
var penTypeField = _mainWindow.GetType().GetField("penType",
BindingFlags.NonPublic | BindingFlags.Instance);
if (penTypeField != null)
{
penTypeField.SetValue(_mainWindow, penTypeIndex);
// 调用CheckPenTypeUIState方法更新UI状态
var checkPenTypeMethod = _mainWindow.GetType().GetMethod("CheckPenTypeUIState",
BindingFlags.NonPublic | BindingFlags.Instance);
if (checkPenTypeMethod != null)
{
checkPenTypeMethod.Invoke(_mainWindow, null);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换到笔类型{penTypeIndex}时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 确保配置文件存在,如果不存在则创建
/// </summary>
private void EnsureConfigFileExists()
{
try
{
// 如果配置文件不存在,创建默认配置文件
if (!File.Exists(HotkeyConfigFile))
{
LogHelper.WriteLogToFile($"快捷键配置文件不存在,创建默认配置文件: {HotkeyConfigFile}", LogHelper.LogType.Event);
CreateDefaultConfigFile();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"确保快捷键配置文件存在时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 创建默认的快捷键配置文件
/// </summary>
private void CreateDefaultConfigFile()
{
try
{
// 确保配置目录存在
string configDir = Path.GetDirectoryName(HotkeyConfigFile);
if (!Directory.Exists(configDir))
{
Directory.CreateDirectory(configDir);
}
// 创建默认配置对象
var config = new HotkeyConfig
{
Version = "1.0",
LastModified = DateTime.Now,
Hotkeys = new List<HotkeyConfigItem>()
};
// 添加默认快捷键配置
config.Hotkeys.AddRange(new[]
{
new HotkeyConfigItem { Name = "Undo", Key = Key.Z, Modifiers = ModifierKeys.Control },
new HotkeyConfigItem { Name = "Redo", Key = Key.Y, Modifiers = ModifierKeys.Control },
new HotkeyConfigItem { Name = "Clear", Key = Key.E, Modifiers = ModifierKeys.Control },
new HotkeyConfigItem { Name = "Paste", Key = Key.V, Modifiers = ModifierKeys.Control },
new HotkeyConfigItem { Name = "SelectTool", Key = Key.S, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "DrawTool", Key = Key.D, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "EraserTool", Key = Key.E, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "BlackboardTool", Key = Key.B, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "QuitDrawTool", Key = Key.Q, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Pen1", Key = Key.D1, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Pen2", Key = Key.D2, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Pen3", Key = Key.D3, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Pen4", Key = Key.D4, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Pen5", Key = Key.D5, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "DrawLine", Key = Key.L, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Screenshot", Key = Key.C, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Hide", Key = Key.V, Modifiers = ModifierKeys.Alt },
new HotkeyConfigItem { Name = "Exit", Key = Key.Escape, Modifiers = ModifierKeys.None }
});
// 序列化为JSON
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented
};
string jsonContent = JsonConvert.SerializeObject(config, settings);
// 写入配置文件
File.WriteAllText(HotkeyConfigFile, jsonContent, Encoding.UTF8);
LogHelper.WriteLogToFile($"已创建默认快捷键配置文件: {HotkeyConfigFile}", LogHelper.LogType.Event);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"创建默认快捷键配置文件时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 从配置文件加载快捷键设置
/// </summary>
/// <returns>是否加载成功</returns>
private bool LoadHotkeysFromConfigFile()
{
try
{
if (!File.Exists(HotkeyConfigFile))
{
LogHelper.WriteLogToFile($"快捷键配置文件不存在: {HotkeyConfigFile}", LogHelper.LogType.Warning);
return false;
}
// 读取配置文件内容
string jsonContent = File.ReadAllText(HotkeyConfigFile, Encoding.UTF8);
if (string.IsNullOrEmpty(jsonContent))
{
LogHelper.WriteLogToFile("快捷键配置文件为空", LogHelper.LogType.Warning);
return false;
}
// 反序列化配置
var config = JsonConvert.DeserializeObject<HotkeyConfig>(jsonContent);
if (config?.Hotkeys == null || config.Hotkeys.Count == 0)
{
LogHelper.WriteLogToFile("快捷键配置为空或格式错误", LogHelper.LogType.Warning);
return false;
}
// 注册配置中的快捷键
int successCount = 0;
foreach (var hotkeyConfig in config.Hotkeys)
{
try
{
// 根据快捷键名称获取对应的动作
var action = GetActionByName(hotkeyConfig.Name);
if (action != null)
{
if (RegisterHotkey(hotkeyConfig.Name, hotkeyConfig.Key, hotkeyConfig.Modifiers, action))
{
successCount++;
}
}
else
{
LogHelper.WriteLogToFile($"未找到快捷键 {hotkeyConfig.Name} 对应的动作", LogHelper.LogType.Warning);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"注册快捷键 {hotkeyConfig.Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
LogHelper.WriteLogToFile($"成功加载 {successCount}/{config.Hotkeys.Count} 个快捷键配置", LogHelper.LogType.Event);
if (successCount > 0)
{
_hotkeysShouldBeRegistered = true;
}
return successCount > 0;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从配置文件加载快捷键时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 保存快捷键配置到配置文件
/// </summary>
/// <returns>是否保存成功</returns>
private bool SaveHotkeysToConfigFile()
{
try
{
// 确保配置目录存在
string configDir = Path.GetDirectoryName(HotkeyConfigFile);
if (!Directory.Exists(configDir))
{
Directory.CreateDirectory(configDir);
}
// 创建配置对象
var config = new HotkeyConfig
{
Version = "1.0",
LastModified = DateTime.Now,
Hotkeys = new List<HotkeyConfigItem>()
};
// 添加所有已注册的快捷键
foreach (var hotkey in _registeredHotkeys.Values)
{
config.Hotkeys.Add(new HotkeyConfigItem
{
Name = hotkey.Name,
Key = hotkey.Key,
Modifiers = hotkey.Modifiers
});
}
// 序列化为JSON
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented
};
string jsonContent = JsonConvert.SerializeObject(config, settings);
// 直接写入原文件,覆盖原有内容
File.WriteAllText(HotkeyConfigFile, jsonContent, Encoding.UTF8);
LogHelper.WriteLogToFile($"快捷键配置已保存到: {HotkeyConfigFile}", LogHelper.LogType.Event);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存快捷键配置到配置文件时出错: {ex.Message}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 根据快捷键名称获取对应的动作
/// </summary>
/// <param name="hotkeyName">快捷键名称</param>
/// <returns>对应的动作,如果不存在则返回null</returns>
private Action GetActionByName(string hotkeyName)
{
try
{
switch (hotkeyName)
{
case "Undo":
return () => _mainWindow.SymbolIconUndo_MouseUp(null, null);
case "Redo":
return () => _mainWindow.SymbolIconRedo_MouseUp(null, null);
case "Clear":
return () => _mainWindow.SymbolIconDelete_MouseUp(null, null);
case "Paste":
return () => _mainWindow.HandleGlobalPaste(null, null);
case "SelectTool":
return () => _mainWindow.SymbolIconSelect_MouseUp(null, null);
case "DrawTool":
return () => _mainWindow.PenIcon_Click(null, null);
case "EraserTool":
return () => _mainWindow.EraserIcon_Click(null, null);
case "BlackboardTool":
return () => _mainWindow.ImageBlackboard_MouseUp(null, null);
case "QuitDrawTool":
return () => _mainWindow.KeyChangeToQuitDrawTool(null, null);
case "Pen1":
return () => SwitchToPenType(0);
case "Pen2":
return () => SwitchToPenType(1);
case "Pen3":
return () => SwitchToPenType(2);
case "Pen4":
return () => SwitchToPenType(3);
case "Pen5":
return () => SwitchToPenType(4);
case "DrawLine":
return () => _mainWindow.BtnDrawLine_Click(null, null);
case "Screenshot":
return () => _mainWindow.SaveScreenShotToDesktop();
case "Hide":
return () => _mainWindow.SymbolIconEmoji_MouseUp(null, null);
case "Exit":
return () => _mainWindow.KeyExit(null, null);
default:
LogHelper.WriteLogToFile($"未知的快捷键名称: {hotkeyName}", LogHelper.LogType.Warning);
return null;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取快捷键 {hotkeyName} 对应动作时出错: {ex.Message}", LogHelper.LogType.Error);
return null;
}
}
/// <summary>
/// 检查当前是否处于鼠标模式(选择模式)
/// </summary>
/// <returns>如果处于鼠标模式则返回true(不应该注册快捷键),否则返回false(应该注册快捷键)</returns>
private bool IsInSelectMode()
{
try
{
// 通过反射访问主窗口的FloatingbarSelectionBG字段
var floatingbarSelectionBGField = _mainWindow.GetType().GetField("FloatingbarSelectionBG",
BindingFlags.NonPublic | BindingFlags.Instance);
if (floatingbarSelectionBGField != null)
{
var floatingbarSelectionBG = floatingbarSelectionBGField.GetValue(_mainWindow);
if (floatingbarSelectionBG != null)
{
// 检查高光是否可见
var visibilityProperty = floatingbarSelectionBG.GetType().GetProperty("Visibility");
if (visibilityProperty != null)
{
var visibility = visibilityProperty.GetValue(floatingbarSelectionBG);
if (visibility != null && visibility.ToString() == "Hidden")
{
// 高光隐藏,说明没有选中任何工具,此时应该注销快捷键以释放系统快捷键
return true; // 返回true表示应该注销快捷键
}
}
// 通过反射访问Canvas.GetLeft方法来获取高光位置
var canvasType = Type.GetType("System.Windows.Controls.Canvas, PresentationFramework");
if (canvasType != null)
{
var getLeftMethod = canvasType.GetMethod("GetLeft", BindingFlags.Public | BindingFlags.Static);
if (getLeftMethod != null)
{
var leftPosition = getLeftMethod.Invoke(null, new[] { floatingbarSelectionBG });
if (leftPosition != null)
{
var position = Convert.ToDouble(leftPosition);
// 根据高光位置判断当前选中的工具
// 位置计算基于SetFloatingBarHighlightPosition方法中的逻辑
bool isMouseMode;
// 简化判断:如果位置接近0,说明是鼠标模式
// 如果位置接近28,说明是批注模式
// 如果位置更大,说明是其他工具
if (position < 5) // 鼠标模式:marginOffset + (cursorWidth - actualHighlightWidth) / 2 ≈ 0
{
isMouseMode = true;
}
else if (position < 35) // 批注模式:marginOffset + cursorWidth + (penWidth - actualHighlightWidth) / 2 ≈ 28
{
isMouseMode = false;
}
else // 其他工具(橡皮擦、选择等)
{
isMouseMode = false;
}
return isMouseMode;
}
}
}
}
}
// 如果无法获取高光状态,则回退到inkCanvas.EditingMode判断
// 通过反射访问主窗口的inkCanvas字段
var inkCanvasField = _mainWindow.GetType().GetField("inkCanvas",
BindingFlags.NonPublic | BindingFlags.Instance);
if (inkCanvasField != null)
{
var inkCanvas = inkCanvasField.GetValue(_mainWindow);
if (inkCanvas != null)
{
// 通过反射访问inkCanvas的EditingMode属性
var editingModeProperty = inkCanvas.GetType().GetProperty("EditingMode");
if (editingModeProperty != null)
{
var editingMode = editingModeProperty.GetValue(inkCanvas);
if (editingMode != null)
{
// 检查是否为批注模式
var isInkMode = editingMode.ToString().Contains("Ink");
var isSelectMode = editingMode.ToString().Contains("Select");
// 如果是批注模式或选择模式,则应该注册快捷键(返回false)
// 如果是橡皮擦模式或其他模式,则不应该注册快捷键(返回true)
var shouldNotRegisterHotkeys = !isInkMode && !isSelectMode;
return shouldNotRegisterHotkeys;
}
}
}
}
// 如果无法获取任何状态信息,则回退到原来的判断逻辑
// 通过反射访问主窗口的currentMode字段(作为最后的备用方案)
var currentModeField = _mainWindow.GetType().GetField("currentMode",
BindingFlags.NonPublic | BindingFlags.Instance);
if (currentModeField != null)
{
var currentMode = currentModeField.GetValue(_mainWindow);
if (currentMode != null)
{
var modeValue = currentMode.ToString();
// 注意:这里的逻辑需要修正
// currentMode == 0 表示屏幕模式(PPT放映),此时应该允许快捷键
// currentMode == 1 表示黑板/白板模式,此时也应该允许快捷键
var isSelectMode = false; // 修正:所有模式都应该允许快捷键
return isSelectMode;
}
}
return false; // 默认允许快捷键
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检查鼠标模式状态时出错: {ex.Message}", LogHelper.LogType.Warning);
return false; // 出错时默认允许快捷键
}
}
#endregion
#region IDisposable Implementation
public void Dispose()
{
if (!_isDisposed)
{
_isDisposed = true;
}
}
#endregion
#region Nested Classes
/// <summary>
/// 快捷键信息类
/// </summary>
public class HotkeyInfo
{
public string Name { get; set; }
public Key Key { get; set; }
public ModifierKeys Modifiers { get; set; }
public Action Action { get; set; }
public override string ToString()
{
var modifiersText = Modifiers == ModifierKeys.None ? "" : $"{Modifiers}+";
return $"{modifiersText}{Key}";
}
}
/// <summary>
/// 快捷键配置类
/// </summary>
private class HotkeyConfig
{
public string Version { get; set; }
public DateTime LastModified { get; set; }
public List<HotkeyConfigItem> Hotkeys { get; set; }
}
/// <summary>
/// 快捷键配置项类
/// </summary>
private class HotkeyConfigItem
{
public string Name { get; set; }
public Key Key { get; set; }
public ModifierKeys Modifiers { get; set; }
}
#endregion
}
}
@@ -16,7 +16,6 @@ namespace Ink_Canvas.Helpers
{
private readonly RenderTargetBitmap _renderTarget;
private readonly DrawingVisual _drawingVisual;
private readonly DrawingContext _drawingContext;
private bool _isInitialized;
public HardwareAcceleratedInkProcessor(int width = 1920, int height = 1080)
@@ -24,11 +23,11 @@ namespace Ink_Canvas.Helpers
// 创建硬件加速的渲染目标
_renderTarget = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
_drawingVisual = new DrawingVisual();
// 启用硬件加速
RenderOptions.SetBitmapScalingMode(_drawingVisual, BitmapScalingMode.HighQuality);
RenderOptions.SetEdgeMode(_drawingVisual, EdgeMode.Aliased);
_isInitialized = true;
}
@@ -46,10 +45,10 @@ namespace Ink_Canvas.Helpers
{
// 使用PathGeometry进行硬件加速的曲线拟合
var pathGeometry = CreateSmoothPathGeometry(originalStroke.StylusPoints);
// 将PathGeometry转换回StylusPoint集合
var smoothedPoints = ConvertPathGeometryToStylusPoints(pathGeometry, originalStroke.StylusPoints);
return new Stroke(new StylusPointCollection(smoothedPoints))
{
DrawingAttributes = originalStroke.DrawingAttributes.Clone()
@@ -69,22 +68,22 @@ namespace Ink_Canvas.Helpers
{
var pathGeometry = new PathGeometry();
var pathFigure = new PathFigure();
if (points.Count < 2) return pathGeometry;
pathFigure.StartPoint = new Point(points[0].X, points[0].Y);
// 使用贝塞尔曲线段创建平滑路径
for (int i = 0; i < points.Count - 1; i += 3)
// 使用贝塞尔曲线段创建平滑路径,增加插点密度
for (int i = 0; i < points.Count - 1; i += 2) // 从i+=3改为i+=2,增加插点密度
{
var p1 = i + 1 < points.Count ? new Point(points[i + 1].X, points[i + 1].Y) : pathFigure.StartPoint;
var p2 = i + 2 < points.Count ? new Point(points[i + 2].X, points[i + 2].Y) : p1;
var p3 = i + 3 < points.Count ? new Point(points[i + 3].X, points[i + 3].Y) : p2;
var bezierSegment = new BezierSegment(p1, p2, p3, true);
pathFigure.Segments.Add(bezierSegment);
}
pathGeometry.Figures.Add(pathFigure);
return pathGeometry;
}
@@ -96,11 +95,11 @@ namespace Ink_Canvas.Helpers
{
var result = new List<StylusPoint>();
var flattened = pathGeometry.GetFlattenedPathGeometry();
foreach (var figure in flattened.Figures)
{
result.Add(new StylusPoint(figure.StartPoint.X, figure.StartPoint.Y, 0.5f));
foreach (var segment in figure.Segments)
{
if (segment is LineSegment lineSegment)
@@ -116,10 +115,10 @@ namespace Ink_Canvas.Helpers
}
}
}
// 保持原始压感信息
InterpolatePressure(result, originalPoints);
return result;
}
@@ -129,13 +128,13 @@ namespace Ink_Canvas.Helpers
private void InterpolatePressure(List<StylusPoint> smoothedPoints, StylusPointCollection originalPoints)
{
if (originalPoints.Count == 0 || smoothedPoints.Count == 0) return;
for (int i = 0; i < smoothedPoints.Count; i++)
{
double ratio = (double)i / (smoothedPoints.Count - 1);
int originalIndex = (int)(ratio * (originalPoints.Count - 1));
originalIndex = Math.Max(0, Math.Min(originalIndex, originalPoints.Count - 1));
var point = smoothedPoints[i];
float pressure = originalPoints[originalIndex].PressureFactor;
smoothedPoints[i] = new StylusPoint(point.X, point.Y, Math.Max(pressure, 0.1f));
@@ -145,26 +144,26 @@ namespace Ink_Canvas.Helpers
/// <summary>
/// 使用GPU加速的并行贝塞尔计算
/// </summary>
public static StylusPoint[] ParallelBezierInterpolation(StylusPoint[] controlPoints, int segments = 16)
public static StylusPoint[] ParallelBezierInterpolation(StylusPoint[] controlPoints, int segments = 32)
{
if (controlPoints.Length < 4) return controlPoints;
var result = new StylusPoint[segments * (controlPoints.Length / 4)];
Parallel.For(0, controlPoints.Length / 4, segmentIndex =>
{
var p0 = controlPoints[segmentIndex * 4];
var p1 = controlPoints[segmentIndex * 4 + 1];
var p2 = controlPoints[segmentIndex * 4 + 2];
var p3 = controlPoints[segmentIndex * 4 + 3];
for (int i = 0; i < segments; i++)
{
double t = (double)i / (segments - 1);
result[segmentIndex * segments + i] = CubicBezierFast(p0, p1, p2, p3, t);
}
});
return result;
}
@@ -178,11 +177,11 @@ namespace Ink_Canvas.Helpers
double uu = u * u;
double uuu = uu * u;
double ttt = tt * t;
double x = uuu * p0.X + 3 * uu * t * p1.X + 3 * u * tt * p2.X + ttt * p3.X;
double y = uuu * p0.Y + 3 * uu * t * p1.Y + 3 * u * tt * p2.Y + ttt * p3.Y;
float pressure = (float)(p1.PressureFactor * u + p2.PressureFactor * t);
return new StylusPoint(x, y, Math.Max(pressure, 0.1f));
}
@@ -191,67 +190,10 @@ namespace Ink_Canvas.Helpers
/// </summary>
public void Dispose()
{
_drawingContext?.Close();
_renderTarget?.Clear();
_isInitialized = false;
}
}
/// <summary>
/// 质量配置枚举
/// </summary>
public enum InkSmoothingQuality
{
HighPerformance = 0, // 高性能低质量
Balanced = 1, // 平衡
HighQuality = 2 // 高质量低性能
}
/// <summary>
/// 墨迹平滑配置
/// </summary>
public class InkSmoothingConfig
{
public InkSmoothingQuality Quality { get; set; } = InkSmoothingQuality.Balanced;
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.6;
public double ResampleInterval { get; set; } = 1.2;
public int InterpolationSteps { get; set; } = 16;
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 = 8;
break;
case InkSmoothingQuality.Balanced:
SmoothingStrength = 0.6;
ResampleInterval = 1.2;
InterpolationSteps = 16;
break;
case InkSmoothingQuality.HighQuality:
SmoothingStrength = 0.8;
ResampleInterval = 0.8;
InterpolationSteps = 32;
break;
}
}
}
}
+5 -6
View File
@@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Reflection;
using System.Windows;
namespace Ink_Canvas.Helpers
{
@@ -13,7 +12,7 @@ namespace Ink_Canvas.Helpers
{
private static readonly string[] RequiredDlls = {
"IACore.dll",
"IALoader.dll",
"IALoader.dll",
"IAWinFX.dll"
};
@@ -30,7 +29,7 @@ namespace Ink_Canvas.Helpers
foreach (string dllName in RequiredDlls)
{
string targetPath = Path.Combine(appDirectory, dllName);
// 检查文件是否已存在且有效
if (File.Exists(targetPath) && IsValidDll(targetPath))
{
@@ -109,7 +108,7 @@ namespace Ink_Canvas.Helpers
return false;
FileInfo fileInfo = new FileInfo(filePath);
// 检查文件大小(空文件或过小的文件可能无效)
if (fileInfo.Length < 1024) // 小于1KB可能无效
return false;
@@ -140,11 +139,11 @@ namespace Ink_Canvas.Helpers
try
{
string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
foreach (string dllName in RequiredDlls)
{
string filePath = Path.Combine(appDirectory, dllName);
if (File.Exists(filePath))
{
try
@@ -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
{
/// <summary>
/// 改进的三次贝塞尔曲线平滑算法
/// </summary>
public class ImprovedBezierSmoothing
{
private readonly InkSmoothingConfig _config;
public ImprovedBezierSmoothing(InkSmoothingConfig config = null)
{
_config = config ?? new InkSmoothingConfig();
}
/// <summary>
/// 使用改进的贝塞尔曲线算法平滑笔画
/// </summary>
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()
};
}
/// <summary>
/// 去除噪声点
/// </summary>
private StylusPoint[] RemoveNoisePoints(StylusPoint[] points)
{
if (points.Length < 3) return points;
var result = new List<StylusPoint> { 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();
}
/// <summary>
/// 检查是否为异常点
/// </summary>
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;
}
/// <summary>
/// 应用三次贝塞尔曲线平滑
/// </summary>
private StylusPoint[] ApplyCubicBezierSmoothing(StylusPoint[] points)
{
if (points.Length < 4) return points;
var result = new List<StylusPoint>();
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();
}
/// <summary>
/// 计算最优控制点
/// </summary>
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);
}
/// <summary>
/// 计算切线方向
/// </summary>
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;
}
/// <summary>
/// 计算两点间距离
/// </summary>
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);
}
/// <summary>
/// 计算插值步数
/// </summary>
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));
}
/// <summary>
/// 计算曲率
/// </summary>
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
}
/// <summary>
/// 计算贝塞尔曲线上的点
/// </summary>
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);
}
/// <summary>
/// 后处理点集
/// </summary>
private StylusPoint[] PostProcessPoints(StylusPoint[] points)
{
if (points.Length == 0) return points;
// 如果点数过多,进行重采样
if (points.Length > _config.MaxPointsPerStroke)
{
return ResamplePoints(points, _config.ResampleInterval);
}
return points;
}
/// <summary>
/// 重采样点集
/// </summary>
private StylusPoint[] ResamplePoints(StylusPoint[] points, double interval)
{
var result = new List<StylusPoint> { 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();
}
}
}
+892
View File
@@ -0,0 +1,892 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 墨迹渐隐管理器 - 管理墨迹的渐隐动画和状态
/// </summary>
public class InkFadeManager
{
#region Properties
/// <summary>
/// 是否启用墨迹渐隐功能
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 墨迹渐隐时间(毫秒)
/// </summary>
public int FadeTime { get; set; } = 3000;
/// <summary>
/// 渐隐动画持续时间(毫秒)
/// </summary>
public int AnimationDuration { get; set; } = 1000;
#endregion
#region Private Fields
private readonly MainWindow _mainWindow;
private readonly Dispatcher _dispatcher;
private readonly Dictionary<Stroke, DispatcherTimer> _fadeTimers;
private readonly Dictionary<Stroke, UIElement> _strokeVisuals;
private readonly Dictionary<Stroke, Point> _strokeStartPoints;
private readonly Dictionary<Stroke, Point> _strokeEndPoints;
#endregion
#region Constructor
public InkFadeManager(MainWindow mainWindow)
{
_mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow));
_dispatcher = _mainWindow.Dispatcher;
_fadeTimers = new Dictionary<Stroke, DispatcherTimer>();
_strokeVisuals = new Dictionary<Stroke, UIElement>();
_strokeStartPoints = new Dictionary<Stroke, Point>();
_strokeEndPoints = new Dictionary<Stroke, Point>();
}
#endregion
#region Public Methods
/// <summary>
/// 添加需要渐隐的墨迹
/// </summary>
/// <param name="stroke">墨迹对象</param>
/// <param name="startPoint">落笔点</param>
/// <param name="endPoint">抬笔点</param>
public void AddFadingStroke(Stroke stroke, Point startPoint, Point endPoint)
{
if (!IsEnabled || stroke == null)
{
return;
}
try
{
// 记录墨迹的起点和终点
_strokeStartPoints[stroke] = startPoint;
_strokeEndPoints[stroke] = endPoint;
// 创建墨迹的视觉元素(湿墨迹状态)
var strokeVisual = CreateStrokeVisual(stroke);
if (strokeVisual == null) return;
_strokeVisuals[stroke] = strokeVisual;
// 创建定时器,在指定时间后开始渐隐动画
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(FadeTime)
};
timer.Tick += (sender, e) =>
{
StartFadeAnimation(stroke);
timer.Stop();
_fadeTimers.Remove(stroke);
};
_fadeTimers[stroke] = timer;
timer.Start();
// 将视觉元素添加到画布上
_dispatcher.InvokeAsync(() =>
{
try
{
if (_mainWindow.inkCanvas != null)
{
// 将墨迹添加到 inkCanvas 的父容器中,而不是 inkCanvas.Children
// 这样可以避免坐标系统问题
var parent = _mainWindow.inkCanvas.Parent as Panel;
if (parent != null)
{
parent.Children.Add(strokeVisual);
}
else
{
// 如果无法获取父容器,则添加到 inkCanvas.Children
_mainWindow.inkCanvas.Children.Add(strokeVisual);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"添加墨迹视觉元素到画布失败: {ex}", LogHelper.LogType.Error);
}
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"添加渐隐墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 移除墨迹
/// </summary>
/// <param name="stroke">要移除的墨迹</param>
public void RemoveStroke(Stroke stroke)
{
if (stroke == null) return;
try
{
if (_fadeTimers.TryGetValue(stroke, out var timer))
{
timer.Stop();
_fadeTimers.Remove(stroke);
}
if (_strokeVisuals.TryGetValue(stroke, out var visual))
{
_dispatcher.InvokeAsync(() =>
{
try
{
// 从父容器中移除墨迹
var parent = _mainWindow.inkCanvas?.Parent as Panel;
if (parent != null && parent.Children.Contains(visual))
{
parent.Children.Remove(visual);
}
else if (_mainWindow.inkCanvas != null && _mainWindow.inkCanvas.Children.Contains(visual))
{
_mainWindow.inkCanvas.Children.Remove(visual);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从画布移除墨迹视觉元素失败: {ex}", LogHelper.LogType.Error);
}
});
_strokeVisuals.Remove(stroke);
}
_strokeStartPoints.Remove(stroke);
_strokeEndPoints.Remove(stroke);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"移除渐隐墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 清除所有渐隐墨迹
/// </summary>
public void ClearAllFadingStrokes()
{
try
{
foreach (var timer in _fadeTimers.Values)
{
timer.Stop();
}
_fadeTimers.Clear();
_dispatcher.InvokeAsync(() =>
{
try
{
if (_mainWindow.inkCanvas != null)
{
var parent = _mainWindow.inkCanvas.Parent as Panel;
foreach (var visual in _strokeVisuals.Values)
{
if (parent != null && parent.Children.Contains(visual))
{
parent.Children.Remove(visual);
}
else if (_mainWindow.inkCanvas.Children.Contains(visual))
{
_mainWindow.inkCanvas.Children.Remove(visual);
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除所有墨迹视觉元素失败: {ex}", LogHelper.LogType.Error);
}
});
_strokeVisuals.Clear();
_strokeStartPoints.Clear();
_strokeEndPoints.Clear();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除所有渐隐墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 更新渐隐时间设置
/// </summary>
/// <param name="fadeTime">新的渐隐时间(毫秒)</param>
public void UpdateFadeTime(int fadeTime)
{
FadeTime = fadeTime;
foreach (var kvp in _fadeTimers)
{
var stroke = kvp.Key;
var timer = kvp.Value;
timer.Stop();
timer.Interval = TimeSpan.FromMilliseconds(FadeTime);
timer.Start();
}
}
/// <summary>
/// 启用墨迹渐隐功能
/// </summary>
public void Enable()
{
IsEnabled = true;
LogHelper.WriteLogToFile("墨迹渐隐功能已启用");
}
/// <summary>
/// 禁用墨迹渐隐功能
/// </summary>
public void Disable()
{
IsEnabled = false;
LogHelper.WriteLogToFile("墨迹渐隐功能已禁用");
}
#endregion
#region Private Methods
/// <summary>
/// 创建墨迹的视觉元素
/// </summary>
/// <param name="stroke">墨迹对象</param>
/// <returns>视觉元素</returns>
private UIElement CreateStrokeVisual(Stroke stroke)
{
try
{
// 创建路径几何,使用墨迹的实际位置
var geometry = stroke.GetGeometry();
if (geometry == null)
{
return null;
}
// 获取绘画属性
var drawingAttribs = stroke.DrawingAttributes;
// 创建路径元素,确保使用正确的绘画属性
var path = new Path
{
Data = geometry,
Stroke = new SolidColorBrush(drawingAttribs.Color),
StrokeThickness = drawingAttribs.Width, // 使用原始墨迹的粗细
StrokeStartLineCap = PenLineCap.Round,
StrokeEndLineCap = PenLineCap.Round,
StrokeLineJoin = PenLineJoin.Round,
Fill = drawingAttribs.IsHighlighter ? new SolidColorBrush(drawingAttribs.Color) : null, // 高亮笔需要填充
Opacity = 0.95, // 初始透明度更高,显得更自然
// 优化渲染质量
UseLayoutRounding = false,
SnapsToDevicePixels = false
};
// 如果是高亮笔,调整透明度和混合模式
if (drawingAttribs.IsHighlighter)
{
path.Opacity = 0.4; // 高亮笔初始透明度更低,更符合荧光笔特性
// 为高亮笔添加特殊的混合效果
// 使用更柔和的笔触样式
path.StrokeStartLineCap = PenLineCap.Flat;
path.StrokeEndLineCap = PenLineCap.Flat;
path.StrokeLineJoin = PenLineJoin.Miter;
// 高亮笔通常需要更宽的笔触来覆盖下面的内容
if (drawingAttribs.Width < 20)
{
path.StrokeThickness = Math.Max(drawingAttribs.Width * 1.5, 20);
}
// 为高亮笔添加轻微的模糊效果,使渐隐更加自然
path.Effect = new BlurEffect
{
Radius = 0.5, // 轻微的模糊效果
KernelType = KernelType.Gaussian
};
}
// 不设置任何变换,保持墨迹原有粗细
var bounds = geometry.Bounds;
// 设置墨迹的初始位置
System.Windows.Controls.Canvas.SetLeft(path, bounds.Left);
System.Windows.Controls.Canvas.SetTop(path, bounds.Top);
return path;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// 开始渐隐动画
/// </summary>
/// <param name="stroke">要渐隐的墨迹</param>
private void StartFadeAnimation(Stroke stroke)
{
if (!_strokeVisuals.TryGetValue(stroke, out var visual)) return;
try
{
_dispatcher.InvokeAsync(() =>
{
// 获取当前透明度和判断是否为高亮笔
var currentOpacity = visual.Opacity;
var isHighlighter = stroke.DrawingAttributes.IsHighlighter;
// 根据墨迹类型选择不同的动画效果
if (isHighlighter)
{
StartHighlighterFadeAnimation(visual, stroke, currentOpacity);
}
else
{
StartNormalStrokeFadeAnimation(visual, stroke, currentOpacity);
}
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"开始渐隐动画失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 开始普通墨迹的渐隐动画
/// </summary>
private void StartNormalStrokeFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity)
{
try
{
StartProgressiveFadeAnimation(visual, stroke, currentOpacity, AnimationDuration);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"开始普通墨迹渐隐动画失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 统一渐隐动画 - 整个墨迹作为一个整体进行渐隐,与擦除效果一致
/// </summary>
private void StartUnifiedFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity, int duration)
{
try
{
// 创建透明度动画,模拟擦除时的效果
var fadeAnimation = new DoubleAnimation
{
From = currentOpacity,
To = 0.0,
Duration = TimeSpan.FromMilliseconds(duration),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
};
// 如果是高亮笔,添加轻微的缩放效果,使渐隐更加自然
if (stroke.DrawingAttributes.IsHighlighter)
{
// 创建轻微的缩放动画,模拟墨迹"蒸发"的效果
var scaleAnimation = new DoubleAnimation
{
From = 1.0,
To = 0.95, // 轻微缩小,增加自然感
Duration = TimeSpan.FromMilliseconds(duration),
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseIn }
};
// 创建缩放变换
var scaleTransform = new ScaleTransform();
visual.RenderTransform = scaleTransform;
visual.RenderTransformOrigin = new Point(0.5, 0.5);
// 应用缩放动画
scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAnimation);
scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAnimation);
}
// 添加动画完成事件
fadeAnimation.Completed += (sender, e) => OnAnimationCompleted(visual, stroke);
// 应用透明度动画
visual.BeginAnimation(UIElement.OpacityProperty, fadeAnimation);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"统一渐隐动画失败: {ex}", LogHelper.LogType.Error);
OnAnimationCompleted(visual, stroke);
}
}
/// <summary>
/// 开始高亮笔的渐隐动画
/// </summary>
private void StartHighlighterFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity)
{
try
{
// 高亮笔使用统一的渐隐动画,与擦除效果一致
StartUnifiedFadeAnimation(visual, stroke, currentOpacity, (int)(AnimationDuration * 1.2));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"开始高亮笔渐隐动画失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 渐进式渐隐动画 - 从起点到终点逐渐消失
/// </summary>
private void StartProgressiveFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity, int duration)
{
try
{
// 确保所有墨迹都能显示动画,包括短墨迹
if (stroke.StylusPoints.Count < 2)
{
// 只有1个点的墨迹也使用分段动画,确保视觉效果
CreateSegmentedStroke(visual, stroke, currentOpacity, duration);
return;
}
// 将墨迹分段并创建多个 Path
CreateSegmentedStroke(visual, stroke, currentOpacity, duration);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"渐进式渐隐动画失败: {ex}", LogHelper.LogType.Error);
// 失败时回退到简单动画
StartSimpleFadeAnimation(visual, stroke, currentOpacity, duration);
}
}
/// <summary>
/// 创建分段墨迹并开始渐进消失
/// </summary>
private void CreateSegmentedStroke(UIElement originalVisual, Stroke stroke, double opacity, int duration)
{
try
{
var stylusPoints = stroke.StylusPoints;
var totalPoints = stylusPoints.Count;
// 分段算法 - 确保所有墨迹都有足够的动画效果
var strokeLength = CalculateStrokeLength(stylusPoints);
var segmentCount = CalculateOptimalSegmentCount(totalPoints, strokeLength);
// 强制最小分段数量,确保短墨迹也有动画效果
segmentCount = Math.Max(segmentCount, 4);
var pointsPerSegment = Math.Max(1, totalPoints / segmentCount);
// 隐藏原始视觉元素
originalVisual.Visibility = Visibility.Hidden;
var segments = new List<UIElement>();
var parent = _mainWindow.inkCanvas?.Parent as Panel;
if (parent == null)
{
// 如果父容器不是Panel,直接使用InkCanvas
parent = null; // 稍后会检查并使用InkCanvas.Children
}
// 创建各个分段 - 确保短墨迹也能正确分段
for (int i = 0; i < segmentCount; i++)
{
var startIndex = i * pointsPerSegment;
var endIndex = (i == segmentCount - 1) ? totalPoints - 1 : (i + 1) * pointsPerSegment;
// 确保有足够的点来创建分段,对于短墨迹特殊处理
if (endIndex <= startIndex && totalPoints > 1)
{
// 短墨迹:每个点作为一个分段
startIndex = i;
endIndex = Math.Min(i + 1, totalPoints - 1);
}
// 为每个分段添加重叠,确保连接处平滑
var overlap = Math.Max(1, pointsPerSegment / 6); // 15%的重叠,平衡平滑与速度
var actualStartIndex = Math.Max(0, startIndex - overlap);
var actualEndIndex = Math.Min(totalPoints - 1, endIndex + overlap);
var segment = CreateStrokeSegment(stroke, actualStartIndex, actualEndIndex, opacity);
if (segment != null)
{
segments.Add(segment);
if (parent != null)
{
parent.Children.Add(segment);
}
else if (_mainWindow.inkCanvas != null)
{
_mainWindow.inkCanvas.Children.Add(segment);
}
}
}
// 开始分段渐隐动画
StartSegmentedFadeAnimation(segments, stroke, originalVisual, duration);
}
catch (Exception)
{
StartSimpleFadeAnimation(originalVisual, stroke, opacity, duration);
}
}
/// <summary>
/// 创建墨迹分段
/// </summary>
private UIElement CreateStrokeSegment(Stroke originalStroke, int startIndex, int endIndex, double opacity)
{
try
{
// 创建分段的 StylusPoint 集合
var segmentPoints = new StylusPointCollection();
for (int i = startIndex; i <= endIndex && i < originalStroke.StylusPoints.Count; i++)
{
segmentPoints.Add(originalStroke.StylusPoints[i]);
}
if (segmentPoints.Count < 2) return null;
// 创建分段墨迹
var segmentStroke = new Stroke(segmentPoints)
{
DrawingAttributes = originalStroke.DrawingAttributes.Clone()
};
// 创建分段的视觉元素
var geometry = segmentStroke.GetGeometry();
if (geometry == null) return null;
var drawingAttribs = segmentStroke.DrawingAttributes;
var path = new Path
{
Data = geometry,
Stroke = new SolidColorBrush(drawingAttribs.Color),
StrokeThickness = drawingAttribs.Width,
StrokeStartLineCap = drawingAttribs.IsHighlighter ? PenLineCap.Flat : PenLineCap.Round,
StrokeEndLineCap = drawingAttribs.IsHighlighter ? PenLineCap.Flat : PenLineCap.Round,
StrokeLineJoin = drawingAttribs.IsHighlighter ? PenLineJoin.Miter : PenLineJoin.Round,
Fill = drawingAttribs.IsHighlighter ? new SolidColorBrush(drawingAttribs.Color) : null,
Opacity = opacity,
UseLayoutRounding = false,
SnapsToDevicePixels = false
};
// 设置位置
var bounds = geometry.Bounds;
System.Windows.Controls.Canvas.SetLeft(path, bounds.Left);
System.Windows.Controls.Canvas.SetTop(path, bounds.Top);
return path;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// 开始分段渐隐动画
/// </summary>
private void StartSegmentedFadeAnimation(List<UIElement> segments, Stroke originalStroke, UIElement originalVisual, int totalDuration)
{
try
{
// 动画时序算法
var segmentDuration = CalculateOptimalSegmentDuration(totalDuration, segments.Count);
var animationCurve = CreateAppleStyleAnimationCurve(segments.Count, totalDuration);
// 跟踪动画完成状态
var completedSegments = new HashSet<UIElement>();
var totalSegments = segments.Count;
// 渐隐效果 - 使用自然的动画曲线
for (int i = 0; i < segments.Count; i++)
{
var segment = segments[i];
// 使用预计算的动画曲线获取延迟时间
var delay = animationCurve[i];
// 使用定时器延迟启动每个分段的动画
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(delay)
};
int segmentIndex = i; // 捕获当前索引
timer.Tick += (sender, e) =>
{
StartSingleSegmentFadeAnimation(segment, segmentDuration, () =>
{
// 动画完成回调
lock (completedSegments)
{
completedSegments.Add(segment);
// 检查是否所有分段都完成了
if (completedSegments.Count >= totalSegments)
{
CleanupSegmentedAnimation(segments, originalStroke, originalVisual);
}
}
});
timer.Stop();
};
timer.Start();
}
// 设置一个安全超时定时器,防止无限等待
var safetyTimeout = totalDuration + (segments.Count * segmentDuration) + 1200; // 额外1.2秒缓冲,确保动画完整
var safetyTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(safetyTimeout)
};
safetyTimer.Tick += (sender, e) =>
{
CleanupSegmentedAnimation(segments, originalStroke, originalVisual);
safetyTimer.Stop();
};
safetyTimer.Start();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"分段渐隐动画失败: {ex}", LogHelper.LogType.Error);
CleanupSegmentedAnimation(segments, originalStroke, originalVisual);
}
}
/// <summary>
/// 单个分段的渐隐动画
/// </summary>
private void StartSingleSegmentFadeAnimation(UIElement segment, int duration, Action onCompleted = null)
{
try
{
// 只使用透明度动画,保持墨迹原有粗细
var fadeAnimation = new DoubleAnimation
{
From = segment.Opacity,
To = 0.0,
Duration = TimeSpan.FromMilliseconds(duration),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut } // 更平滑的缓动
};
// 添加动画完成事件
if (onCompleted != null)
{
fadeAnimation.Completed += (sender, e) =>
{
onCompleted?.Invoke();
};
}
// 只应用透明度动画,不改变墨迹大小
segment.BeginAnimation(UIElement.OpacityProperty, fadeAnimation);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"单个分段渐隐动画失败: {ex}", LogHelper.LogType.Error);
// 即使失败也要调用完成回调
onCompleted?.Invoke();
}
}
/// <summary>
/// 清理分段动画
/// </summary>
private void CleanupSegmentedAnimation(List<UIElement> segments, Stroke originalStroke, UIElement originalVisual)
{
try
{
// 移除所有分段
var parent = _mainWindow.inkCanvas?.Parent as Panel;
foreach (var segment in segments)
{
if (parent != null && parent.Children.Contains(segment))
{
parent.Children.Remove(segment);
}
else if (_mainWindow.inkCanvas != null && _mainWindow.inkCanvas.Children.Contains(segment))
{
_mainWindow.inkCanvas.Children.Remove(segment);
}
}
// 清理原始墨迹
OnAnimationCompleted(originalVisual, originalStroke);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理分段动画失败: {ex}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 简单渐隐动画(备用方案)
/// </summary>
private void StartSimpleFadeAnimation(UIElement visual, Stroke stroke, double currentOpacity, int duration)
{
try
{
var fadeAnimation = new DoubleAnimation
{
From = currentOpacity,
To = 0.0,
Duration = TimeSpan.FromMilliseconds(duration),
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseIn }
};
fadeAnimation.Completed += (sender, e) => OnAnimationCompleted(visual, stroke);
visual.BeginAnimation(UIElement.OpacityProperty, fadeAnimation);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"简单渐隐动画失败: {ex}", LogHelper.LogType.Error);
OnAnimationCompleted(visual, stroke);
}
}
/// <summary>
/// 计算墨迹的实际长度
/// </summary>
private double CalculateStrokeLength(StylusPointCollection points)
{
if (points.Count < 2) return 0;
double totalLength = 0;
for (int i = 1; i < points.Count; i++)
{
var p1 = points[i - 1].ToPoint();
var p2 = points[i].ToPoint();
totalLength += Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
}
return totalLength;
}
/// <summary>
/// 根据墨迹特性计算最优分段数量 - 平衡速度与完整性
/// </summary>
private int CalculateOptimalSegmentCount(int pointCount, double strokeLength)
{
// 平衡速度与完整性,确保动画效果的同时提高速度
const double PIXELS_PER_SEGMENT = 12.0; // 每段适中长度,平衡效果与速度
const int MIN_SEGMENTS = 5; // 适当的最小分段数,确保动画效果
const int MAX_SEGMENTS = 100; // 适中的最大分段数,平衡性能与效果
// 根据长度计算基础分段数
var lengthBasedSegments = Math.Max(MIN_SEGMENTS, (int)(strokeLength / PIXELS_PER_SEGMENT));
// 根据点密度调整,平衡效果与速度
var density = pointCount > 0 ? strokeLength / pointCount : 1;
var densityFactor = Math.Max(0.4, Math.Min(2.5, density / 1.8));
var finalSegments = (int)(lengthBasedSegments * densityFactor);
// 对于短墨迹,确保至少有4个分段
if (pointCount <= 5)
{
finalSegments = Math.Max(finalSegments, 4);
}
// 限制在合理范围内
return Math.Min(MAX_SEGMENTS, Math.Max(MIN_SEGMENTS, finalSegments));
}
/// <summary>
/// 计算最优的单段动画持续时间 - 平衡速度与完整性
/// </summary>
private int CalculateOptimalSegmentDuration(int totalDuration, int segmentCount)
{
// 平衡速度与动画完整性
var baseDuration = totalDuration / Math.Max(segmentCount, 1);
var minDuration = 150; // 每段最少150ms,确保动画完整显示
var maxDuration = 500; // 每段最多500ms,平衡速度与完整性
return Math.Max(minDuration, Math.Min(maxDuration, baseDuration));
}
/// <summary>
/// 创建优化的动画时间曲线 - 平衡速度与完整性
/// </summary>
private int[] CreateAppleStyleAnimationCurve(int segmentCount, int totalDuration)
{
var curve = new int[segmentCount];
// 平衡速度与完整性,确保动画有足够时间播放
var availableTime = totalDuration * 0.6; // 使用60%的总时间,给动画留足够缓冲
var delayBetweenSegments = Math.Max(60, availableTime / Math.Max(segmentCount, 1));
for (int i = 0; i < segmentCount; i++)
{
// 线性延迟,确保每个分段都有足够时间
curve[i] = (int)(i * delayBetweenSegments);
}
return curve;
}
/// <summary>
/// 动画完成后的统一处理
/// </summary>
private void OnAnimationCompleted(UIElement visual, Stroke stroke)
{
try
{
// 从父容器中移除墨迹
var parent = _mainWindow.inkCanvas?.Parent as Panel;
if (parent != null && parent.Children.Contains(visual))
{
parent.Children.Remove(visual);
}
else if (_mainWindow.inkCanvas != null && _mainWindow.inkCanvas.Children.Contains(visual))
{
_mainWindow.inkCanvas.Children.Remove(visual);
}
RemoveStroke(stroke);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"渐隐动画完成后清理墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
#endregion
}
}
+161
View File
@@ -0,0 +1,161 @@
using System;
using System.Diagnostics;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 墨迹平滑配置类
/// </summary>
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 // 高质量低性能
}
/// <summary>
/// 从设置中加载配置
/// </summary>
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)
{
Debug.WriteLine($"加载平滑配置失败: {ex.Message}");
}
return config;
}
/// <summary>
/// 应用质量设置
/// </summary>
public void ApplyQualitySettings()
{
switch (Quality)
{
case SmoothingQuality.Performance:
SmoothingStrength = 0.15;
ResampleInterval = 5.0;
InterpolationSteps = 4;
UseAdaptiveInterpolation = false;
CurveTension = 0.15;
MaxConcurrentTasks = Math.Max(1, Environment.ProcessorCount / 2);
UseHardwareAcceleration = true;
UseAsyncProcessing = true;
break;
case SmoothingQuality.Balanced:
SmoothingStrength = 0.3;
ResampleInterval = 3.0;
InterpolationSteps = 8;
UseAdaptiveInterpolation = true;
CurveTension = 0.25;
MaxConcurrentTasks = Environment.ProcessorCount;
UseHardwareAcceleration = true;
UseAsyncProcessing = true;
break;
case SmoothingQuality.Quality:
SmoothingStrength = 0.5;
ResampleInterval = 2.0;
InterpolationSteps = 15;
UseAdaptiveInterpolation = true;
CurveTension = 0.35;
MaxConcurrentTasks = Environment.ProcessorCount;
UseHardwareAcceleration = true;
UseAsyncProcessing = true;
break;
}
}
/// <summary>
/// 保存配置到设置
/// </summary>
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)
{
Debug.WriteLine($"保存平滑配置失败: {ex.Message}");
}
}
/// <summary>
/// 验证配置参数
/// </summary>
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;
}
/// <summary>
/// 获取配置摘要
/// </summary>
public string GetSummary()
{
return $"质量: {Quality}, 强度: {SmoothingStrength:F2}, 间隔: {ResampleInterval:F1}, " +
$"步数: {InterpolationSteps}, 自适应: {UseAdaptiveInterpolation}, " +
$"张力: {CurveTension:F2}, 硬件加速: {UseHardwareAcceleration}";
}
}
}
+15 -12
View File
@@ -25,7 +25,7 @@ namespace Ink_Canvas.Helpers
_uiDispatcher = uiDispatcher;
_config = InkSmoothingConfig.FromSettings();
_config.ApplyQualitySettings();
_asyncSmoothing = new AsyncAdvancedBezierSmoothing(uiDispatcher)
{
SmoothingStrength = _config.SmoothingStrength,
@@ -34,7 +34,7 @@ namespace Ink_Canvas.Helpers
UseHardwareAcceleration = _config.UseHardwareAcceleration,
MaxConcurrentTasks = _config.MaxConcurrentTasks
};
_hardwareProcessor = new HardwareAcceleratedInkProcessor();
_performanceMonitor = new InkSmoothingPerformanceMonitor();
}
@@ -42,7 +42,7 @@ namespace Ink_Canvas.Helpers
/// <summary>
/// 平滑笔画(自动选择最佳方法)
/// </summary>
public async Task<Stroke> SmoothStrokeAsync(Stroke originalStroke,
public async Task<Stroke> SmoothStrokeAsync(Stroke originalStroke,
Action<Stroke, Stroke> onCompleted = null,
CancellationToken cancellationToken = default)
{
@@ -142,7 +142,7 @@ namespace Ink_Canvas.Helpers
{
var newConfig = InkSmoothingConfig.FromSettings();
newConfig.ApplyQualitySettings();
_asyncSmoothing.SmoothingStrength = newConfig.SmoothingStrength;
_asyncSmoothing.ResampleInterval = newConfig.ResampleInterval;
_asyncSmoothing.InterpolationSteps = newConfig.InterpolationSteps;
@@ -189,33 +189,36 @@ namespace Ink_Canvas.Helpers
public static InkSmoothingConfig GetRecommendedConfig()
{
var config = new InkSmoothingConfig();
// 根据系统性能调整配置
var processorCount = Environment.ProcessorCount;
var isHardwareAccelerated = IsHardwareAccelerationSupported();
if (processorCount >= 8 && isHardwareAccelerated)
if (processorCount >= 4 && isHardwareAccelerated)
{
config.Quality = InkSmoothingQuality.HighQuality;
// 降低高质量模式的门槛,4核以上且支持硬件加速就使用高质量
config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.HighQuality;
config.UseHardwareAcceleration = true;
config.UseAsyncProcessing = true;
config.MaxConcurrentTasks = Math.Min(processorCount, 8);
}
else if (processorCount >= 4)
else if (processorCount >= 2)
{
config.Quality = InkSmoothingQuality.Balanced;
// 2核以上使用平衡模式
config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.Balanced;
config.UseHardwareAcceleration = isHardwareAccelerated;
config.UseAsyncProcessing = true;
config.MaxConcurrentTasks = Math.Min(processorCount, 4);
}
else
{
config.Quality = InkSmoothingQuality.HighPerformance;
// 单核或性能较低的设备使用高性能模式
config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.HighPerformance;
config.UseHardwareAcceleration = false;
config.UseAsyncProcessing = false;
config.MaxConcurrentTasks = 1;
}
config.ApplyQualitySettings();
return config;
}
+10 -5
View File
@@ -5,11 +5,15 @@ using System.Windows.Forms;
using System.Windows.Interop;
using Point = System.Windows.Point;
namespace Ink_Canvas.Helpers {
internal class IsOutsideOfScreenHelper {
public static bool IsOutsideOfScreen(FrameworkElement target) {
namespace Ink_Canvas.Helpers
{
internal class IsOutsideOfScreenHelper
{
public static bool IsOutsideOfScreen(FrameworkElement target)
{
var hwndSource = (HwndSource)PresentationSource.FromVisual(target);
if (hwndSource is null) {
if (hwndSource is null)
{
return true;
}
@@ -19,7 +23,8 @@ namespace Ink_Canvas.Helpers {
var screens = Screen.AllScreens;
return !screens.Any(x => x.Bounds.IntersectsWith(targetBounds));
Rectangle GetPixelBoundsToScreen(FrameworkElement visual) {
Rectangle GetPixelBoundsToScreen(FrameworkElement visual)
{
var pixelBoundsToScreen = Rect.Empty;
pixelBoundsToScreen.Union(visual.PointToScreen(new Point(0, 0)));
pixelBoundsToScreen.Union(visual.PointToScreen(new Point(visual.ActualWidth, 0)));
+10 -10
View File
@@ -33,12 +33,12 @@ namespace Ink_Canvas.Helpers
{
// 检查日志是否启用
if (MainWindow.Settings != null && MainWindow.Settings.Advanced != null && !MainWindow.Settings.Advanced.IsLogEnabled) return;
string strLogType = logType.ToString();
try
{
string file;
// 检查是否启用了日期保存功能
if (MainWindow.Settings != null && MainWindow.Settings.Advanced != null && MainWindow.Settings.Advanced.IsSaveLogByDate)
{
@@ -48,10 +48,10 @@ namespace Ink_Canvas.Helpers
{
Directory.CreateDirectory(logsPath);
}
// 检查Logs文件夹大小,如果超过5MB则清空
CheckAndCleanLogsFolder(logsPath);
// 使用软件启动时间作为日志文件名
file = Path.Combine(logsPath, $"Log_{AppStartTime}.txt");
}
@@ -59,12 +59,12 @@ namespace Ink_Canvas.Helpers
{
file = App.RootPath + LogFile;
}
if (!Directory.Exists(App.RootPath))
{
Directory.CreateDirectory(App.RootPath);
}
var threadId = Thread.CurrentThread.ManagedThreadId;
var callingMethod = new StackTrace(2, true).GetFrame(0);
string callerInfo = "<unknown>";
@@ -92,16 +92,16 @@ namespace Ink_Canvas.Helpers
{
long totalSize = 0;
DirectoryInfo dirInfo = new DirectoryInfo(logsPath);
// 如果目录不存在,直接返回
if (!dirInfo.Exists) return;
// 计算文件夹大小
foreach (FileInfo file in dirInfo.GetFiles())
{
totalSize += file.Length;
}
// 如果超过5MB,清空文件夹
if (totalSize > MaxLogsFolderSizeBytes)
{
@@ -113,7 +113,7 @@ namespace Ink_Canvas.Helpers
}
catch { }
}
// 记录清理操作
string cleanupMessage = $"Logs folder exceeded size limit ({totalSize / 1024.0 / 1024.0:F2} MB > {MaxLogsFolderSizeBytes / 1024.0 / 1024.0:F2} MB). Folder cleaned.";
using (StreamWriter sw = new StreamWriter(Path.Combine(logsPath, $"Log_{AppStartTime}.txt"), true))
+666
View File
@@ -0,0 +1,666 @@
using Microsoft.Office.Interop.PowerPoint;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Ink;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 多PPT墨迹管理器 - 支持多个PPT窗口分别管理墨迹
/// </summary>
public class MultiPPTInkManager : IDisposable
{
#region Properties
public bool IsAutoSaveEnabled { get; set; } = true;
public string AutoSaveLocation { get; set; } = "";
public PPTManager PPTManager { get; set; }
#endregion
#region Private Fields
private readonly Dictionary<string, PPTInkManager> _presentationManagers;
private readonly Dictionary<string, PresentationInfo> _presentationInfos;
private readonly object _lockObject = new object();
private bool _disposed;
private string _currentActivePresentationId = "";
#endregion
#region Constructor
public MultiPPTInkManager()
{
_presentationManagers = new Dictionary<string, PPTInkManager>();
_presentationInfos = new Dictionary<string, PresentationInfo>();
}
#endregion
#region Public Methods
/// <summary>
/// 初始化新的演示文稿
/// </summary>
public void InitializePresentation(Presentation presentation)
{
if (presentation == null) return;
lock (_lockObject)
{
try
{
var presentationId = GeneratePresentationId(presentation);
// 如果已存在该演示文稿的管理器,先清理
if (_presentationManagers.ContainsKey(presentationId))
{
_presentationManagers[presentationId].Dispose();
_presentationManagers.Remove(presentationId);
}
// 创建新的墨迹管理器
var inkManager = new PPTInkManager();
inkManager.IsAutoSaveEnabled = IsAutoSaveEnabled;
inkManager.AutoSaveLocation = AutoSaveLocation;
inkManager.InitializePresentation(presentation);
// 保存管理器和演示文稿信息
_presentationManagers[presentationId] = inkManager;
_presentationInfos[presentationId] = new PresentationInfo
{
Id = presentationId,
Name = presentation.Name,
FullName = presentation.FullName,
SlideCount = presentation.Slides.Count,
CreatedTime = DateTime.Now,
LastAccessTime = DateTime.Now
};
// 设置为当前活跃的演示文稿
_currentActivePresentationId = presentationId;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化多PPT墨迹管理失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 切换到指定的演示文稿
/// </summary>
public bool SwitchToPresentation(Presentation presentation)
{
if (presentation == null) return false;
lock (_lockObject)
{
try
{
var presentationId = GeneratePresentationId(presentation);
if (_presentationManagers.ContainsKey(presentationId))
{
// 如果切换的是不同的演示文稿,先保存当前活跃演示文稿的墨迹
if (!string.IsNullOrEmpty(_currentActivePresentationId) &&
_currentActivePresentationId != presentationId)
{
var currentManager = GetCurrentManager();
if (currentManager != null)
{
// 获取当前活跃的演示文稿并保存墨迹
var currentPresentation = GetCurrentActivePresentation();
if (currentPresentation != null)
{
currentManager.SaveAllStrokesToFile(currentPresentation);
LogHelper.WriteLogToFile($"已保存当前演示文稿墨迹: {currentPresentation.Name}", LogHelper.LogType.Trace);
}
}
}
_currentActivePresentationId = presentationId;
// 更新最后访问时间
if (_presentationInfos.ContainsKey(presentationId))
{
_presentationInfos[presentationId].LastAccessTime = DateTime.Now;
}
if (_currentActivePresentationId != presentationId)
{
LogHelper.WriteLogToFile($"已切换到演示文稿: {presentation.Name}", LogHelper.LogType.Trace);
}
return true;
}
else
{
// 如果不存在,尝试初始化
InitializePresentation(presentation);
return true;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换到演示文稿失败: {ex}", LogHelper.LogType.Error);
return false;
}
}
}
/// <summary>
/// 保存当前页面的墨迹
/// </summary>
public void SaveCurrentSlideStrokes(int slideIndex, StrokeCollection strokes)
{
if (slideIndex <= 0 || strokes == null) return;
lock (_lockObject)
{
try
{
var manager = GetCurrentManager();
if (manager != null)
{
manager.SaveCurrentSlideStrokes(slideIndex, strokes);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存当前页面墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 强制保存指定页面的墨迹(忽略锁定状态)
/// </summary>
public void ForceSaveSlideStrokes(int slideIndex, StrokeCollection strokes)
{
if (slideIndex <= 0 || strokes == null) return;
lock (_lockObject)
{
try
{
var manager = GetCurrentManager();
if (manager != null)
{
manager.ForceSaveSlideStrokes(slideIndex, strokes);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"强制保存页面墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 加载指定页面的墨迹
/// </summary>
public StrokeCollection LoadSlideStrokes(int slideIndex)
{
if (slideIndex <= 0) return new StrokeCollection();
lock (_lockObject)
{
try
{
var manager = GetCurrentManager();
if (manager != null)
{
return manager.LoadSlideStrokes(slideIndex);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载页面墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
return new StrokeCollection();
}
/// <summary>
/// 切换到指定页面并加载墨迹
/// </summary>
public StrokeCollection SwitchToSlide(int slideIndex, StrokeCollection currentStrokes = null)
{
lock (_lockObject)
{
try
{
var manager = GetCurrentManager();
if (manager != null)
{
return manager.SwitchToSlide(slideIndex, currentStrokes);
}
else
{
LogHelper.WriteLogToFile($"无法获取当前墨迹管理器,页面切换失败: {slideIndex}", LogHelper.LogType.Warning);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换页面墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
return new StrokeCollection();
}
/// <summary>
/// 保存所有墨迹到文件
/// </summary>
public void SaveAllStrokesToFile(Presentation presentation)
{
if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation) || presentation == null) return;
lock (_lockObject)
{
try
{
var presentationId = GeneratePresentationId(presentation);
if (_presentationManagers.ContainsKey(presentationId))
{
_presentationManagers[presentationId].SaveAllStrokesToFile(presentation);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存所有墨迹到文件失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 从文件加载已保存的墨迹
/// </summary>
public void LoadSavedStrokes(Presentation presentation)
{
if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation) || presentation == null) return;
lock (_lockObject)
{
try
{
var presentationId = GeneratePresentationId(presentation);
if (_presentationManagers.ContainsKey(presentationId))
{
_presentationManagers[presentationId].LoadSavedStrokes();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从文件加载墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 清除指定演示文稿的所有墨迹
/// </summary>
public void ClearPresentationStrokes(Presentation presentation)
{
if (presentation == null) return;
lock (_lockObject)
{
try
{
var presentationId = GeneratePresentationId(presentation);
if (_presentationManagers.ContainsKey(presentationId))
{
_presentationManagers[presentationId].ClearAllStrokes();
LogHelper.WriteLogToFile($"已清除演示文稿墨迹: {presentation.Name}", LogHelper.LogType.Trace);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除演示文稿墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 清除所有演示文稿的墨迹
/// </summary>
public void ClearAllStrokes()
{
lock (_lockObject)
{
try
{
foreach (var manager in _presentationManagers.Values)
{
manager?.ClearAllStrokes();
}
LogHelper.WriteLogToFile("已清除所有演示文稿墨迹", LogHelper.LogType.Trace);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除所有墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 翻页后锁定墨迹写入
/// </summary>
public void LockInkForSlide(int slideIndex)
{
lock (_lockObject)
{
try
{
var manager = GetCurrentManager();
if (manager != null)
{
manager.LockInkForSlide(slideIndex);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"锁定墨迹写入失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 检查是否可以写入墨迹
/// </summary>
public bool CanWriteInk(int currentSlideIndex)
{
lock (_lockObject)
{
try
{
var manager = GetCurrentManager();
if (manager != null)
{
return manager.CanWriteInk(currentSlideIndex);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检查墨迹写入权限失败: {ex}", LogHelper.LogType.Error);
}
}
return false;
}
/// <summary>
/// 重置当前演示文稿的墨迹锁定状态
/// </summary>
public void ResetCurrentPresentationLockState()
{
lock (_lockObject)
{
try
{
var manager = GetCurrentManager();
if (manager != null)
{
manager.ResetLockState();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"重置墨迹锁定状态失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 移除演示文稿管理器
/// </summary>
public void RemovePresentation(Presentation presentation)
{
if (presentation == null) return;
lock (_lockObject)
{
try
{
var presentationId = GeneratePresentationId(presentation);
if (_presentationManagers.ContainsKey(presentationId))
{
// 保存墨迹到文件
_presentationManagers[presentationId].SaveAllStrokesToFile(presentation);
// 释放资源
_presentationManagers[presentationId].Dispose();
_presentationManagers.Remove(presentationId);
}
if (_presentationInfos.ContainsKey(presentationId))
{
_presentationInfos.Remove(presentationId);
}
// 如果移除的是当前活跃的演示文稿,重置活跃ID
if (_currentActivePresentationId == presentationId)
{
_currentActivePresentationId = "";
}
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005 || hr == 0x800706BA || hr == 0x800706BE || hr == 0x80048010)
{
}
}
catch (Exception)
{
}
}
}
/// <summary>
/// 获取当前管理的演示文稿数量
/// </summary>
public int GetPresentationCount()
{
lock (_lockObject)
{
return _presentationManagers.Count;
}
}
/// <summary>
/// 获取所有演示文稿信息
/// </summary>
public List<PresentationInfo> GetAllPresentationInfos()
{
lock (_lockObject)
{
return _presentationInfos.Values.ToList();
}
}
/// <summary>
/// 清理长时间未访问的演示文稿管理器
/// </summary>
public void CleanupInactivePresentations(TimeSpan inactiveThreshold)
{
lock (_lockObject)
{
try
{
var inactiveIds = new List<string>();
var cutoffTime = DateTime.Now - inactiveThreshold;
foreach (var info in _presentationInfos.Values)
{
if (info.LastAccessTime < cutoffTime && info.Id != _currentActivePresentationId)
{
inactiveIds.Add(info.Id);
}
}
foreach (var id in inactiveIds)
{
if (_presentationManagers.ContainsKey(id))
{
_presentationManagers[id].Dispose();
_presentationManagers.Remove(id);
}
_presentationInfos.Remove(id);
LogHelper.WriteLogToFile($"已清理非活跃演示文稿: {id}", LogHelper.LogType.Trace);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理非活跃演示文稿失败: {ex}", LogHelper.LogType.Error);
}
}
}
#endregion
#region Private Methods
private PPTInkManager GetCurrentManager()
{
if (string.IsNullOrEmpty(_currentActivePresentationId) ||
!_presentationManagers.ContainsKey(_currentActivePresentationId))
{
return null;
}
return _presentationManagers[_currentActivePresentationId];
}
private Presentation GetCurrentActivePresentation()
{
try
{
// 通过PPTManager获取当前活跃的演示文稿
return PPTManager?.GetCurrentActivePresentation();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取当前活跃演示文稿失败: {ex}", LogHelper.LogType.Error);
return null;
}
}
private string GeneratePresentationId(Presentation presentation)
{
try
{
// 检查COM对象是否仍然有效
if (presentation == null)
{
return $"invalid_{DateTime.Now.Ticks}";
}
var presentationPath = presentation.FullName;
var fileHash = GetFileHash(presentationPath);
var processId = GetProcessId(presentation);
return $"{presentation.Name}_{fileHash}_{processId}";
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005 || hr == 0x800706BA || hr == 0x800706BE || hr == 0x80048010)
{
return $"disconnected_{DateTime.Now.Ticks}";
}
return $"error_{DateTime.Now.Ticks}";
}
catch (Exception)
{
return $"unknown_{DateTime.Now.Ticks}";
}
}
private string GetFileHash(string filePath)
{
try
{
if (string.IsNullOrEmpty(filePath)) return "unknown";
using (var md5 = MD5.Create())
{
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(filePath));
return BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 8);
}
}
catch (Exception)
{
// 所有异常都静默处理,避免日志噪音
return "error";
}
}
private string GetProcessId(Presentation presentation)
{
try
{
// 尝试获取PowerPoint应用程序的进程ID
if (presentation.Application != null)
{
// 通过COM对象获取进程信息
var hwnd = presentation.Application.HWND;
if (hwnd != 0)
{
return hwnd.ToString();
}
}
return "unknown";
}
catch (COMException comEx)
{
// COM对象已失效,这是正常情况,完全静默处理
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005 || hr == 0x800706BA || hr == 0x800706BE || hr == 0x80048010)
{
return "disconnected";
}
return "error";
}
catch (Exception)
{
return "error";
}
}
#endregion
#region Dispose
public void Dispose()
{
if (!_disposed)
{
lock (_lockObject)
{
foreach (var manager in _presentationManagers.Values)
{
manager?.Dispose();
}
_presentationManagers.Clear();
_presentationInfos.Clear();
}
_disposed = true;
}
}
#endregion
}
/// <summary>
/// 演示文稿信息
/// </summary>
public class PresentationInfo
{
public string Id { get; set; }
public string Name { get; set; }
public string FullName { get; set; }
public int SlideCount { get; set; }
public DateTime CreatedTime { get; set; }
public DateTime LastAccessTime { get; set; }
}
}
+35 -4
View File
@@ -25,10 +25,14 @@ namespace Ink_Canvas.Helpers
}
/// <summary>
/// 用于显示笔迹的类
/// 用于显示笔迹的类
/// </summary>
public class StrokeVisual : DrawingVisual
{
private bool _needsRedraw = true;
private int _lastPointCount = 0;
private const int REDRAW_THRESHOLD = 3;
/// <summary>
/// 创建显示笔迹的类
/// </summary>
@@ -49,15 +53,20 @@ namespace Ink_Canvas.Helpers
public StrokeVisual(DrawingAttributes drawingAttributes)
{
_drawingAttributes = drawingAttributes;
// 启用硬件加速
RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.HighQuality);
RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);
RenderOptions.SetCachingHint(this, CachingHint.Cache);
}
/// <summary>
/// 设置或获取显示的笔迹
/// 设置或获取显示的笔迹
/// </summary>
public Stroke Stroke { set; get; }
/// <summary>
/// 在笔迹中添加点
/// 在笔迹中添加点
/// </summary>
/// <param name="point"></param>
public void Add(StylusPoint point)
@@ -66,28 +75,50 @@ namespace Ink_Canvas.Helpers
{
var collection = new StylusPointCollection { point };
Stroke = new Stroke(collection) { DrawingAttributes = _drawingAttributes };
_lastPointCount = 1;
}
else
{
Stroke.StylusPoints.Add(point);
_lastPointCount++;
}
// 标记需要重绘
_needsRedraw = true;
}
/// <summary>
/// 重新画出笔迹
/// 重新画出笔迹
/// </summary>
public void Redraw()
{
if (!_needsRedraw || Stroke == null) return;
if (_lastPointCount % REDRAW_THRESHOLD != 0 && _lastPointCount > REDRAW_THRESHOLD)
{
return;
}
try
{
using (var dc = RenderOpen())
{
Stroke.Draw(dc);
}
_needsRedraw = false;
}
catch { }
}
/// <summary>
/// 强制重绘
/// </summary>
public void ForceRedraw()
{
_needsRedraw = true;
Redraw();
}
private readonly DrawingAttributes _drawingAttributes;
public static implicit operator Stroke(StrokeVisual v)
+507
View File
@@ -0,0 +1,507 @@
using Microsoft.Office.Interop.PowerPoint;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Ink;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// PPT墨迹管理器 - 负责PPT中墨迹的保存、加载和同步
/// </summary>
public class PPTInkManager : IDisposable
{
#region Properties
public bool IsAutoSaveEnabled { get; set; } = true;
public string AutoSaveLocation { get; set; } = "";
public StrokeCollection CurrentStrokes { get; private set; } = new StrokeCollection();
#endregion
#region Private Fields
private MemoryStream[] _memoryStreams;
private int _maxSlides = 100;
private string _currentPresentationId = "";
private readonly object _lockObject = new object();
private bool _disposed;
// 墨迹锁定机制,防止翻页时的墨迹冲突
private DateTime _inkLockUntil = DateTime.MinValue;
private int _lockedSlideIndex = -1;
private const int InkLockMilliseconds = 500;
// 添加快速切换保护机制
private DateTime _lastSwitchTime = DateTime.MinValue;
private int _lastSwitchSlideIndex = -1;
private const int MinSwitchIntervalMs = 100; // 最小切换间隔100毫秒
#endregion
#region Constructor
public PPTInkManager()
{
InitializeMemoryStreams();
}
private void InitializeMemoryStreams()
{
_memoryStreams = new MemoryStream[_maxSlides + 2];
}
#endregion
#region Public Methods
/// <summary>
/// 初始化新的演示文稿
/// </summary>
public void InitializePresentation(Presentation presentation)
{
if (presentation == null) return;
lock (_lockObject)
{
try
{
// 完全清理之前的墨迹状态
ClearAllStrokes();
// 重置墨迹锁定状态
_inkLockUntil = DateTime.MinValue;
_lockedSlideIndex = -1;
// 生成演示文稿唯一标识符
_currentPresentationId = GeneratePresentationId(presentation);
// 重新初始化内存流数组
int slideCount = 0;
try
{
slideCount = presentation.Slides.Count;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x80048010)
{
return;
}
throw;
}
_memoryStreams = new MemoryStream[slideCount + 2];
// 如果启用自动保存,尝试加载已保存的墨迹
if (IsAutoSaveEnabled && !string.IsNullOrEmpty(AutoSaveLocation))
{
LoadSavedStrokes();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化演示文稿墨迹管理失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 保存当前页面的墨迹
/// </summary>
public void SaveCurrentSlideStrokes(int slideIndex, StrokeCollection strokes)
{
if (slideIndex <= 0 || strokes == null) return;
lock (_lockObject)
{
try
{
// 检查墨迹锁定
if (!CanWriteInk(slideIndex))
{
if (DateTime.Now < _inkLockUntil)
{
LogHelper.WriteLogToFile($"墨迹写入被锁定,当前页:{slideIndex},锁定页:{_lockedSlideIndex}", LogHelper.LogType.Warning);
}
return;
}
if (slideIndex < _memoryStreams.Length)
{
var ms = new MemoryStream();
strokes.Save(ms);
ms.Position = 0;
// 释放旧的内存流
_memoryStreams[slideIndex]?.Dispose();
_memoryStreams[slideIndex] = ms;
if (ms.Length > 0)
{
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存第{slideIndex}页墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 强制保存指定页面的墨迹(忽略锁定状态)
/// </summary>
public void ForceSaveSlideStrokes(int slideIndex, StrokeCollection strokes)
{
if (slideIndex <= 0 || strokes == null) return;
lock (_lockObject)
{
try
{
if (slideIndex < _memoryStreams.Length)
{
var ms = new MemoryStream();
strokes.Save(ms);
ms.Position = 0;
// 释放旧的内存流
_memoryStreams[slideIndex]?.Dispose();
_memoryStreams[slideIndex] = ms;
LogHelper.WriteLogToFile($"已强制保存第{slideIndex}页墨迹,大小: {ms.Length} bytes", LogHelper.LogType.Trace);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"强制保存第{slideIndex}页墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 加载指定页面的墨迹
/// </summary>
public StrokeCollection LoadSlideStrokes(int slideIndex)
{
if (slideIndex <= 0) return new StrokeCollection();
lock (_lockObject)
{
try
{
if (slideIndex < _memoryStreams.Length && _memoryStreams[slideIndex] != null && _memoryStreams[slideIndex].Length > 0)
{
_memoryStreams[slideIndex].Position = 0;
var strokes = new StrokeCollection(_memoryStreams[slideIndex]);
return strokes;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载第{slideIndex}页墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
return new StrokeCollection();
}
/// <summary>
/// 切换到指定页面并加载墨迹
/// </summary>
public StrokeCollection SwitchToSlide(int slideIndex, StrokeCollection currentStrokes = null)
{
lock (_lockObject)
{
try
{
// 检查快速切换保护
var now = DateTime.Now;
if (now - _lastSwitchTime < TimeSpan.FromMilliseconds(MinSwitchIntervalMs) &&
_lastSwitchSlideIndex == slideIndex)
{
LogHelper.WriteLogToFile($"快速切换保护:忽略重复的页面切换请求 {slideIndex}", LogHelper.LogType.Warning);
return LoadSlideStrokes(slideIndex);
}
// 设置墨迹锁定
LockInkForSlide(slideIndex);
// 加载新页面的墨迹
var newStrokes = LoadSlideStrokes(slideIndex);
// 更新切换记录
_lastSwitchTime = now;
_lastSwitchSlideIndex = slideIndex;
if (newStrokes.Count > 0)
{
}
return newStrokes;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换到第{slideIndex}页失败: {ex}", LogHelper.LogType.Error);
return new StrokeCollection();
}
}
}
/// <summary>
/// 保存所有墨迹到文件
/// </summary>
public void SaveAllStrokesToFile(Presentation presentation)
{
if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation) || presentation == null) return;
lock (_lockObject)
{
try
{
var folderPath = GetPresentationFolderPath();
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
// 保存位置信息
try
{
File.WriteAllText(Path.Combine(folderPath, "Position"), _lockedSlideIndex.ToString());
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存位置信息失败: {ex}", LogHelper.LogType.Error);
}
// 保存所有页面的墨迹
int savedCount = 0;
int slideCount = 0;
try
{
slideCount = presentation.Slides.Count;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x80048010)
{
return;
}
throw;
}
for (int i = 1; i <= slideCount && i < _memoryStreams.Length; i++)
{
if (_memoryStreams[i] != null)
{
try
{
if (_memoryStreams[i].Length > 8)
{
var srcBuf = new byte[_memoryStreams[i].Length];
_memoryStreams[i].Position = 0;
var byteLength = _memoryStreams[i].Read(srcBuf, 0, srcBuf.Length);
var filePath = Path.Combine(folderPath, i.ToString("0000") + ".icstk");
File.WriteAllBytes(filePath, srcBuf);
savedCount++;
}
else
{
// 删除空的墨迹文件
var filePath = Path.Combine(folderPath, i.ToString("0000") + ".icstk");
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存第{i}页墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存墨迹到文件失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 从文件加载已保存的墨迹
/// </summary>
public void LoadSavedStrokes()
{
if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation)) return;
lock (_lockObject)
{
try
{
var folderPath = GetPresentationFolderPath();
if (!Directory.Exists(folderPath)) return;
var files = new DirectoryInfo(folderPath).GetFiles("*.icstk");
int loadedCount = 0;
foreach (var file in files)
{
try
{
if (int.TryParse(Path.GetFileNameWithoutExtension(file.Name), out int slideIndex))
{
if (slideIndex > 0 && slideIndex < _memoryStreams.Length)
{
var fileBytes = File.ReadAllBytes(file.FullName);
_memoryStreams[slideIndex] = new MemoryStream(fileBytes);
_memoryStreams[slideIndex].Position = 0;
loadedCount++;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载墨迹文件{file.Name}失败: {ex}", LogHelper.LogType.Error);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从文件加载墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 清除所有墨迹
/// </summary>
public void ClearAllStrokes()
{
lock (_lockObject)
{
try
{
for (int i = 0; i < _memoryStreams.Length; i++)
{
_memoryStreams[i]?.Dispose();
_memoryStreams[i] = null;
}
CurrentStrokes.Clear();
LogHelper.WriteLogToFile("已清除所有墨迹", LogHelper.LogType.Trace);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除墨迹失败: {ex}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 翻页后锁定墨迹写入
/// </summary>
public void LockInkForSlide(int slideIndex)
{
_inkLockUntil = DateTime.Now.AddMilliseconds(InkLockMilliseconds);
_lockedSlideIndex = slideIndex;
}
/// <summary>
/// 检查是否可以写入墨迹
/// </summary>
public bool CanWriteInk(int currentSlideIndex)
{
// 如果锁定时间已过,允许写入
if (DateTime.Now >= _inkLockUntil)
{
return true;
}
// 如果当前页面与锁定页面相同,允许写入(用户在当前页面绘制)
if (currentSlideIndex == _lockedSlideIndex)
{
return true;
}
// 只有在快速切换且页面不同时才锁定
return false;
}
/// <summary>
/// 重置墨迹锁定状态
/// </summary>
public void ResetLockState()
{
lock (_lockObject)
{
_inkLockUntil = DateTime.MinValue;
_lockedSlideIndex = -1;
_lastSwitchTime = DateTime.MinValue;
_lastSwitchSlideIndex = -1;
}
}
#endregion
#region Private Methods
private string GeneratePresentationId(Presentation presentation)
{
try
{
var presentationPath = presentation.FullName;
var fileHash = GetFileHash(presentationPath);
return $"{presentation.Name}_{fileHash}";
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"生成演示文稿ID失败: {ex}", LogHelper.LogType.Error);
return $"unknown_{DateTime.Now.Ticks}";
}
}
private string GetFileHash(string filePath)
{
try
{
if (string.IsNullOrEmpty(filePath)) return "unknown";
using (var md5 = MD5.Create())
{
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(filePath));
return BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 8);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"计算文件哈希值失败: {ex}", LogHelper.LogType.Error);
return "error";
}
}
private string GetPresentationFolderPath()
{
return Path.Combine(AutoSaveLocation, "Auto Saved - Presentations", _currentPresentationId);
}
#endregion
#region Dispose
public void Dispose()
{
if (!_disposed)
{
lock (_lockObject)
{
ClearAllStrokes();
}
_disposed = true;
}
}
#endregion
}
}
File diff suppressed because it is too large Load Diff
+491
View File
@@ -0,0 +1,491 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// PPT UI管理器 - 统一管理PPT相关的UI更新和样式设置
/// </summary>
public class PPTUIManager
{
#region Properties
public bool ShowPPTButton { get; set; } = true;
public int PPTButtonsDisplayOption { get; set; } = 2222;
public int PPTSButtonsOption { get; set; } = 221;
public int PPTBButtonsOption { get; set; } = 121;
public int PPTLSButtonPosition { get; set; } = 0;
public int PPTRSButtonPosition { get; set; } = 0;
public int PPTLBButtonPosition { get; set; } = 0;
public int PPTRBButtonPosition { get; set; } = 0;
public bool EnablePPTButtonPageClickable { get; set; } = true;
public bool EnablePPTButtonLongPressPageTurn { get; set; } = true;
#endregion
#region Private Fields
private readonly MainWindow _mainWindow;
private readonly Dispatcher _dispatcher;
#endregion
#region Constructor
public PPTUIManager(MainWindow mainWindow)
{
_mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow));
_dispatcher = _mainWindow.Dispatcher;
}
#endregion
#region Public Methods
/// <summary>
/// 更新PPT连接状态UI
/// </summary>
public void UpdateConnectionStatus(bool isConnected)
{
_dispatcher.InvokeAsync(() =>
{
try
{
if (isConnected)
{
_mainWindow.StackPanelPPTControls.Visibility = Visibility.Visible;
_mainWindow.BtnPPTSlideShow.Visibility = Visibility.Visible;
}
else
{
_mainWindow.StackPanelPPTControls.Visibility = Visibility.Collapsed;
_mainWindow.BtnPPTSlideShow.Visibility = Visibility.Collapsed;
_mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed;
HideAllNavigationPanels();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新PPT连接状态UI失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 更新幻灯片放映状态UI
/// </summary>
public void UpdateSlideShowStatus(bool isInSlideShow, int currentSlide = 0, int totalSlides = 0)
{
_dispatcher.InvokeAsync(() =>
{
try
{
if (isInSlideShow)
{
_mainWindow.BtnPPTSlideShow.Visibility = Visibility.Collapsed;
_mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Visible;
// 只有在页数有效时才更新页码显示
if (currentSlide > 0 && totalSlides > 0)
{
_mainWindow.PPTBtnPageNow.Text = currentSlide.ToString();
_mainWindow.PPTBtnPageTotal.Text = $"/ {totalSlides}";
}
else
{
// 页数无效时清空页码显示
_mainWindow.PPTBtnPageNow.Text = "?";
_mainWindow.PPTBtnPageTotal.Text = "/ ?";
LogHelper.WriteLogToFile($"PPT页数无效,清空页码显示: 当前页={currentSlide}, 总页数={totalSlides}", LogHelper.LogType.Warning);
}
UpdateNavigationPanelsVisibility();
UpdateNavigationButtonStyles();
}
else
{
_mainWindow.BtnPPTSlideShow.Visibility = Visibility.Visible;
_mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed;
HideAllNavigationPanels();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新幻灯片放映状态UI失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 更新当前页码显示
/// </summary>
public void UpdateCurrentSlideNumber(int currentSlide, int totalSlides)
{
_dispatcher.InvokeAsync(() =>
{
try
{
// 只有在页数有效时才更新页码显示
if (currentSlide > 0 && totalSlides > 0)
{
_mainWindow.PPTBtnPageNow.Text = currentSlide.ToString();
_mainWindow.PPTBtnPageTotal.Text = $"/ {totalSlides}";
}
else
{
// 页数无效时清空页码显示
_mainWindow.PPTBtnPageNow.Text = "?";
_mainWindow.PPTBtnPageTotal.Text = "/ ?";
LogHelper.WriteLogToFile($"PPT页数无效,清空页码显示: 当前页={currentSlide}, 总页数={totalSlides}", LogHelper.LogType.Warning);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新页码显示失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 处理PPT放映状态变化
/// </summary>
public void OnSlideShowStateChanged(bool isInSlideShow)
{
_dispatcher.InvokeAsync(() =>
{
try
{
if (!isInSlideShow)
{
// 如果不在放映模式,隐藏所有导航面板
HideAllNavigationPanels();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理PPT放映状态变化失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 更新导航面板显示状态
/// </summary>
public void UpdateNavigationPanelsVisibility()
{
_dispatcher.InvokeAsync(() =>
{
try
{
// 检查是否应该显示PPT按钮
// 不仅要检查按钮设置,还要确保确实在PPT放映模式下且页数有效
bool isInSlideShow = _mainWindow.PPTManager?.IsInSlideShow == true;
int slidesCount = _mainWindow.PPTManager?.SlidesCount ?? 0;
bool hasValidPageCount = slidesCount > 0;
bool shouldShowButtons = ShowPPTButton &&
_mainWindow.BtnPPTSlideShowEnd.Visibility == Visibility.Visible &&
isInSlideShow &&
hasValidPageCount;
if (!shouldShowButtons)
{
HideAllNavigationPanels();
return;
}
// 设置侧边按钮位置
_mainWindow.LeftSidePanelForPPTNavigation.Margin = new Thickness(0, 0, 0, PPTLSButtonPosition * 2);
_mainWindow.RightSidePanelForPPTNavigation.Margin = new Thickness(0, 0, 0, PPTRSButtonPosition * 2);
// 设置底部按钮水平位置
_mainWindow.LeftBottomPanelForPPTNavigation.Margin = new Thickness(6 + PPTLBButtonPosition, 0, 0, 6);
_mainWindow.RightBottomPanelForPPTNavigation.Margin = new Thickness(0, 0, 6 + PPTRBButtonPosition, 6);
// 根据显示选项设置面板可见性
var displayOption = PPTButtonsDisplayOption.ToString();
if (displayOption.Length >= 4)
{
var options = displayOption.ToCharArray();
// 左下角面板
if (options[0] == '2')
AnimationsHelper.ShowWithFadeIn(_mainWindow.LeftBottomPanelForPPTNavigation);
else
_mainWindow.LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
// 右下角面板
if (options[1] == '2')
AnimationsHelper.ShowWithFadeIn(_mainWindow.RightBottomPanelForPPTNavigation);
else
_mainWindow.RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
// 左侧面板
if (options[2] == '2')
AnimationsHelper.ShowWithFadeIn(_mainWindow.LeftSidePanelForPPTNavigation);
else
_mainWindow.LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
// 右侧面板
if (options[3] == '2')
AnimationsHelper.ShowWithFadeIn(_mainWindow.RightSidePanelForPPTNavigation);
else
_mainWindow.RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新导航面板显示状态失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 更新导航按钮样式
/// </summary>
public void UpdateNavigationButtonStyles()
{
_dispatcher.InvokeAsync(() =>
{
try
{
UpdateSideButtonStyles();
UpdateBottomButtonStyles();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新导航按钮样式失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 隐藏所有导航面板
/// </summary>
public void HideAllNavigationPanels()
{
_dispatcher.InvokeAsync(() =>
{
try
{
_mainWindow.LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
_mainWindow.RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
_mainWindow.LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
_mainWindow.RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"隐藏导航面板失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 显示/隐藏侧边栏退出按钮
/// </summary>
public void UpdateSidebarExitButtons(bool show)
{
_dispatcher.InvokeAsync(() =>
{
try
{
var visibility = show ? Visibility.Visible : Visibility.Collapsed;
if (_mainWindow.BtnExitPptFromSidebarLeft != null)
_mainWindow.BtnExitPptFromSidebarLeft.Visibility = visibility;
if (_mainWindow.BtnExitPptFromSidebarRight != null)
_mainWindow.BtnExitPptFromSidebarRight.Visibility = visibility;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新侧边栏退出按钮失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 设置浮动栏透明度
/// </summary>
public void SetFloatingBarOpacity(double opacity)
{
_dispatcher.InvokeAsync(() =>
{
try
{
_mainWindow.ViewboxFloatingBar.Opacity = opacity;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"设置浮动栏透明度失败: {ex}", LogHelper.LogType.Error);
}
});
}
/// <summary>
/// 设置主面板边距
/// </summary>
public void SetMainPanelMargin(Thickness margin)
{
_dispatcher.InvokeAsync(() =>
{
try
{
_mainWindow.ViewBoxStackPanelMain.Margin = margin;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"设置主面板边距失败: {ex}", LogHelper.LogType.Error);
}
});
}
#endregion
#region Private Methods
private void UpdateSideButtonStyles()
{
try
{
var sideOption = PPTSButtonsOption.ToString();
if (sideOption.Length < 3) return;
var options = sideOption.ToCharArray();
// 页码按钮显示
var pageButtonVisibility = options[0] == '2' ? Visibility.Visible : Visibility.Collapsed;
_mainWindow.PPTLSPageButton.Visibility = pageButtonVisibility;
_mainWindow.PPTRSPageButton.Visibility = pageButtonVisibility;
// 透明度设置
var opacity = options[1] == '2' ? 0.5 : 1.0;
_mainWindow.PPTBtnLSBorder.Opacity = opacity;
_mainWindow.PPTBtnRSBorder.Opacity = opacity;
// 颜色主题
bool isDarkTheme = options[2] == '2';
ApplyButtonTheme(_mainWindow.PPTBtnLSBorder, _mainWindow.PPTBtnRSBorder, isDarkTheme, true);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新侧边按钮样式失败: {ex}", LogHelper.LogType.Error);
}
}
private void UpdateBottomButtonStyles()
{
try
{
var bottomOption = PPTBButtonsOption.ToString();
if (bottomOption.Length < 3) return;
var options = bottomOption.ToCharArray();
// 页码按钮显示
var pageButtonVisibility = options[0] == '2' ? Visibility.Visible : Visibility.Collapsed;
_mainWindow.PPTLBPageButton.Visibility = pageButtonVisibility;
_mainWindow.PPTRBPageButton.Visibility = pageButtonVisibility;
// 透明度设置
var opacity = options[1] == '2' ? 0.5 : 1.0;
_mainWindow.PPTBtnLBBorder.Opacity = opacity;
_mainWindow.PPTBtnRBBorder.Opacity = opacity;
// 颜色主题
bool isDarkTheme = options[2] == '2';
ApplyButtonTheme(_mainWindow.PPTBtnLBBorder, _mainWindow.PPTBtnRBBorder, isDarkTheme, false);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"更新底部按钮样式失败: {ex}", LogHelper.LogType.Error);
}
}
private void ApplyButtonTheme(Border leftBorder, Border rightBorder, bool isDarkTheme, bool isSideButton)
{
try
{
Color backgroundColor, borderColor, foregroundColor, feedbackColor;
if (isDarkTheme)
{
backgroundColor = Color.FromRgb(39, 39, 42);
borderColor = Color.FromRgb(82, 82, 91);
foregroundColor = Colors.White;
feedbackColor = Colors.White;
}
else
{
backgroundColor = Color.FromRgb(244, 244, 245);
borderColor = Color.FromRgb(161, 161, 170);
foregroundColor = Color.FromRgb(39, 39, 42);
feedbackColor = Color.FromRgb(24, 24, 27);
}
// 应用背景和边框颜色
var backgroundBrush = new SolidColorBrush(backgroundColor);
var borderBrush = new SolidColorBrush(borderColor);
leftBorder.Background = backgroundBrush;
leftBorder.BorderBrush = borderBrush;
rightBorder.Background = backgroundBrush;
rightBorder.BorderBrush = borderBrush;
// 应用图标和文字颜色
var foregroundBrush = new SolidColorBrush(foregroundColor);
var feedbackBrush = new SolidColorBrush(feedbackColor);
if (isSideButton)
{
ApplySideButtonColors(foregroundBrush, feedbackBrush);
}
else
{
ApplyBottomButtonColors(foregroundBrush, feedbackBrush);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"应用按钮主题失败: {ex}", LogHelper.LogType.Error);
}
}
private void ApplySideButtonColors(SolidColorBrush foregroundBrush, SolidColorBrush feedbackBrush)
{
// 图标颜色
_mainWindow.PPTLSPreviousButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTRSPreviousButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTLSNextButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTRSNextButtonGeometry.Brush = foregroundBrush;
// 反馈背景颜色
_mainWindow.PPTLSPreviousButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRSPreviousButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTLSPageButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRSPageButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTLSNextButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRSNextButtonFeedbackBorder.Background = feedbackBrush;
// 文字颜色
TextBlock.SetForeground(_mainWindow.PPTLSPageButton, foregroundBrush);
TextBlock.SetForeground(_mainWindow.PPTRSPageButton, foregroundBrush);
}
private void ApplyBottomButtonColors(SolidColorBrush foregroundBrush, SolidColorBrush feedbackBrush)
{
// 图标颜色
_mainWindow.PPTLBPreviousButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTRBPreviousButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTLBNextButtonGeometry.Brush = foregroundBrush;
_mainWindow.PPTRBNextButtonGeometry.Brush = foregroundBrush;
// 反馈背景颜色
_mainWindow.PPTLBPreviousButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRBPreviousButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTLBPageButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRBPageButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTLBNextButtonFeedbackBorder.Background = feedbackBrush;
_mainWindow.PPTRBNextButtonFeedbackBorder.Background = feedbackBrush;
// 文字颜色
TextBlock.SetForeground(_mainWindow.PPTLBPageButton, foregroundBrush);
TextBlock.SetForeground(_mainWindow.PPTRBPageButton, foregroundBrush);
}
#endregion
}
}
@@ -1,9 +1,9 @@
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using iNKORE.UI.WPF.Modern.Controls;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
@@ -16,17 +16,17 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
/// 父插件
/// </summary>
private readonly SuperLauncherPlugin _plugin;
/// <summary>
/// 实际按钮控件
/// </summary>
private readonly SimpleStackPanel _panel;
/// <summary>
/// 获取按钮UI元素
/// </summary>
public UIElement Element => _panel;
/// <summary>
/// 构造函数
/// </summary>
@@ -37,7 +37,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
_plugin = plugin;
LogHelper.WriteLogToFile("开始创建启动台按钮");
// 创建SimpleStackPanel
_panel = new SimpleStackPanel
{
@@ -48,13 +48,13 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
Margin = new Thickness(0, -2, 0, 0),
Background = Brushes.Transparent
};
LogHelper.WriteLogToFile("创建SimpleStackPanel完成");
// 添加图标
var image = CreateIconImage();
_panel.Children.Add(image);
// 添加文本
TextBlock textBlock = new TextBlock
{
@@ -65,18 +65,18 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
TextAlignment = TextAlignment.Center
};
_panel.Children.Add(textBlock);
// 设置鼠标事件
_panel.MouseDown += Panel_MouseDown;
_panel.MouseUp += Panel_MouseUp;
_panel.MouseLeave += Panel_MouseLeave;
// 右键菜单支持
_panel.ContextMenu = CreateContextMenu();
// 设置工具提示
_panel.ToolTip = "启动台";
LogHelper.WriteLogToFile("启动台按钮创建完成");
}
catch (Exception ex)
@@ -85,7 +85,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 创建右键菜单
/// </summary>
@@ -95,31 +95,31 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 创建菜单
ContextMenu menu = new ContextMenu();
// 创建位置切换菜单项
MenuItem positionMenuItem = new MenuItem();
positionMenuItem.Header = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
positionMenuItem.Header = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
"移至右侧" : "移至左侧";
positionMenuItem.Click += (s, e) =>
positionMenuItem.Click += (s, e) =>
{
// 切换位置
_plugin.Config.ButtonPosition = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
_plugin.Config.ButtonPosition = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left ?
LauncherButtonPosition.Right : LauncherButtonPosition.Left;
// 更新按钮位置
_plugin.UpdateButtonPosition();
// 保存配置
_plugin.SaveConfig();
LogHelper.WriteLogToFile($"通过右键菜单切换启动台按钮位置为: {_plugin.Config.ButtonPosition}");
};
menu.Items.Add(positionMenuItem);
// 添加设置菜单项
MenuItem settingsMenuItem = new MenuItem();
settingsMenuItem.Header = "打开设置";
settingsMenuItem.Click += (s, e) =>
settingsMenuItem.Click += (s, e) =>
{
// 打开插件设置窗口
var mainWindow = Application.Current.MainWindow;
@@ -142,7 +142,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
}
};
menu.Items.Add(settingsMenuItem);
return menu;
}
catch (Exception ex)
@@ -151,7 +151,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
return null;
}
}
/// <summary>
/// 获取实际的UI元素
/// </summary>
@@ -160,7 +160,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
return _panel;
}
/// <summary>
/// 创建图标图像
/// </summary>
@@ -174,40 +174,40 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
Height = 17,
Margin = new Thickness(0, 3, 0, 0)
};
// 设置位图缩放模式
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
// 创建绘图图像
DrawingImage drawingImage = new DrawingImage();
DrawingGroup drawingGroup = new DrawingGroup();
drawingGroup.ClipGeometry = Geometry.Parse("M0,0 V24 H24 V0 H0 Z");
// 使用提供的应用网格图标
GeometryDrawing geometryDrawing = new GeometryDrawing
{
Brush = new SolidColorBrush(Color.FromRgb(0x1B, 0x1B, 0x1B)),
Geometry = Geometry.Parse("F0 M24,24z M0,0z M4.41721,4.29873C4.35178,4.29873,4.29873,4.35178,4.29873,4.41721L4.29873,9.15646C4.29873,9.22189,4.35178,9.27494,4.41721,9.27494L9.15646,9.27494C9.22189,9.27494,9.27494,9.22189,9.27494,9.15646L9.27494,4.41721C9.27494,4.35178,9.22189,4.29873,9.15646,4.29873L4.41721,4.29873z M2.64,4.41721C2.64,3.43569,3.43569,2.64,4.41721,2.64L9.15646,2.64C10.138,2.64,10.9337,3.43569,10.9337,4.41721L10.9337,9.15646C10.9337,10.138,10.138,10.9337,9.15646,10.9337L4.41721,10.9337C3.43569,10.9337,2.64,10.138,2.64,9.15646L2.64,4.41721z M14.8435,4.29873C14.7781,4.29873,14.7251,4.35178,14.7251,4.41721L14.7251,9.15646C14.7251,9.22189,14.7781,9.27494,14.8435,9.27494L19.5828,9.27494C19.6482,9.27494,19.7013,9.22189,19.7013,9.15646L19.7013,4.41721C19.7013,4.35178,19.6482,4.29873,19.5828,4.29873L14.8435,4.29873z M13.0663,4.41721C13.0663,3.43569,13.862,2.64,14.8435,2.64L19.5828,2.64C20.5643,2.64,21.36,3.43569,21.36,4.41721L21.36,9.15646C21.36,10.138,20.5643,10.9337,19.5828,10.9337L14.8435,10.9337C13.862,10.9337,13.0663,10.138,13.0663,9.15646L13.0663,4.41721z M14.8435,14.7251C14.7781,14.7251,14.7251,14.7781,14.7251,14.8435L14.7251,19.5828C14.7251,19.6482,14.7781,19.7013,14.8435,19.7013L19.5828,19.7013C19.6482,19.7013,19.7013,19.6482,19.7013,19.5828L19.7013,14.8435C19.7013,14.7781,19.6482,14.7251,19.5828,14.7251L14.8435,14.7251z M13.0663,14.8435C13.0663,13.862,13.862,13.0663,14.8435,13.0663L19.5828,13.0663C20.5643,13.0663,21.36,13.862,21.36,14.8435L21.36,19.5828C21.36,20.5643,20.5643,21.36,19.5828,21.36L14.8435,21.36C13.862,21.36,13.0663,20.5643,13.0663,19.5828L13.0663,14.8435z M4.41721,14.7251C4.35178,14.7251,4.29873,14.7781,4.29873,14.8435L4.29873,19.5828C4.29873,19.6482,4.35178,19.7013,4.41721,19.7013L9.15646,19.7013C9.22189,19.7013,9.27494,19.6482,9.27494,19.5828L9.27494,14.8435C9.27494,14.7781,9.22189,14.7251,9.15646,14.7251L4.41721,14.7251z M2.64,14.8435C2.64,13.862,3.43569,13.0663,4.41721,13.0663L9.15646,13.0663C10.138,13.0663,10.9337,13.862,10.9337,14.8435L10.9337,19.5828C10.9337,20.5643,10.138,21.36,9.15646,21.36L4.41721,21.36C3.43569,21.36,2.64,20.5643,2.64,19.5828L2.64,14.8435z")
};
drawingGroup.Children.Add(geometryDrawing);
// 设置图像源
drawingImage.Drawing = drawingGroup;
image.Source = drawingImage;
return image;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"创建图标图像时出错: {ex.Message}", LogHelper.LogType.Error);
LogHelper.NewLog(ex);
// 返回一个空图像
return new Image();
}
}
/// <summary>
/// 鼠标按下事件
/// </summary>
@@ -224,7 +224,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
LogHelper.WriteLogToFile($"启动台按钮鼠标按下事件出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 鼠标抬起事件
/// </summary>
@@ -237,14 +237,14 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
return;
}
// 恢复背景
_panel.Background = Brushes.Transparent;
LogHelper.WriteLogToFile("启动台按钮鼠标抬起,准备显示启动台窗口");
// 获取按钮在屏幕上的位置
Point buttonPosition = _panel.PointToScreen(new Point(_panel.ActualWidth / 2, 0));
// 显示启动台窗口
_plugin.ShowLauncherWindow(buttonPosition);
}
@@ -254,7 +254,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 鼠标离开事件
/// </summary>
@@ -271,4 +271,4 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
}
}
}
}
}
@@ -1,3 +1,5 @@
using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -8,8 +10,6 @@ using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using Newtonsoft.Json;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
@@ -22,13 +22,13 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
/// 左侧
/// </summary>
Left,
/// <summary>
/// 右侧
/// </summary>
Right
}
/// <summary>
/// 启动台配置
/// </summary>
@@ -38,13 +38,13 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
/// 启动台按钮位置
/// </summary>
public LauncherButtonPosition ButtonPosition { get; set; } = LauncherButtonPosition.Right;
/// <summary>
/// 启动台应用程序列表
/// </summary>
public List<LauncherItem> Items { get; set; } = new List<LauncherItem>();
}
/// <summary>
/// 启动台应用项
/// </summary>
@@ -54,33 +54,33 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
/// 应用程序名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 应用程序路径
/// </summary>
public string Path { get; set; }
/// <summary>
/// 是否可见
/// </summary>
public bool IsVisible { get; set; } = true;
/// <summary>
/// 在启动台中的位置(0-39
/// </summary>
public int Position { get; set; } = -1;
/// <summary>
/// 是否已固定位置
/// </summary>
public bool IsPositionFixed { get; set; } = false;
/// <summary>
/// 图标缓存
/// </summary>
[JsonIgnore]
private ImageSource _iconCache;
/// <summary>
/// 获取应用程序图标
/// </summary>
@@ -93,7 +93,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
return _iconCache;
}
try
{
if (File.Exists(Path))
@@ -106,7 +106,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
icon.Dispose();
return _iconCache;
}
@@ -126,7 +126,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
string[] parts = iconPath.Split(',');
string iconFile = parts[0].Trim('"');
int iconIndex = parts.Length > 1 ? Convert.ToInt32(parts[1]) : 0;
if (File.Exists(iconFile))
{
Icon icon = IconExtractor.Extract(iconFile, iconIndex, true);
@@ -136,7 +136,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
icon.Dispose();
return _iconCache;
}
@@ -150,12 +150,12 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
LogHelper.WriteLogToFile($"获取应用图标时出错: {ex.Message}", LogHelper.LogType.Error);
}
// 返回默认图标
return GetDefaultIcon();
}
}
/// <summary>
/// 获取默认图标
/// </summary>
@@ -179,7 +179,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
icon.Dispose();
return _iconCache;
}
@@ -190,7 +190,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
LogHelper.WriteLogToFile($"获取资源管理器图标时出错: {ex.Message}", LogHelper.LogType.Warning);
// 如果获取Windows图标失败,回退到默认图标
}
// 回退到备用图标
string explorerIconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-Fluent", "ic_fluent_folder_24_regular.png");
if (File.Exists(explorerIconPath))
@@ -201,7 +201,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
return _iconCache;
}
}
// 返回一个简单的默认图标
string iconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-png", "icc.png");
if (File.Exists(iconPath))
@@ -211,7 +211,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
_iconCache = image;
return _iconCache;
}
// 如果还是没有找到,尝试使用应用程序图标
string appIconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "Icons-Fluent", "ic_fluent_apps_24_regular.png");
if (File.Exists(appIconPath))
@@ -222,14 +222,14 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
return _iconCache;
}
}
catch (Exception ex)
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取默认图标时出错: {ex.Message}", LogHelper.LogType.Error);
}
return null;
}
/// <summary>
/// 启动应用程序
/// </summary>
@@ -242,7 +242,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
LogHelper.WriteLogToFile("无法启动应用程序:路径为空", LogHelper.LogType.Error);
return;
}
// 检查文件是否存在
if (!File.Exists(Path) && !Path.Contains(":\\"))
{
@@ -264,7 +264,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
};
Process.Start(psi);
}
LogHelper.WriteLogToFile($"已启动应用程序: {Path}");
}
catch (Exception ex)
@@ -274,7 +274,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
}
}
}
/// <summary>
/// 图标提取工具类
/// </summary>
@@ -294,7 +294,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
IntPtr large;
IntPtr small;
ExtractIconEx(file, index, out large, out small, 1);
try
{
return Icon.FromHandle(largeIcon ? large : small);
@@ -307,7 +307,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
if (large != IntPtr.Zero)
DestroyIcon(large);
if (small != IntPtr.Zero)
DestroyIcon(small);
}
@@ -317,7 +317,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
return null;
}
}
[DllImport("Shell32.dll", EntryPoint = "ExtractIconEx")]
private static extern int ExtractIconEx(
[MarshalAs(UnmanagedType.LPStr)] string lpszFile,
@@ -325,8 +325,8 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
out IntPtr phiconLarge,
out IntPtr phiconSmall,
int nIcons);
[DllImport("User32.dll")]
private static extern int DestroyIcon(IntPtr hIcon);
}
}
}
@@ -1,10 +1,10 @@
using Ink_Canvas.Windows;
using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Ink_Canvas.Windows;
using Microsoft.Win32;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
@@ -17,7 +17,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
/// 父插件
/// </summary>
private readonly SuperLauncherPlugin _plugin;
/// <summary>
/// 构造函数
/// </summary>
@@ -25,20 +25,20 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
public LauncherSettingsControl(SuperLauncherPlugin plugin)
{
InitializeComponent();
_plugin = plugin;
// 设置按钮位置
RbtnLeft.IsChecked = _plugin.Config.ButtonPosition == LauncherButtonPosition.Left;
RbtnRight.IsChecked = _plugin.Config.ButtonPosition == LauncherButtonPosition.Right;
// 绑定应用列表
DgApps.ItemsSource = _plugin.LauncherItems;
// 初始化按钮状态
UpdateButtonStates();
}
/// <summary>
/// 更新按钮状态
/// </summary>
@@ -48,16 +48,16 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
BtnEdit.IsEnabled = hasSelection;
BtnDelete.IsEnabled = hasSelection;
}
/// <summary>
/// 位置单选按钮选择事件
/// </summary>
private void RbtnPosition_Checked(object sender, RoutedEventArgs e)
{
if (!IsLoaded) return;
LauncherButtonPosition oldPosition = _plugin.Config.ButtonPosition;
if (sender == RbtnLeft)
{
_plugin.Config.ButtonPosition = LauncherButtonPosition.Left;
@@ -66,7 +66,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
_plugin.Config.ButtonPosition = LauncherButtonPosition.Right;
}
// 如果位置发生变化,更新按钮位置
if (oldPosition != _plugin.Config.ButtonPosition)
{
@@ -74,10 +74,10 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 更新按钮位置
_plugin.UpdateButtonPosition();
// 保存配置
_plugin.SaveConfig();
LogHelper.WriteLogToFile($"启动台按钮位置已更改为: {_plugin.Config.ButtonPosition}");
}
catch (Exception ex)
@@ -87,7 +87,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
}
}
}
/// <summary>
/// 添加按钮点击事件
/// </summary>
@@ -103,7 +103,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
IsVisible = true,
Position = -1 // 让插件管理器分配位置
};
// 直接显示编辑对话框
EditLauncherItem(item, true);
}
@@ -113,7 +113,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
MessageBox.Show($"添加启动项时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 编辑应用按钮点击事件
/// </summary>
@@ -124,7 +124,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
EditLauncherItem(item, false);
}
}
/// <summary>
/// 删除应用按钮点击事件
/// </summary>
@@ -134,22 +134,22 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 确认删除
MessageBoxResult result = MessageBox.Show(
$"确定要删除 {item.Name} 吗?",
"删除确认",
MessageBoxButton.YesNo,
$"确定要删除 {item.Name} 吗?",
"删除确认",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
// 从集合中移除
_plugin.LauncherItems.Remove(item);
// 保存配置
_plugin.SaveConfig();
}
}
}
/// <summary>
/// 保存设置按钮点击事件
/// </summary>
@@ -159,7 +159,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 保存配置
_plugin.SaveConfig();
// 如果插件已启用,重新加载启动台按钮
if (_plugin.IsEnabled)
{
@@ -170,7 +170,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 如果插件未启用,则启用它
_plugin.Enable();
// 通知PluginSettingsWindow刷新插件列表
var window = Window.GetWindow(this);
if (window is PluginSettingsWindow pluginSettingsWindow)
@@ -179,7 +179,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
pluginSettingsWindow.RefreshPluginList();
}
}
MessageBox.Show("设置已保存并应用!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
@@ -188,7 +188,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
MessageBox.Show($"保存设置时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 应用项选择变更事件
/// </summary>
@@ -196,7 +196,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
UpdateButtonStates();
}
/// <summary>
/// 编辑启动项
/// </summary>
@@ -213,63 +213,63 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
WindowStartupLocation = WindowStartupLocation.CenterScreen,
ResizeMode = ResizeMode.NoResize
};
// 创建编辑表单
Grid grid = new Grid
{
Margin = new Thickness(20)
};
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
// 名称输入框
TextBlock nameLabel = new TextBlock
{
Text = "名称:",
VerticalAlignment = VerticalAlignment.Center
TextBlock nameLabel = new TextBlock
{
Text = "名称:",
VerticalAlignment = VerticalAlignment.Center
};
TextBox nameTextBox = new TextBox
{
Text = item.Name,
Margin = new Thickness(0, 5, 0, 5)
TextBox nameTextBox = new TextBox
{
Text = item.Name,
Margin = new Thickness(0, 5, 0, 5)
};
Grid.SetRow(nameLabel, 0);
Grid.SetColumn(nameLabel, 0);
Grid.SetRow(nameTextBox, 0);
Grid.SetColumn(nameTextBox, 1);
grid.Children.Add(nameLabel);
grid.Children.Add(nameTextBox);
// 路径输入框
TextBlock pathLabel = new TextBlock
{
Text = "路径:",
VerticalAlignment = VerticalAlignment.Center
TextBlock pathLabel = new TextBlock
{
Text = "路径:",
VerticalAlignment = VerticalAlignment.Center
};
Grid pathGrid = new Grid();
pathGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
pathGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength() });
TextBox pathTextBox = new TextBox
{
Text = item.Path,
Margin = new Thickness(0, 5, 5, 5)
TextBox pathTextBox = new TextBox
{
Text = item.Path,
Margin = new Thickness(0, 5, 5, 5)
};
Button browseButton = new Button
{
Content = "浏览",
Button browseButton = new Button
{
Content = "浏览",
Padding = new Thickness(5, 0, 5, 0),
Margin = new Thickness(0, 5, 0, 5)
};
browseButton.Click += (s, e) =>
browseButton.Click += (s, e) =>
{
OpenFileDialog dialog = new OpenFileDialog
{
@@ -278,11 +278,11 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
Multiselect = false,
FileName = pathTextBox.Text
};
if (dialog.ShowDialog() == true)
{
pathTextBox.Text = dialog.FileName;
// 如果选择的是.exe文件,自动获取文件名填入名称字段
if (Path.GetExtension(dialog.FileName).ToLower() == ".exe")
{
@@ -295,20 +295,20 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
}
}
};
Grid.SetColumn(pathTextBox, 0);
Grid.SetColumn(browseButton, 1);
pathGrid.Children.Add(pathTextBox);
pathGrid.Children.Add(browseButton);
Grid.SetRow(pathLabel, 1);
Grid.SetColumn(pathLabel, 0);
Grid.SetRow(pathGrid, 1);
Grid.SetColumn(pathGrid, 1);
grid.Children.Add(pathLabel);
grid.Children.Add(pathGrid);
// 确认和取消按钮
StackPanel buttonPanel = new StackPanel
{
@@ -316,7 +316,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
HorizontalAlignment = HorizontalAlignment.Right,
Margin = new Thickness(0, 10, 0, 0)
};
Button okButton = new Button
{
Content = "确定",
@@ -324,15 +324,15 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
Margin = new Thickness(0, 0, 10, 0),
IsDefault = true
};
Button cancelButton = new Button
{
Content = "取消",
Padding = new Thickness(15, 5, 15, 5),
IsCancel = true
};
okButton.Click += (s, e) =>
okButton.Click += (s, e) =>
{
// 验证输入
if (string.IsNullOrWhiteSpace(nameTextBox.Text))
@@ -340,17 +340,17 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
MessageBox.Show("请输入应用名称!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (string.IsNullOrWhiteSpace(pathTextBox.Text))
{
MessageBox.Show("请输入应用路径!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
// 更新项目
item.Name = nameTextBox.Text;
item.Path = pathTextBox.Text;
// 如果是新建,添加到集合
if (isNew)
{
@@ -363,34 +363,34 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
view.Refresh();
}
// 保存配置
_plugin.SaveConfig();
}
editWindow.DialogResult = true;
editWindow.Close();
};
cancelButton.Click += (s, e) =>
cancelButton.Click += (s, e) =>
{
editWindow.DialogResult = false;
editWindow.Close();
};
buttonPanel.Children.Add(okButton);
buttonPanel.Children.Add(cancelButton);
Grid.SetRow(buttonPanel, 2);
Grid.SetColumnSpan(buttonPanel, 2);
grid.Children.Add(buttonPanel);
// 设置窗口内容
editWindow.Content = grid;
// 显示窗口
editWindow.ShowDialog();
}
}
}
}
@@ -23,39 +23,39 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
/// 父插件
/// </summary>
private readonly SuperLauncherPlugin _plugin;
/// <summary>
/// 是否处于固定模式
/// </summary>
private bool _isFixMode;
/// <summary>
/// 应用项按钮列表
/// </summary>
private readonly Dictionary<Button, LauncherItem> _appButtons = new Dictionary<Button, LauncherItem>();
/// <summary>
/// 拖拽中的按钮
/// </summary>
private Button _draggingButton;
/// <summary>
/// 拖拽开始位置
/// </summary>
private Point _dragStartPoint;
/// <summary>
/// 构造函数
/// </summary>
public LauncherWindow(SuperLauncherPlugin plugin)
{
InitializeComponent();
_plugin = plugin;
// 加载应用项
LoadLauncherItems();
// 添加鼠标按下事件(用于拖动窗口)
MouseDown += (s, e) =>
{
@@ -64,11 +64,11 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
DragMove();
}
};
// 根据应用数量调整窗口大小
AdjustWindowSize();
}
/// <summary>
/// 加载启动台应用项
/// </summary>
@@ -77,13 +77,13 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
// 清空现有应用项
AppPanel.Children.Clear();
_appButtons.Clear();
// 获取显示的应用项
var visibleItems = _plugin.LauncherItems
.Where(item => item.IsVisible)
.OrderBy(item => item.Position)
.ToList();
foreach (var item in visibleItems)
{
// 创建应用按钮
@@ -93,23 +93,23 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
DataContext = item,
Tag = item.Position
};
// 添加点击事件
appButton.Click += AppButton_Click;
// 在固定模式下,添加拖拽事件
appButton.PreviewMouseDown += AppButton_PreviewMouseDown;
appButton.PreviewMouseMove += AppButton_PreviewMouseMove;
appButton.PreviewMouseUp += AppButton_PreviewMouseUp;
// 记录按钮和项目的对应关系
_appButtons.Add(appButton, item);
// 添加到面板
AppPanel.Children.Add(appButton);
}
}
/// <summary>
/// 根据应用数量调整窗口大小
/// </summary>
@@ -119,14 +119,14 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 每行最多显示4个应用
const int appsPerRow = 4;
// 计算行数
int visibleCount = _appButtons.Count;
int rowCount = (int)Math.Ceiling(visibleCount / (double)appsPerRow);
// 设置窗口宽度(每个应用90像素宽 = 80 + 5*2
Width = Math.Min(appsPerRow * 90 + 40, 400); // 最大宽度400
// 设置窗口高度(每个应用90像素高 = 80 + 5*2
Height = Math.Min(rowCount * 90 + 60, 600); // 最大高度600,标题栏40 + 边距20
}
@@ -135,7 +135,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
LogHelper.WriteLogToFile($"调整启动台窗口大小时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 应用按钮点击事件
/// </summary>
@@ -144,18 +144,18 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
try
{
if (_isFixMode) return; // 在固定模式下,不响应点击事件
if (sender is Button button && _appButtons.TryGetValue(button, out LauncherItem item))
{
// 获取应用路径和名称,用于后续启动
string appPath = item.Path;
string appName = item.Name;
LogHelper.WriteLogToFile($"点击启动应用: {appName}, 路径: {appPath}");
// 首先标记窗口正在关闭
IsClosing = true;
// 创建一个应用启动任务
var launchTask = new Task(() =>
{
@@ -163,9 +163,9 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 等待一段时间,确保窗口关闭流程已经开始
Thread.Sleep(200);
// 使用UI线程启动应用
Application.Current.Dispatcher.Invoke(() =>
Application.Current.Dispatcher.Invoke(() =>
{
try
{
@@ -174,11 +174,11 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 创建进程启动信息
var psi = new ProcessStartInfo
{
FileName = appPath,
{
FileName = appPath,
UseShellExecute = true,
};
};
// 启动应用程序
var process = Process.Start(psi);
LogHelper.WriteLogToFile($"应用程序 {appName} 已启动");
@@ -201,14 +201,14 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
LogHelper.WriteLogToFile($"应用启动任务出错: {ex.Message}", LogHelper.LogType.Error);
}
});
// 关闭窗口
try
{
Dispatcher.BeginInvoke(new Action(() =>
{
try { Close(); } catch { }
// 启动应用程序任务
launchTask.Start();
}), DispatcherPriority.Background);
@@ -227,86 +227,86 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
try { IsClosing = true; Close(); } catch { }
}
}
#region
/// <summary>
/// 应用按钮鼠标按下事件
/// </summary>
private void AppButton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (!_isFixMode) return;
if (e.ChangedButton == MouseButton.Left && sender is Button button)
{
_draggingButton = button;
_dragStartPoint = e.GetPosition(AppPanel);
button.CaptureMouse();
button.Opacity = 0.7;
// 阻止事件冒泡,以避免触发按钮点击
e.Handled = true;
}
}
/// <summary>
/// 应用按钮鼠标移动事件
/// </summary>
private void AppButton_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (!_isFixMode || _draggingButton == null) return;
if (e.LeftButton == MouseButtonState.Pressed)
{
Point currentPosition = e.GetPosition(AppPanel);
// 移动按钮
System.Windows.Controls.Canvas.SetLeft(_draggingButton, currentPosition.X - _draggingButton.ActualWidth / 2);
System.Windows.Controls.Canvas.SetTop(_draggingButton, currentPosition.Y - _draggingButton.ActualHeight / 2);
// 将按钮移到最上层
Panel.SetZIndex(_draggingButton, 100);
// 阻止事件冒泡
e.Handled = true;
}
}
/// <summary>
/// 应用按钮鼠标释放事件
/// </summary>
private void AppButton_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (!_isFixMode || _draggingButton == null) return;
// 释放鼠标捕获
_draggingButton.ReleaseMouseCapture();
// 计算新位置
Point releasePoint = e.GetPosition(AppPanel);
int newPosition = CalculateGridPosition(releasePoint);
// 获取当前项目
LauncherItem currentItem = _appButtons[_draggingButton];
// 重新排序
ReorderItems(currentItem, newPosition);
// 重新加载应用项
LoadLauncherItems();
// 保存配置
_plugin.SaveConfig();
// 清除拖拽状态
_draggingButton.Opacity = 1;
Panel.SetZIndex(_draggingButton, 0);
_draggingButton = null;
// 阻止事件冒泡
e.Handled = true;
}
/// <summary>
/// 计算网格位置
/// </summary>
@@ -316,18 +316,18 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
int columnCount = 4; // 每行最多4个应用
int columnWidth = 90; // 应用宽度(包括边距)
int rowHeight = 90; // 应用高度(包括边距)
int column = (int)(point.X / columnWidth);
int row = (int)(point.Y / rowHeight);
// 确保在有效范围内
column = Math.Max(0, Math.Min(column, columnCount - 1));
row = Math.Max(0, row);
// 计算位置索引
return row * columnCount + column;
}
/// <summary>
/// 重新排序应用项
/// </summary>
@@ -337,22 +337,22 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 设置项目为固定位置
item.IsPositionFixed = true;
// 如果位置相同,无需调整
if (item.Position == newPosition)
{
return;
}
// 获取所有可见项目
var visibleItems = _plugin.LauncherItems
.Where(i => i.IsVisible)
.OrderBy(i => i.Position)
.ToList();
// 移除当前项目
visibleItems.Remove(item);
// 查找插入位置
int insertIndex = 0;
for (int i = 0; i < visibleItems.Count; i++)
@@ -364,10 +364,10 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
}
insertIndex = i + 1;
}
// 插入项目
visibleItems.Insert(insertIndex, item);
// 重新分配位置
for (int i = 0; i < visibleItems.Count; i++)
{
@@ -379,11 +379,11 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
LogHelper.WriteLogToFile($"重新排序应用项时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region
/// <summary>
/// 窗口失去焦点事件
/// </summary>
@@ -396,7 +396,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 标记为正在关闭
IsClosing = true;
// 使用Dispatcher.BeginInvoke而不是直接调用Close,避免冲突
Dispatcher.BeginInvoke(new Action(() =>
{
@@ -404,8 +404,8 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 再次检查窗口状态
if (IsLoaded && !IsClosing)
{
Close();
{
Close();
}
}
catch (Exception ex)
@@ -420,12 +420,12 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
LogHelper.WriteLogToFile($"窗口失去焦点关闭时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 窗口是否正在关闭
/// </summary>
private bool IsClosing { get; set; }
/// <summary>
/// 重写OnClosing方法,标记窗口正在关闭
/// </summary>
@@ -434,7 +434,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
IsClosing = true;
base.OnClosing(e);
}
/// <summary>
/// 关闭按钮点击事件
/// </summary>
@@ -442,7 +442,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
Close();
}
/// <summary>
/// 固定模式按钮点击事件
/// </summary>
@@ -450,17 +450,17 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher
{
// 切换固定模式
_isFixMode = !_isFixMode;
// 更新固定模式按钮图标颜色
FixModeIcon.Fill = _isFixMode ? Brushes.Yellow : Brushes.White;
// 显示提示
if (_isFixMode)
{
MessageBox.Show("已进入固定模式,您可以拖动应用图标调整位置。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
#endregion
}
}
}
@@ -1,3 +1,5 @@
using Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -5,8 +7,6 @@ using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Ink_Canvas.Helpers.Plugins.BuiltIn.SuperLauncher;
using Newtonsoft.Json;
namespace Ink_Canvas.Helpers.Plugins.BuiltIn
{
@@ -16,71 +16,71 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
public class SuperLauncherPlugin : PluginBase
{
#region
public override string Name => "超级启动台";
public override string Description => "在浮动栏添加一个启动台按钮,可快速启动常用应用程序。";
public override Version Version => new Version(1, 0, 1);
public override string Author => "ICC CE 团队";
public override bool IsBuiltIn => true;
#endregion
#region
/// <summary>
/// 启动台配置
/// </summary>
public LauncherConfig Config { get; private set; }
/// <summary>
/// 启动台应用程序列表
/// </summary>
public ObservableCollection<LauncherItem> LauncherItems { get; private set; }
/// <summary>
/// 启动台按钮
/// </summary>
private LauncherButton _launcherButton;
/// <summary>
/// 启动台窗口
/// </summary>
private LauncherWindow _launcherWindow;
/// <summary>
/// 配置文件路径
/// </summary>
private readonly string _configPath = Path.Combine(App.RootPath, "PluginConfigs", "SuperLauncher.json");
/// <summary>
/// 标记是否已添加到浮动栏
/// </summary>
private bool _isAddedToFloatingBar;
#endregion
#region
public override void Initialize()
{
try
{
base.Initialize();
// 创建配置目录
string configDir = Path.Combine(App.RootPath, "PluginConfigs");
if (!Directory.Exists(configDir))
{
Directory.CreateDirectory(configDir);
}
// 加载配置
LoadConfig();
LogHelper.WriteLogToFile("超级启动台插件已初始化");
}
catch (Exception ex)
@@ -89,29 +89,29 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.NewLog(ex);
}
}
public override void Enable()
{
try
{
if (IsEnabled) return; // 防止重复启用
// 创建启动台按钮
if (_launcherButton == null)
{
_launcherButton = new LauncherButton(this);
LogHelper.WriteLogToFile("超级启动台按钮已创建");
}
// 添加启动台按钮到浮动栏
AddLauncherButtonToFloatingBar();
// 设置启用状态
base.Enable();
// 保存插件配置
SavePluginSettings();
LogHelper.WriteLogToFile("超级启动台插件已启用");
}
catch (Exception ex)
@@ -120,29 +120,29 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.NewLog(ex);
}
}
public override void Disable()
{
try
{
if (!IsEnabled) return; // 防止重复禁用
// 从浮动栏移除启动台按钮
RemoveLauncherButtonFromFloatingBar();
// 如果启动台窗口打开,则关闭
if (_launcherWindow != null && _launcherWindow.IsVisible)
{
_launcherWindow.Close();
_launcherWindow = null;
}
// 设置禁用状态
base.Disable();
// 保存插件配置
SavePluginSettings();
LogHelper.WriteLogToFile("超级启动台插件已禁用");
}
catch (Exception ex)
@@ -151,30 +151,30 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.NewLog(ex);
}
}
public override UserControl GetSettingsView()
{
return new LauncherSettingsControl(this);
}
public override void Cleanup()
{
// 保存配置
SaveConfig();
// 从浮动栏移除启动台按钮
RemoveLauncherButtonFromFloatingBar();
// 如果启动台窗口打开,则关闭
if (_launcherWindow != null && _launcherWindow.IsVisible)
{
_launcherWindow.Close();
_launcherWindow = null;
}
base.Cleanup();
}
/// <summary>
/// 保存插件设置
/// </summary>
@@ -187,12 +187,12 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
{
LoadConfig();
}
// 更新其他设置,但不更改插件启用状态
// 保存配置
SaveConfig();
LogHelper.WriteLogToFile("超级启动台插件设置已保存");
}
catch (Exception ex)
@@ -200,11 +200,11 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.WriteLogToFile($"保存超级启动台插件设置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
#region
/// <summary>
/// 加载配置
/// </summary>
@@ -217,7 +217,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
string json = File.ReadAllText(_configPath);
Config = JsonConvert.DeserializeObject<LauncherConfig>(json) ?? CreateDefaultConfig();
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items ?? new List<LauncherItem>());
// 注意:不再根据配置更改插件启用状态
// 插件状态由PluginManager统一管理
}
@@ -235,7 +235,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LauncherItems = new ObservableCollection<LauncherItem>(Config.Items);
}
}
/// <summary>
/// 保存配置
/// </summary>
@@ -245,11 +245,11 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
{
// 同步LauncherItems到Config
Config.Items = new List<LauncherItem>(LauncherItems);
// 序列化并保存配置
string json = JsonConvert.SerializeObject(Config, Formatting.Indented);
File.WriteAllText(_configPath, json);
LogHelper.WriteLogToFile("超级启动台配置已保存");
}
catch (Exception ex)
@@ -257,7 +257,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.WriteLogToFile($"保存超级启动台配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 创建默认配置
/// </summary>
@@ -278,14 +278,14 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
}
}
};
return config;
}
#endregion
#region
/// <summary>
/// 将启动台按钮添加到浮动栏
/// </summary>
@@ -299,7 +299,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
RemoveLauncherButtonFromFloatingBar();
_isAddedToFloatingBar = false;
}
// 获取主窗口实例
var mainWindow = Application.Current.MainWindow;
if (mainWindow == null)
@@ -307,11 +307,11 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.WriteLogToFile("未找到主窗口实例,无法添加启动台按钮", LogHelper.LogType.Error);
return;
}
// 创建启动台按钮
_launcherButton = new LauncherButton(this);
var buttonElement = _launcherButton.Element;
// 查找浮动栏
var floatingBar = mainWindow.FindName("StackPanelFloatingBar") as Panel;
if (floatingBar == null)
@@ -321,13 +321,13 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
FindStackPanelFloatingBar(mainWindow, ref floatingBarPanelFromTree);
floatingBar = floatingBarPanelFromTree;
}
if (floatingBar == null)
{
LogHelper.WriteLogToFile("未找到浮动栏,无法添加启动台按钮", LogHelper.LogType.Error);
return;
}
// 添加启动台按钮到浮动栏
if (Config.ButtonPosition == LauncherButtonPosition.Left)
{
@@ -339,7 +339,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
floatingBar.Children.Add(buttonElement);
LogHelper.WriteLogToFile("启动台按钮已添加到浮动栏右侧");
}
_isAddedToFloatingBar = true;
}
catch (Exception ex)
@@ -348,14 +348,14 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 递归查找StackPanelFloatingBar
/// </summary>
private void FindStackPanelFloatingBar(DependencyObject parent, ref Panel result)
{
if (parent == null || result != null) return;
try
{
// 检查当前对象是否为我们要找的面板
@@ -364,10 +364,10 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
result = panel;
return;
}
// 获取子元素数量
int childCount = VisualTreeHelper.GetChildrenCount(parent);
// 遍历所有子元素
for (int i = 0; i < childCount; i++)
{
@@ -380,7 +380,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.WriteLogToFile($"查找StackPanelFloatingBar时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 从浮动栏移除启动台按钮
/// </summary>
@@ -392,7 +392,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
{
return;
}
// 获取主窗口实例
var mainWindow = Application.Current.MainWindow;
if (mainWindow == null)
@@ -400,10 +400,10 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.WriteLogToFile("未找到主窗口实例,无法移除启动台按钮", LogHelper.LogType.Error);
return;
}
// 获取按钮元素
var buttonElement = _launcherButton.Element;
// 查找浮动栏
var floatingBar = mainWindow.FindName("StackPanelFloatingBar") as Panel;
if (floatingBar == null)
@@ -413,20 +413,20 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
FindStackPanelFloatingBar(mainWindow, ref floatingBarPanelFromTree);
floatingBar = floatingBarPanelFromTree;
}
if (floatingBar == null)
{
LogHelper.WriteLogToFile("未找到浮动栏,无法移除启动台按钮", LogHelper.LogType.Error);
return;
}
// 从浮动栏移除启动台按钮
if (floatingBar.Children.Contains(buttonElement))
{
floatingBar.Children.Remove(buttonElement);
LogHelper.WriteLogToFile("启动台按钮已从浮动栏移除");
}
_isAddedToFloatingBar = false;
}
catch (Exception ex)
@@ -435,7 +435,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.NewLog(ex);
}
}
/// <summary>
/// 更新启动台按钮位置
/// </summary>
@@ -457,11 +457,11 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.NewLog(ex);
}
}
#endregion
#region
/// <summary>
/// 显示启动台窗口
/// </summary>
@@ -477,13 +477,13 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
_launcherWindow = null;
return;
}
// 创建新的启动台窗口
_launcherWindow = new LauncherWindow(this);
// 计算窗口位置,使其位于按钮上方
PositionLauncherWindow(_launcherWindow, buttonPosition);
// 显示窗口
_launcherWindow.Show();
}
@@ -492,7 +492,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
LogHelper.WriteLogToFile($"显示启动台窗口时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 设置启动台窗口位置
/// </summary>
@@ -504,18 +504,18 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
if (window.ActualWidth == 0 || window.ActualHeight == 0)
{
window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
// 设置窗口加载完成后的位置
window.Loaded += (s, e) =>
{
// 窗口位于按钮上方居中
double left = buttonPosition.X - (window.ActualWidth / 2);
double top = buttonPosition.Y - window.ActualHeight - 10; // 在按钮上方留出一些间距
// 确保窗口在屏幕内
left = Math.Max(0, Math.Min(left, SystemParameters.WorkArea.Width - window.ActualWidth));
top = Math.Max(0, Math.Min(top, SystemParameters.WorkArea.Height - window.ActualHeight));
window.Left = left;
window.Top = top;
};
@@ -525,16 +525,16 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
// 窗口位于按钮上方居中
double left = buttonPosition.X - (window.ActualWidth / 2);
double top = buttonPosition.Y - window.ActualHeight - 10; // 在按钮上方留出一些间距
// 确保窗口在屏幕内
left = Math.Max(0, Math.Min(left, SystemParameters.WorkArea.Width - window.ActualWidth));
top = Math.Max(0, Math.Min(top, SystemParameters.WorkArea.Height - window.ActualHeight));
window.Left = left;
window.Top = top;
}
}
/// <summary>
/// 添加应用到启动台
/// </summary>
@@ -547,18 +547,18 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
MessageBox.Show("启动台项目数量已达上限(40个)!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 寻找合适的位置
if (item.Position < 0)
{
item.Position = FindNextAvailablePosition();
}
// 添加项目并保存配置
LauncherItems.Add(item);
SaveConfig();
}
/// <summary>
/// 查找下一个可用位置
/// </summary>
@@ -570,7 +570,7 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
{
usedPositions.Add(item.Position);
}
// 查找第一个可用位置
for (int i = 0; i < 40; i++)
{
@@ -579,11 +579,11 @@ namespace Ink_Canvas.Helpers.Plugins.BuiltIn
return i;
}
}
// 如果所有位置都已使用,则返回0
return 0;
}
#endregion
}
}
}
@@ -0,0 +1,92 @@
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 增强的插件基类,提供对插件服务的访问和基本实现
/// </summary>
public abstract class EnhancedPluginBase : PluginBase, IEnhancedPlugin
{
/// <summary>
/// 插件服务实例
/// </summary>
public IPluginService PluginService { get; private set; }
/// <summary>
/// 构造函数
/// </summary>
protected EnhancedPluginBase()
{
PluginService = PluginServiceManager.Instance;
}
/// <summary>
/// 插件启动时调用,在Initialize之后
/// </summary>
public virtual void OnStartup()
{
LogHelper.WriteLogToFile($"插件 {Name} 已启动");
}
/// <summary>
/// 插件关闭时调用,在Cleanup之前
/// </summary>
public virtual void OnShutdown()
{
LogHelper.WriteLogToFile($"插件 {Name} 正在关闭");
}
/// <summary>
/// 获取插件的菜单项
/// </summary>
/// <returns>菜单项集合</returns>
public virtual MenuItem[] GetMenuItems()
{
return new MenuItem[0];
}
/// <summary>
/// 获取插件的工具栏按钮
/// </summary>
/// <returns>工具栏按钮集合</returns>
public virtual Button[] GetToolbarButtons()
{
return new Button[0];
}
/// <summary>
/// 获取插件的状态栏信息
/// </summary>
/// <returns>状态栏信息</returns>
public virtual string GetStatusBarInfo()
{
return $"{Name} v{Version} - {(IsEnabled ? "" : "")}";
}
/// <summary>
/// 插件配置变更时调用
/// </summary>
public virtual void OnConfigurationChanged()
{
LogHelper.WriteLogToFile($"插件 {Name} 配置已变更");
}
/// <summary>
/// 重写初始化方法,调用OnStartup
/// </summary>
public override void Initialize()
{
base.Initialize();
OnStartup();
}
/// <summary>
/// 重写清理方法,调用OnShutdown
/// </summary>
public override void Cleanup()
{
OnShutdown();
base.Cleanup();
}
}
}
@@ -0,0 +1,241 @@
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 增强的插件基类 V2,提供对三个专门服务接口的访问
/// 插件开发者可以根据需要选择性地使用这些服务
/// </summary>
public abstract class EnhancedPluginBaseV2 : PluginBase, IEnhancedPlugin
{
/// <summary>
/// 获取服务实例
/// </summary>
public IGetService GetService { get; private set; }
/// <summary>
/// 窗口服务实例
/// </summary>
public IWindowService WindowService { get; private set; }
/// <summary>
/// 操作服务实例
/// </summary>
public IActionService ActionService { get; private set; }
/// <summary>
/// 插件服务实例(兼容性)
/// </summary>
public IPluginService PluginService { get; private set; }
/// <summary>
/// 构造函数
/// </summary>
protected EnhancedPluginBaseV2()
{
// 初始化所有服务实例
PluginService = PluginServiceManager.Instance;
GetService = PluginServiceManager.Instance;
WindowService = PluginServiceManager.Instance;
ActionService = PluginServiceManager.Instance;
}
/// <summary>
/// 插件启动时调用,在Initialize之后
/// </summary>
public virtual void OnStartup()
{
LogHelper.WriteLogToFile($"插件 {Name} 已启动");
}
/// <summary>
/// 插件关闭时调用,在Cleanup之前
/// </summary>
public virtual void OnShutdown()
{
LogHelper.WriteLogToFile($"插件 {Name} 正在关闭");
}
/// <summary>
/// 获取插件的菜单项
/// </summary>
/// <returns>菜单项集合</returns>
public virtual MenuItem[] GetMenuItems()
{
return new MenuItem[0];
}
/// <summary>
/// 获取插件的工具栏按钮
/// </summary>
/// <returns>工具栏按钮集合</returns>
public virtual Button[] GetToolbarButtons()
{
return new Button[0];
}
/// <summary>
/// 获取插件的状态栏信息
/// </summary>
/// <returns>状态栏信息</returns>
public virtual string GetStatusBarInfo()
{
return $"{Name} v{Version} - {(IsEnabled ? "" : "")}";
}
/// <summary>
/// 插件配置变更时调用
/// </summary>
public virtual void OnConfigurationChanged()
{
LogHelper.WriteLogToFile($"插件 {Name} 配置已变更");
}
#region 便
/// <summary>
/// 显示通知消息
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="type">消息类型</param>
protected void ShowNotification(string message, NotificationType type = NotificationType.Info)
{
WindowService.ShowNotification(message, type);
}
/// <summary>
/// 显示确认对话框
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="title">标题</param>
/// <returns>用户选择结果</returns>
protected bool ShowConfirmDialog(string message, string title = "确认")
{
return WindowService.ShowConfirmDialog(message, title);
}
/// <summary>
/// 显示输入对话框
/// </summary>
/// <param name="message">提示消息</param>
/// <param name="title">标题</param>
/// <param name="defaultValue">默认值</param>
/// <returns>用户输入内容</returns>
protected string ShowInputDialog(string message, string title = "输入", string defaultValue = "")
{
return WindowService.ShowInputDialog(message, title, defaultValue);
}
/// <summary>
/// 获取系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>设置值</returns>
protected T GetSetting<T>(string key, T defaultValue = default(T))
{
return GetService.GetSetting(key, defaultValue);
}
/// <summary>
/// 设置系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="value">设置值</param>
protected void SetSetting<T>(string key, T value)
{
ActionService.SetSetting(key, value);
}
/// <summary>
/// 保存设置
/// </summary>
protected void SaveSettings()
{
ActionService.SaveSettings();
}
/// <summary>
/// 清除当前画布
/// </summary>
protected void ClearCanvas()
{
ActionService.ClearCanvas();
}
/// <summary>
/// 撤销操作
/// </summary>
protected void Undo()
{
ActionService.Undo();
}
/// <summary>
/// 重做操作
/// </summary>
protected void Redo()
{
ActionService.Redo();
}
/// <summary>
/// 检查是否可以撤销
/// </summary>
protected bool CanUndo => GetService.CanUndo;
/// <summary>
/// 检查是否可以重做
/// </summary>
protected bool CanRedo => GetService.CanRedo;
/// <summary>
/// 获取当前绘制模式
/// </summary>
protected int CurrentDrawingMode => GetService.CurrentDrawingMode;
/// <summary>
/// 设置绘制模式
/// </summary>
/// <param name="mode">绘制模式</param>
protected void SetDrawingMode(int mode)
{
ActionService.SetDrawingMode(mode);
}
/// <summary>
/// 注册事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
protected void RegisterEventHandler(string eventName, System.EventHandler handler)
{
ActionService.RegisterEventHandler(eventName, handler);
}
/// <summary>
/// 注销事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
protected void UnregisterEventHandler(string eventName, System.EventHandler handler)
{
ActionService.UnregisterEventHandler(eventName, handler);
}
/// <summary>
/// 触发事件
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="sender">事件发送者</param>
/// <param name="args">事件参数</param>
protected void TriggerEvent(string eventName, object sender, System.EventArgs args)
{
ActionService.TriggerEvent(eventName, sender, args);
}
#endregion
}
}
@@ -0,0 +1,296 @@
using System;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 操作服务接口,统一所有执行操作相关的方法
/// </summary>
public interface IActionService
{
#region
/// <summary>
/// 清除当前画布
/// </summary>
void ClearCanvas();
/// <summary>
/// 清除所有画布
/// </summary>
void ClearAllCanvases();
/// <summary>
/// 添加新页面
/// </summary>
void AddNewPage();
/// <summary>
/// 删除当前页面
/// </summary>
void DeleteCurrentPage();
/// <summary>
/// 切换到指定页面
/// </summary>
/// <param name="pageIndex">页面索引</param>
void SwitchToPage(int pageIndex);
/// <summary>
/// 切换到下一页
/// </summary>
void NextPage();
/// <summary>
/// 切换到上一页
/// </summary>
void PreviousPage();
#endregion
#region
/// <summary>
/// 设置绘制模式
/// </summary>
/// <param name="mode">绘制模式</param>
void SetDrawingMode(int mode);
/// <summary>
/// 设置笔触宽度
/// </summary>
/// <param name="width">宽度</param>
void SetInkWidth(double width);
/// <summary>
/// 设置笔触颜色
/// </summary>
/// <param name="color">颜色</param>
void SetInkColor(Color color);
/// <summary>
/// 设置高亮笔宽度
/// </summary>
/// <param name="width">宽度</param>
void SetHighlighterWidth(double width);
/// <summary>
/// 设置橡皮擦大小
/// </summary>
/// <param name="size">大小</param>
void SetEraserSize(int size);
/// <summary>
/// 设置橡皮擦类型
/// </summary>
/// <param name="type">类型</param>
void SetEraserType(int type);
/// <summary>
/// 设置橡皮擦形状
/// </summary>
/// <param name="shape">形状</param>
void SetEraserShape(int shape);
/// <summary>
/// 设置笔触透明度
/// </summary>
/// <param name="alpha">透明度</param>
void SetInkAlpha(double alpha);
/// <summary>
/// 设置笔触样式
/// </summary>
/// <param name="style">样式</param>
void SetInkStyle(int style);
/// <summary>
/// 设置背景颜色
/// </summary>
/// <param name="color">颜色</param>
void SetBackgroundColor(string color);
#endregion
#region
/// <summary>
/// 保存画布内容
/// </summary>
/// <param name="filePath">文件路径</param>
void SaveCanvas(string filePath);
/// <summary>
/// 加载画布内容
/// </summary>
/// <param name="filePath">文件路径</param>
void LoadCanvas(string filePath);
/// <summary>
/// 导出为图片
/// </summary>
/// <param name="filePath">文件路径</param>
/// <param name="format">图片格式</param>
void ExportAsImage(string filePath, string format);
/// <summary>
/// 导出为PDF
/// </summary>
/// <param name="filePath">文件路径</param>
void ExportAsPDF(string filePath);
#endregion
#region
/// <summary>
/// 撤销操作
/// </summary>
void Undo();
/// <summary>
/// 重做操作
/// </summary>
void Redo();
#endregion
#region
/// <summary>
/// 全选
/// </summary>
void SelectAll();
/// <summary>
/// 取消选择
/// </summary>
void DeselectAll();
/// <summary>
/// 删除选中内容
/// </summary>
void DeleteSelected();
/// <summary>
/// 复制选中内容
/// </summary>
void CopySelected();
/// <summary>
/// 剪切选中内容
/// </summary>
void CutSelected();
/// <summary>
/// 粘贴内容
/// </summary>
void Paste();
#endregion
#region
/// <summary>
/// 设置系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="value">设置值</param>
void SetSetting<T>(string key, T value);
/// <summary>
/// 保存设置到文件
/// </summary>
void SaveSettings();
/// <summary>
/// 从文件加载设置
/// </summary>
void LoadSettings();
/// <summary>
/// 重置设置为默认值
/// </summary>
void ResetSettings();
#endregion
#region
/// <summary>
/// 启用插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void EnablePlugin(string pluginName);
/// <summary>
/// 禁用插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void DisablePlugin(string pluginName);
/// <summary>
/// 卸载插件
/// </summary>
/// <param name="pluginName">插件名称</param>
void UnloadPlugin(string pluginName);
#endregion
#region
/// <summary>
/// 注册事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
void RegisterEventHandler(string eventName, EventHandler handler);
/// <summary>
/// 注销事件处理器
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="handler">事件处理器</param>
void UnregisterEventHandler(string eventName, EventHandler handler);
/// <summary>
/// 触发事件
/// </summary>
/// <param name="eventName">事件名称</param>
/// <param name="sender">事件发送者</param>
/// <param name="args">事件参数</param>
void TriggerEvent(string eventName, object sender, EventArgs args);
#endregion
#region
/// <summary>
/// 重启应用程序
/// </summary>
void RestartApplication();
/// <summary>
/// 退出应用程序
/// </summary>
void ExitApplication();
/// <summary>
/// 检查更新
/// </summary>
void CheckForUpdates();
/// <summary>
/// 打开帮助文档
/// </summary>
void OpenHelpDocument();
/// <summary>
/// 打开关于页面
/// </summary>
void OpenAboutPage();
#endregion
}
}
@@ -13,7 +13,7 @@ namespace Ink_Canvas.Helpers.Plugins
private readonly string _pluginName;
private readonly Version _pluginVersion;
private bool _isInitialized;
/// <summary>
/// 创建 ICCPP 插件适配器
/// </summary>
@@ -24,11 +24,11 @@ namespace Ink_Canvas.Helpers.Plugins
_pluginPath = pluginPath;
_pluginData = pluginData;
PluginPath = pluginPath;
// 从文件名获取插件名称
_pluginName = Path.GetFileNameWithoutExtension(pluginPath);
_pluginVersion = new Version(1, 0, 0); // 默认版本
// 尝试从插件数据中读取更多信息
TryReadPluginMetadata();
}
@@ -42,7 +42,7 @@ namespace Ink_Canvas.Helpers.Plugins
_pluginVersion = new Version(1, 0, 0);
// 可选:初始化其他字段
}
/// <summary>
/// 尝试从插件数据中读取元数据
/// </summary>
@@ -52,7 +52,7 @@ namespace Ink_Canvas.Helpers.Plugins
{
// 这里可以根据 .iccpp 文件的实际格式解析元数据
// 例如,如果文件有特定的头部结构,可以在这里解析
// 示例:如果前100字节包含元数据
if (_pluginData.Length > 100)
{
@@ -64,46 +64,46 @@ namespace Ink_Canvas.Helpers.Plugins
LogHelper.WriteLogToFile($"解析插件 {_pluginName} 元数据时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#region IPlugin
/// <summary>
/// 插件名称
/// </summary>
public override string Name => _pluginName;
/// <summary>
/// 插件描述
/// </summary>
public override string Description => $"{_pluginName} (ICCPP 格式插件)";
/// <summary>
/// 插件版本
/// </summary>
public override Version Version => _pluginVersion;
/// <summary>
/// 插件作者
/// </summary>
public override string Author => "未知";
/// <summary>
/// 是否为内置插件
/// </summary>
public override bool IsBuiltIn => false;
/// <summary>
/// 初始化插件
/// </summary>
public override void Initialize()
{
if (_isInitialized) return;
try
{
// 这里可以添加 .iccpp 插件的初始化逻辑
// 例如,根据文件格式加载特定资源
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已初始化");
_isInitialized = true;
}
@@ -112,19 +112,19 @@ namespace Ink_Canvas.Helpers.Plugins
LogHelper.WriteLogToFile($"初始化 ICCPP 插件 {Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 启用插件
/// </summary>
public override void Enable()
{
if (IsEnabled) return;
try
{
// 这里可以添加 .iccpp 插件的启用逻辑
// 例如,加载动态库、注册事件等
base.Enable(); // 设置启用状态并触发事件
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已启用");
}
@@ -133,19 +133,19 @@ namespace Ink_Canvas.Helpers.Plugins
LogHelper.WriteLogToFile($"启用 ICCPP 插件 {Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 禁用插件
/// </summary>
public override void Disable()
{
if (!IsEnabled) return;
try
{
// 这里可以添加 .iccpp 插件的禁用逻辑
// 例如,卸载动态库、注销事件等
base.Disable(); // 设置禁用状态并触发事件
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已禁用");
}
@@ -154,7 +154,7 @@ namespace Ink_Canvas.Helpers.Plugins
LogHelper.WriteLogToFile($"禁用 ICCPP 插件 {Name} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 清理插件资源
/// </summary>
@@ -164,7 +164,7 @@ namespace Ink_Canvas.Helpers.Plugins
{
// 这里可以添加 .iccpp 插件的清理逻辑
// 例如,释放资源等
LogHelper.WriteLogToFile($"ICCPP 插件 {Name} 已清理资源");
}
catch (Exception ex)
@@ -172,7 +172,7 @@ namespace Ink_Canvas.Helpers.Plugins
LogHelper.WriteLogToFile($"清理 ICCPP 插件 {Name} 资源时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
#endregion
}
}
}
@@ -0,0 +1,48 @@
using System.Windows.Controls;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 增强的插件接口,提供对插件服务的访问
/// </summary>
public interface IEnhancedPlugin : IPlugin
{
/// <summary>
/// 获取插件服务实例
/// </summary>
IPluginService PluginService { get; }
/// <summary>
/// 插件启动时调用,在Initialize之后
/// </summary>
void OnStartup();
/// <summary>
/// 插件关闭时调用,在Cleanup之前
/// </summary>
void OnShutdown();
/// <summary>
/// 获取插件的菜单项
/// </summary>
/// <returns>菜单项集合</returns>
MenuItem[] GetMenuItems();
/// <summary>
/// 获取插件的工具栏按钮
/// </summary>
/// <returns>工具栏按钮集合</returns>
Button[] GetToolbarButtons();
/// <summary>
/// 获取插件的状态栏信息
/// </summary>
/// <returns>状态栏信息</returns>
string GetStatusBarInfo();
/// <summary>
/// 插件配置变更时调用
/// </summary>
void OnConfigurationChanged();
}
}
+214
View File
@@ -0,0 +1,214 @@
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 获取服务接口,统一所有获取类的方法
/// </summary>
public interface IGetService
{
#region UI获取
/// <summary>
/// 获取主窗口引用
/// </summary>
Window MainWindow { get; }
/// <summary>
/// 获取当前画布
/// </summary>
InkCanvas CurrentCanvas { get; }
/// <summary>
/// 获取所有画布页面
/// </summary>
List<Canvas> AllCanvasPages { get; }
/// <summary>
/// 获取当前页面索引
/// </summary>
int CurrentPageIndex { get; }
/// <summary>
/// 获取当前页面数量
/// </summary>
int TotalPageCount { get; }
/// <summary>
/// 获取浮动工具栏
/// </summary>
FrameworkElement FloatingToolBar { get; }
/// <summary>
/// 获取左侧面板
/// </summary>
FrameworkElement LeftPanel { get; }
/// <summary>
/// 获取右侧面板
/// </summary>
FrameworkElement RightPanel { get; }
/// <summary>
/// 获取顶部面板
/// </summary>
FrameworkElement TopPanel { get; }
/// <summary>
/// 获取底部面板
/// </summary>
FrameworkElement BottomPanel { get; }
#endregion
#region
/// <summary>
/// 获取当前绘制模式
/// </summary>
int CurrentDrawingMode { get; }
/// <summary>
/// 获取当前笔触宽度
/// </summary>
double CurrentInkWidth { get; }
/// <summary>
/// 获取当前笔触颜色
/// </summary>
Color CurrentInkColor { get; }
/// <summary>
/// 获取当前高亮笔宽度
/// </summary>
double CurrentHighlighterWidth { get; }
/// <summary>
/// 获取当前橡皮擦大小
/// </summary>
int CurrentEraserSize { get; }
/// <summary>
/// 获取当前橡皮擦类型
/// </summary>
int CurrentEraserType { get; }
/// <summary>
/// 获取当前橡皮擦形状
/// </summary>
int CurrentEraserShape { get; }
/// <summary>
/// 获取当前笔触透明度
/// </summary>
double CurrentInkAlpha { get; }
/// <summary>
/// 获取当前笔触样式
/// </summary>
int CurrentInkStyle { get; }
/// <summary>
/// 获取当前背景颜色
/// </summary>
string CurrentBackgroundColor { get; }
#endregion
#region
/// <summary>
/// 获取当前主题模式
/// </summary>
bool IsDarkTheme { get; }
/// <summary>
/// 获取当前是否为白板模式
/// </summary>
bool IsWhiteboardMode { get; }
/// <summary>
/// 获取当前是否为PPT模式
/// </summary>
bool IsPPTMode { get; }
/// <summary>
/// 获取当前是否为全屏模式
/// </summary>
bool IsFullScreenMode { get; }
/// <summary>
/// 获取当前是否为画板模式
/// </summary>
bool IsCanvasMode { get; }
/// <summary>
/// 获取当前是否为选择模式
/// </summary>
bool IsSelectionMode { get; }
/// <summary>
/// 获取当前是否为擦除模式
/// </summary>
bool IsEraserMode { get; }
/// <summary>
/// 获取当前是否为形状绘制模式
/// </summary>
bool IsShapeDrawingMode { get; }
/// <summary>
/// 获取当前是否为高亮模式
/// </summary>
bool IsHighlighterMode { get; }
#endregion
#region
/// <summary>
/// 获取是否可以撤销
/// </summary>
bool CanUndo { get; }
/// <summary>
/// 获取是否可以重做
/// </summary>
bool CanRedo { get; }
#endregion
#region
/// <summary>
/// 获取系统设置
/// </summary>
/// <typeparam name="T">设置类型</typeparam>
/// <param name="key">设置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>设置值</returns>
T GetSetting<T>(string key, T defaultValue = default(T));
#endregion
#region
/// <summary>
/// 获取所有已加载的插件
/// </summary>
/// <returns>插件列表</returns>
List<IPlugin> GetAllPlugins();
/// <summary>
/// 获取指定插件
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <returns>插件实例</returns>
IPlugin GetPlugin(string pluginName);
#endregion
}
}
+10 -10
View File
@@ -12,56 +12,56 @@ namespace Ink_Canvas.Helpers.Plugins
/// 插件名称
/// </summary>
string Name { get; }
/// <summary>
/// 插件描述
/// </summary>
string Description { get; }
/// <summary>
/// 插件版本
/// </summary>
Version Version { get; }
/// <summary>
/// 插件作者
/// </summary>
string Author { get; }
/// <summary>
/// 是否为内置插件
/// </summary>
bool IsBuiltIn { get; }
/// <summary>
/// 初始化插件
/// 此方法在插件加载时被调用,用于执行一些初始化工作
/// </summary>
void Initialize();
/// <summary>
/// 启用插件
/// 此方法在插件被用户或系统启用时调用,激活插件功能
/// </summary>
void Enable();
/// <summary>
/// 禁用插件
/// 此方法在插件被用户或系统禁用时调用,停用插件功能
/// </summary>
void Disable();
/// <summary>
/// 获取插件设置界面
/// 此方法返回插件的设置界面控件,用于展示在设置窗口
/// </summary>
/// <returns>插件设置界面</returns>
UserControl GetSettingsView();
/// <summary>
/// 插件卸载时的清理工作
/// 此方法在插件被卸载前调用,用于释放资源和执行清理
/// </summary>
void Cleanup();
}
}
}
@@ -0,0 +1,38 @@
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件服务接口,提供对软件内部功能的访问
/// 继承自三个专门的服务接口:获取服务、窗口服务、操作服务
/// </summary>
public interface IPluginService : IGetService, IWindowService, IActionService
{
// 这个接口现在继承自三个专门的服务接口
// 所有方法都在子接口中定义,这里不需要重复定义
}
/// <summary>
/// 通知类型枚举
/// </summary>
public enum NotificationType
{
/// <summary>
/// 信息
/// </summary>
Info,
/// <summary>
/// 成功
/// </summary>
Success,
/// <summary>
/// 警告
/// </summary>
Warning,
/// <summary>
/// 错误
/// </summary>
Error
}
}
@@ -0,0 +1,152 @@
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 窗口服务接口,统一所有窗口操作相关的方法
/// </summary>
public interface IWindowService
{
#region
/// <summary>
/// 显示设置窗口
/// </summary>
void ShowSettingsWindow();
/// <summary>
/// 隐藏设置窗口
/// </summary>
void HideSettingsWindow();
/// <summary>
/// 显示插件设置窗口
/// </summary>
void ShowPluginSettingsWindow();
/// <summary>
/// 隐藏插件设置窗口
/// </summary>
void HidePluginSettingsWindow();
/// <summary>
/// 显示帮助窗口
/// </summary>
void ShowHelpWindow();
/// <summary>
/// 隐藏帮助窗口
/// </summary>
void HideHelpWindow();
/// <summary>
/// 显示关于窗口
/// </summary>
void ShowAboutWindow();
/// <summary>
/// 隐藏关于窗口
/// </summary>
void HideAboutWindow();
#endregion
#region
/// <summary>
/// 显示通知消息
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="type">消息类型</param>
void ShowNotification(string message, NotificationType type = NotificationType.Info);
/// <summary>
/// 显示确认对话框
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="title">标题</param>
/// <returns>用户选择结果</returns>
bool ShowConfirmDialog(string message, string title = "确认");
/// <summary>
/// 显示输入对话框
/// </summary>
/// <param name="message">提示消息</param>
/// <param name="title">标题</param>
/// <param name="defaultValue">默认值</param>
/// <returns>用户输入内容</returns>
string ShowInputDialog(string message, string title = "输入", string defaultValue = "");
#endregion
#region
/// <summary>
/// 设置窗口全屏状态
/// </summary>
/// <param name="isFullScreen">是否全屏</param>
void SetFullScreen(bool isFullScreen);
/// <summary>
/// 设置窗口置顶状态
/// </summary>
/// <param name="isTopMost">是否置顶</param>
void SetTopMost(bool isTopMost);
/// <summary>
/// 设置窗口可见性
/// </summary>
/// <param name="isVisible">是否可见</param>
void SetWindowVisibility(bool isVisible);
/// <summary>
/// 最小化窗口
/// </summary>
void MinimizeWindow();
/// <summary>
/// 最大化窗口
/// </summary>
void MaximizeWindow();
/// <summary>
/// 恢复窗口
/// </summary>
void RestoreWindow();
/// <summary>
/// 关闭窗口
/// </summary>
void CloseWindow();
#endregion
#region
/// <summary>
/// 设置窗口位置
/// </summary>
/// <param name="x">X坐标</param>
/// <param name="y">Y坐标</param>
void SetWindowPosition(double x, double y);
/// <summary>
/// 设置窗口大小
/// </summary>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
void SetWindowSize(double width, double height);
/// <summary>
/// 获取窗口位置
/// </summary>
/// <returns>窗口位置</returns>
(double x, double y) GetWindowPosition();
/// <summary>
/// 获取窗口大小
/// </summary>
/// <returns>窗口大小</returns>
(double width, double height) GetWindowSize();
#endregion
}
}
+10 -10
View File
@@ -12,11 +12,11 @@ namespace Ink_Canvas.Helpers.Plugins
/// 插件状态(私有字段)
/// </summary>
private bool _isEnabled;
/// <summary>
/// 插件状态(公共属性)
/// </summary>
public bool IsEnabled
public bool IsEnabled
{
get => _isEnabled;
protected set
@@ -28,12 +28,12 @@ namespace Ink_Canvas.Helpers.Plugins
}
}
}
/// <summary>
/// 插件ID
/// </summary>
public string Id { get; protected set; }
/// <summary>
/// 插件路径
/// </summary>
@@ -75,13 +75,13 @@ namespace Ink_Canvas.Helpers.Plugins
public virtual void Initialize()
{
Id = GetType().FullName;
// 添加日志,记录插件名称
try
{
string name = Name;
LogHelper.WriteLogToFile($"初始化插件: ID={Id}, 名称={name ?? ""}");
if (string.IsNullOrEmpty(name))
{
LogHelper.WriteLogToFile($"警告: 插件 {Id} 的名称为空", LogHelper.LogType.Warning);
@@ -91,7 +91,7 @@ namespace Ink_Canvas.Helpers.Plugins
{
LogHelper.WriteLogToFile($"获取插件名称时出错: {ex.Message}", LogHelper.LogType.Error);
}
LogHelper.WriteLogToFile($"插件 {Name} 已初始化");
}
@@ -136,7 +136,7 @@ namespace Ink_Canvas.Helpers.Plugins
{
LogHelper.WriteLogToFile($"插件 {Name} 已卸载");
}
/// <summary>
/// 保存插件自身的设置
/// 注意:此方法仅用于保存插件的特定设置,不应影响插件启用/禁用状态
@@ -148,7 +148,7 @@ namespace Ink_Canvas.Helpers.Plugins
// 子类可以重写此方法,将自身设置保存到配置文件中
LogHelper.WriteLogToFile($"插件 {Name} 设置已保存", LogHelper.LogType.Event);
}
/// <summary>
/// 触发状态变更事件
/// </summary>
@@ -158,4 +158,4 @@ namespace Ink_Canvas.Helpers.Plugins
EnabledStateChanged?.Invoke(this, isEnabled);
}
}
}
}
@@ -0,0 +1,273 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件配置管理器,允许插件管理自己的配置
/// </summary>
public class PluginConfigurationManager
{
private static readonly string PluginConfigDirectory = Path.Combine(App.RootPath, "PluginConfigs");
private static readonly Dictionary<string, Dictionary<string, object>> _pluginConfigs = new Dictionary<string, Dictionary<string, object>>();
private static readonly object _lockObject = new object();
static PluginConfigurationManager()
{
// 确保配置目录存在
if (!Directory.Exists(PluginConfigDirectory))
{
Directory.CreateDirectory(PluginConfigDirectory);
}
}
/// <summary>
/// 获取插件配置值
/// </summary>
/// <typeparam name="T">配置值类型</typeparam>
/// <param name="pluginName">插件名称</param>
/// <param name="key">配置键</param>
/// <param name="defaultValue">默认值</param>
/// <returns>配置值</returns>
public static T GetConfiguration<T>(string pluginName, string key, T defaultValue = default(T))
{
lock (_lockObject)
{
try
{
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
{
if (pluginConfig.TryGetValue(key, out var value))
{
if (value is T typedValue)
{
return typedValue;
}
// 尝试类型转换
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
return defaultValue;
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
return defaultValue;
}
}
/// <summary>
/// 设置插件配置值
/// </summary>
/// <typeparam name="T">配置值类型</typeparam>
/// <param name="pluginName">插件名称</param>
/// <param name="key">配置键</param>
/// <param name="value">配置值</param>
public static void SetConfiguration<T>(string pluginName, string key, T value)
{
lock (_lockObject)
{
try
{
if (!_pluginConfigs.ContainsKey(pluginName))
{
_pluginConfigs[pluginName] = new Dictionary<string, object>();
}
_pluginConfigs[pluginName][key] = value;
// 异步保存配置
Task.Run(() => SavePluginConfiguration(pluginName));
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"设置插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 删除插件配置
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <param name="key">配置键</param>
public static void RemoveConfiguration(string pluginName, string key)
{
lock (_lockObject)
{
try
{
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
{
if (pluginConfig.Remove(key))
{
// 异步保存配置
Task.Run(() => SavePluginConfiguration(pluginName));
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"删除插件 {pluginName} 配置 {key} 时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 获取插件的所有配置
/// </summary>
/// <param name="pluginName">插件名称</param>
/// <returns>配置字典</returns>
public static Dictionary<string, object> GetAllConfigurations(string pluginName)
{
lock (_lockObject)
{
if (_pluginConfigs.TryGetValue(pluginName, out var pluginConfig))
{
return new Dictionary<string, object>(pluginConfig);
}
return new Dictionary<string, object>();
}
}
/// <summary>
/// 清除插件的所有配置
/// </summary>
/// <param name="pluginName">插件名称</param>
public static void ClearAllConfigurations(string pluginName)
{
lock (_lockObject)
{
try
{
if (_pluginConfigs.Remove(pluginName))
{
// 删除配置文件
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
if (File.Exists(configFile))
{
File.Delete(configFile);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清除插件 {pluginName} 所有配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
/// <summary>
/// 加载插件配置
/// </summary>
/// <param name="pluginName">插件名称</param>
public static void LoadPluginConfiguration(string pluginName)
{
try
{
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
if (File.Exists(configFile))
{
string json = File.ReadAllText(configFile);
var config = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
lock (_lockObject)
{
_pluginConfigs[pluginName] = config ?? new Dictionary<string, object>();
}
LogHelper.WriteLogToFile($"已加载插件 {pluginName} 的配置");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载插件 {pluginName} 配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 保存插件配置
/// </summary>
/// <param name="pluginName">插件名称</param>
private static void SavePluginConfiguration(string pluginName)
{
try
{
Dictionary<string, object> pluginConfig;
lock (_lockObject)
{
if (!_pluginConfigs.TryGetValue(pluginName, out pluginConfig))
{
return;
}
}
string configFile = Path.Combine(PluginConfigDirectory, $"{pluginName}.json");
string json = JsonConvert.SerializeObject(pluginConfig, Formatting.Indented);
File.WriteAllText(configFile, json);
LogHelper.WriteLogToFile($"已保存插件 {pluginName} 的配置");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存插件 {pluginName} 配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 加载所有插件的配置
/// </summary>
public static void LoadAllPluginConfigurations()
{
try
{
if (Directory.Exists(PluginConfigDirectory))
{
string[] configFiles = Directory.GetFiles(PluginConfigDirectory, "*.json");
foreach (string configFile in configFiles)
{
string pluginName = Path.GetFileNameWithoutExtension(configFile);
LoadPluginConfiguration(pluginName);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载所有插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 保存所有插件的配置
/// </summary>
public static void SaveAllPluginConfigurations()
{
try
{
lock (_lockObject)
{
foreach (string pluginName in _pluginConfigs.Keys)
{
SavePluginConfiguration(pluginName);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"保存所有插件配置时出错: {ex.Message}", LogHelper.LogType.Error);
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,509 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Ink_Canvas.Helpers.Plugins
{
/// <summary>
/// 插件服务管理器,实现IPluginService接口,提供对软件内部功能的访问
/// </summary>
public class PluginServiceManager : IPluginService
{
private static PluginServiceManager _instance;
private MainWindow _mainWindow;
private Dictionary<string, EventHandler> _eventHandlers;
/// <summary>
/// 单例实例
/// </summary>
public static PluginServiceManager Instance
{
get
{
if (_instance == null)
{
_instance = new PluginServiceManager();
}
return _instance;
}
}
private PluginServiceManager()
{
_eventHandlers = new Dictionary<string, EventHandler>();
}
/// <summary>
/// 设置主窗口引用
/// </summary>
/// <param name="mainWindow">主窗口实例</param>
public void SetMainWindow(MainWindow mainWindow)
{
_mainWindow = mainWindow;
}
#region UI访问
public Window MainWindow => _mainWindow;
public InkCanvas CurrentCanvas => null; // 暂时返回null,避免访问权限问题
public List<Canvas> AllCanvasPages => new List<Canvas>(); // 暂时返回空列表
public int CurrentPageIndex => 0; // 暂时返回0
public int TotalPageCount => 0; // 暂时返回0
public FrameworkElement FloatingToolBar => _mainWindow?.ViewboxFloatingBar;
public FrameworkElement LeftPanel => _mainWindow?.BlackboardLeftSide;
public FrameworkElement RightPanel => _mainWindow?.BlackboardRightSide;
public FrameworkElement TopPanel => _mainWindow?.BorderTools;
public FrameworkElement BottomPanel => _mainWindow?.BorderSettings;
#endregion
#region
public int CurrentDrawingMode => 0; // 暂时返回0
public double CurrentInkWidth => 2.5; // 暂时返回默认值
public Color CurrentInkColor => Colors.Black; // 暂时返回默认值
public double CurrentHighlighterWidth => 20.0; // 暂时返回默认值
public int CurrentEraserSize => 2; // 暂时返回默认值
public int CurrentEraserType => 0; // 暂时返回默认值
public int CurrentEraserShape => 0; // 暂时返回默认值
public double CurrentInkAlpha => 255.0; // 暂时返回默认值
public int CurrentInkStyle => 0; // 暂时返回默认值
public string CurrentBackgroundColor => "#162924"; // 暂时返回默认值
#endregion
#region
public bool IsDarkTheme => false; // 暂时返回默认值
public bool IsWhiteboardMode => false; // 暂时返回默认值
public bool IsPPTMode => false; // 暂时返回默认值
public bool IsFullScreenMode => false; // 暂时返回默认值
public bool IsCanvasMode => true; // 暂时返回默认值
public bool IsSelectionMode => false; // 暂时返回默认值
public bool IsEraserMode => false; // 暂时返回默认值
public bool IsShapeDrawingMode => false; // 暂时返回默认值
public bool IsHighlighterMode => false; // 暂时返回默认值
#endregion
#region IGetService
public bool CanUndo => false; // 暂时返回默认值
public bool CanRedo => false; // 暂时返回默认值
public T GetSetting<T>(string key, T defaultValue = default(T))
{
// 暂时不实现,避免访问权限问题
return defaultValue;
}
public List<IPlugin> GetAllPlugins()
{
return new List<IPlugin>(PluginManager.Instance.Plugins);
}
public IPlugin GetPlugin(string pluginName)
{
return PluginManager.Instance.Plugins.FirstOrDefault(p => p.Name == pluginName);
}
#endregion
#region IWindowService
public void ShowSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HideSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowPluginSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HidePluginSettingsWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowHelpWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HideHelpWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowAboutWindow()
{
// 暂时不实现,避免访问权限问题
}
public void HideAboutWindow()
{
// 暂时不实现,避免访问权限问题
}
public void ShowNotification(string message, NotificationType type = NotificationType.Info)
{
// 暂时不实现,避免访问权限问题
}
public bool ShowConfirmDialog(string message, string title = "确认")
{
// 暂时不实现,避免访问权限问题
return false;
}
public string ShowInputDialog(string message, string title = "输入", string defaultValue = "")
{
// 暂时不实现,避免访问权限问题
return defaultValue;
}
public void SetFullScreen(bool isFullScreen)
{
// 暂时不实现,避免访问权限问题
}
public void SetTopMost(bool isTopMost)
{
// 暂时不实现,避免访问权限问题
}
public void SetWindowVisibility(bool isVisible)
{
// 暂时不实现,避免访问权限问题
}
public void MinimizeWindow()
{
// 暂时不实现,避免访问权限问题
}
public void MaximizeWindow()
{
// 暂时不实现,避免访问权限问题
}
public void RestoreWindow()
{
// 暂时不实现,避免访问权限问题
}
public void CloseWindow()
{
// 暂时不实现,避免访问权限问题
}
public void SetWindowPosition(double x, double y)
{
// 暂时不实现,避免访问权限问题
}
public void SetWindowSize(double width, double height)
{
// 暂时不实现,避免访问权限问题
}
public (double x, double y) GetWindowPosition()
{
// 暂时不实现,避免访问权限问题
return (0, 0);
}
public (double width, double height) GetWindowSize()
{
// 暂时不实现,避免访问权限问题
return (800, 600);
}
#endregion
#region IActionService
public void ClearCanvas()
{
// 暂时不实现,避免访问权限问题
}
public void ClearAllCanvases()
{
// 暂时不实现,避免访问权限问题
}
public void AddNewPage()
{
// 暂时不实现,避免访问权限问题
}
public void DeleteCurrentPage()
{
// 暂时不实现,避免访问权限问题
}
public void SwitchToPage(int pageIndex)
{
// 暂时不实现,避免访问权限问题
}
public void NextPage()
{
// 暂时不实现,避免访问权限问题
}
public void PreviousPage()
{
// 暂时不实现,避免访问权限问题
}
public void SetDrawingMode(int mode)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkWidth(double width)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkColor(Color color)
{
// 暂时不实现,避免访问权限问题
}
public void SetHighlighterWidth(double width)
{
// 暂时不实现,避免访问权限问题
}
public void SetEraserSize(int size)
{
// 暂时不实现,避免访问权限问题
}
public void SetEraserType(int type)
{
// 暂时不实现,避免访问权限问题
}
public void SetEraserShape(int shape)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkAlpha(double alpha)
{
// 暂时不实现,避免访问权限问题
}
public void SetInkStyle(int style)
{
// 暂时不实现,避免访问权限问题
}
public void SetBackgroundColor(string color)
{
// 暂时不实现,避免访问权限问题
}
public void SaveCanvas(string filePath)
{
// 暂时不实现,避免访问权限问题
}
public void LoadCanvas(string filePath)
{
// 暂时不实现,避免访问权限问题
}
public void ExportAsImage(string filePath, string format)
{
// 暂时不实现,避免访问权限问题
}
public void ExportAsPDF(string filePath)
{
// 暂时不实现,避免访问权限问题
}
public void Undo()
{
// 暂时不实现,避免访问权限问题
}
public void Redo()
{
// 暂时不实现,避免访问权限问题
}
public void SelectAll()
{
// 暂时不实现,避免访问权限问题
}
public void DeselectAll()
{
// 暂时不实现,避免访问权限问题
}
public void DeleteSelected()
{
// 暂时不实现,避免访问权限问题
}
public void CopySelected()
{
// 暂时不实现,避免访问权限问题
}
public void CutSelected()
{
// 暂时不实现,避免访问权限问题
}
public void Paste()
{
// 暂时不实现,避免访问权限问题
}
public void SetSetting<T>(string key, T value)
{
// 暂时不实现,避免访问权限问题
}
public void SaveSettings()
{
// 暂时不实现,避免访问权限问题
}
public void LoadSettings()
{
// 暂时不实现,避免访问权限问题
}
public void ResetSettings()
{
// 暂时不实现,避免访问权限问题
}
public void EnablePlugin(string pluginName)
{
var plugin = GetPlugin(pluginName);
if (plugin != null)
{
PluginManager.Instance.TogglePlugin(plugin, true);
}
}
public void DisablePlugin(string pluginName)
{
var plugin = GetPlugin(pluginName);
if (plugin != null)
{
PluginManager.Instance.TogglePlugin(plugin, false);
}
}
public void UnloadPlugin(string pluginName)
{
var plugin = GetPlugin(pluginName);
if (plugin != null)
{
PluginManager.Instance.UnloadPlugin(plugin);
}
}
public void RegisterEventHandler(string eventName, EventHandler handler)
{
if (!_eventHandlers.ContainsKey(eventName))
{
_eventHandlers[eventName] = handler;
}
else
{
_eventHandlers[eventName] += handler;
}
}
public void UnregisterEventHandler(string eventName, EventHandler handler)
{
if (_eventHandlers.ContainsKey(eventName))
{
_eventHandlers[eventName] -= handler;
}
}
public void TriggerEvent(string eventName, object sender, EventArgs args)
{
if (_eventHandlers.ContainsKey(eventName))
{
_eventHandlers[eventName]?.Invoke(sender, args);
}
}
public void RestartApplication()
{
// 暂时不实现,避免访问权限问题
}
public void ExitApplication()
{
// 暂时不实现,避免访问权限问题
}
public void CheckForUpdates()
{
// 暂时不实现,避免访问权限问题
}
public void OpenHelpDocument()
{
// 暂时不实现,避免访问权限问题
}
public void OpenAboutPage()
{
// 暂时不实现,避免访问权限问题
}
#endregion
}
}
+18 -18
View File
@@ -49,18 +49,18 @@ namespace Ink_Canvas.Helpers.Plugins
{
// 先调用基类方法,这样会设置插件ID和记录日志
base.Initialize();
// TODO: 在这里进行插件初始化工作
// 示例:记录初始化信息
LogHelper.WriteLogToFile($"插件 {Name} 开始初始化");
// 示例:加载配置
LoadConfig();
// 示例:注册自定义事件
// MainWindow.Instance.SomeEvent += OnSomeEvent;
LogHelper.WriteLogToFile($"插件 {Name} 初始化完成");
}
@@ -72,9 +72,9 @@ namespace Ink_Canvas.Helpers.Plugins
{
// 先调用基类方法,这样会设置插件状态和记录日志
base.Enable();
// TODO: 在这里启用插件功能
LogHelper.WriteLogToFile($"插件 {Name} 已启用");
}
@@ -86,9 +86,9 @@ namespace Ink_Canvas.Helpers.Plugins
{
// 先调用基类方法,这样会设置插件状态和记录日志
base.Disable();
// TODO: 在这里禁用插件功能
LogHelper.WriteLogToFile($"插件 {Name} 已禁用");
}
@@ -99,13 +99,13 @@ namespace Ink_Canvas.Helpers.Plugins
public override void Cleanup()
{
// TODO: 在这里清理插件资源
// 示例:取消注册事件
// MainWindow.Instance.SomeEvent -= OnSomeEvent;
// 示例:保存配置
SaveConfig();
// 最后调用基类方法
base.Cleanup();
}
@@ -186,7 +186,7 @@ namespace Ink_Canvas.Helpers.Plugins
public void DoSomething()
{
if (!IsEnabled) return;
try
{
// TODO: 实现你的功能
@@ -245,7 +245,7 @@ namespace Ink_Canvas.Helpers.Plugins
Text = "设置项:",
Margin = new Thickness(0, 5, 0, 5)
});
panel.Children.Add(new TextBox
{
Margin = new Thickness(0, 0, 0, 10),
@@ -261,16 +261,16 @@ namespace Ink_Canvas.Helpers.Plugins
Margin = new Thickness(0, 10, 0, 0),
HorizontalAlignment = HorizontalAlignment.Left
};
button.Click += (sender, e) =>
button.Click += (sender, e) =>
{
MessageBox.Show("设置已保存!", "插件模板", MessageBoxButton.OK, MessageBoxImage.Information);
};
panel.Children.Add(button);
// 设置控件内容
Content = panel;
}
}
}
}
+2 -2
View File
@@ -1,8 +1,8 @@
using System;
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Ink_Canvas.Helpers
{
+21 -10
View File
@@ -1,7 +1,9 @@
using System.Windows.Automation;
namespace Ink_Canvas.Helpers {
internal class WinTabWindowsChecker {
namespace Ink_Canvas.Helpers
{
internal class WinTabWindowsChecker
{
/*
public static bool IsWindowMinimized(string windowName, bool matchFullName = true) {
// 获取Win+Tab预览中的窗口
@@ -40,28 +42,37 @@ namespace Ink_Canvas.Helpers {
}
*/
public static bool IsWindowExisted(string windowName, bool matchFullName = true) {
public static bool IsWindowExisted(string windowName, bool matchFullName = true)
{
// 获取Win+Tab预览中的窗口
AutomationElementCollection windows = AutomationElement.RootElement.FindAll(
TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window));
foreach (AutomationElement window in windows) {
foreach (AutomationElement window in windows)
{
//LogHelper.WriteLogToFile("" + window.Current.Name);
string windowTitle = window.Current.Name;
// 如果窗口标题包含 windowName,则进行检查
if (!string.IsNullOrEmpty(windowTitle) && windowTitle.Contains(windowName)) {
if (matchFullName) {
if (windowTitle.Length == windowName.Length) {
if (!string.IsNullOrEmpty(windowTitle) && windowTitle.Contains(windowName))
{
if (matchFullName)
{
if (windowTitle.Length == windowName.Length)
{
WindowPattern windowPattern = window.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
if (windowPattern != null) {
if (windowPattern != null)
{
return true;
}
}
} else {
}
else
{
WindowPattern windowPattern = window.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
if (windowPattern != null) {
if (windowPattern != null)
{
return true;
}
}
+263
View File
@@ -0,0 +1,263 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace Ink_Canvas.Helpers
{
/// <summary>
/// 窗口Z-Order管理器,用于管理窗口的层级顺序
/// 在无焦点模式下,确保后打开的窗口能够置顶于先打开的窗口
/// </summary>
public static class WindowZOrderManager
{
#region Win32 API
private const int GWL_EXSTYLE = -20;
private const int WS_EX_TOPMOST = 0x00000008;
private const int WS_EX_NOACTIVATE = 0x08000000;
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
private const uint SWP_NOMOVE = 0x0002;
private const uint SWP_NOSIZE = 0x0001;
private const uint SWP_NOACTIVATE = 0x0010;
private const uint SWP_SHOWWINDOW = 0x0040;
private const uint SWP_NOOWNERZORDER = 0x0200;
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
private static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentProcessId();
#endregion
// 窗口层级管理
private static readonly List<WindowInfo> _windowStack = new List<WindowInfo>();
private static readonly object _lockObject = new object();
/// <summary>
/// 窗口信息类
/// </summary>
private class WindowInfo
{
public IntPtr Handle { get; set; }
public Window Window { get; set; }
public DateTime CreatedTime { get; set; }
public bool IsTopmost { get; set; }
public bool IsNoFocusMode { get; set; }
}
/// <summary>
/// 注册窗口到Z-Order管理器
/// </summary>
/// <param name="window">要注册的窗口</param>
/// <param name="isTopmost">是否置顶</param>
/// <param name="isNoFocusMode">是否无焦点模式</param>
public static void RegisterWindow(Window window, bool isTopmost = false, bool isNoFocusMode = false)
{
if (window == null) return;
lock (_lockObject)
{
var hwnd = new WindowInteropHelper(window).Handle;
if (hwnd == IntPtr.Zero) return;
// 移除已存在的记录
_windowStack.RemoveAll(w => w.Handle == hwnd);
// 添加新记录
var windowInfo = new WindowInfo
{
Handle = hwnd,
Window = window,
CreatedTime = DateTime.Now,
IsTopmost = isTopmost,
IsNoFocusMode = isNoFocusMode
};
_windowStack.Add(windowInfo);
// 应用Z-Order
ApplyZOrder();
}
}
/// <summary>
/// 从Z-Order管理器中移除窗口
/// </summary>
/// <param name="window">要移除的窗口</param>
public static void UnregisterWindow(Window window)
{
if (window == null) return;
lock (_lockObject)
{
var hwnd = new WindowInteropHelper(window).Handle;
_windowStack.RemoveAll(w => w.Handle == hwnd);
ApplyZOrder();
}
}
/// <summary>
/// 设置窗口为置顶状态
/// </summary>
/// <param name="window">要置顶的窗口</param>
/// <param name="isTopmost">是否置顶</param>
public static void SetWindowTopmost(Window window, bool isTopmost)
{
if (window == null) return;
lock (_lockObject)
{
var windowInfo = _windowStack.FirstOrDefault(w => w.Window == window);
if (windowInfo != null)
{
windowInfo.IsTopmost = isTopmost;
ApplyZOrder();
}
}
}
/// <summary>
/// 将窗口移到最顶层
/// </summary>
/// <param name="window">要移到最顶层的窗口</param>
public static void BringToTop(Window window)
{
if (window == null) return;
try
{
var hwnd = new WindowInteropHelper(window).Handle;
if (hwnd == IntPtr.Zero) return;
// 使用更直接的方法:先激活窗口,再置顶
window.Activate();
window.Focus();
// 设置窗口为置顶
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
// 确保窗口样式正确
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
// 再次确保置顶
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"BringToTop失败: {ex.Message}", LogHelper.LogType.Error);
}
}
/// <summary>
/// 应用Z-Order排序
/// </summary>
private static void ApplyZOrder()
{
// 简化逻辑:直接设置所有窗口为置顶,让Windows系统自然处理层级
foreach (var windowInfo in _windowStack.ToList())
{
if (windowInfo.IsTopmost && IsWindow(windowInfo.Handle) && IsWindowVisible(windowInfo.Handle) && !IsIconic(windowInfo.Handle))
{
// 设置窗口为置顶
SetWindowPos(windowInfo.Handle, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
// 确保窗口样式正确
int exStyle = GetWindowLong(windowInfo.Handle, GWL_EXSTYLE);
if ((exStyle & WS_EX_TOPMOST) == 0)
{
SetWindowLong(windowInfo.Handle, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
}
}
}
}
/// <summary>
/// 检查是否有子窗口在前景
/// </summary>
/// <returns>如果有子窗口在前景返回true</returns>
public static bool HasChildWindowInForeground()
{
lock (_lockObject)
{
var foregroundWindow = GetForegroundWindow();
if (foregroundWindow == IntPtr.Zero) return false;
return _windowStack.Any(w => w.Handle == foregroundWindow);
}
}
/// <summary>
/// 清理无效的窗口记录
/// </summary>
public static void CleanupInvalidWindows()
{
lock (_lockObject)
{
_windowStack.RemoveAll(w => !IsWindow(w.Handle) || !IsWindowVisible(w.Handle));
}
}
/// <summary>
/// 获取当前注册的窗口数量
/// </summary>
/// <returns>窗口数量</returns>
public static int GetWindowCount()
{
lock (_lockObject)
{
return _windowStack.Count;
}
}
/// <summary>
/// 强制刷新所有窗口的置顶状态
/// </summary>
public static void ForceRefreshAllWindows()
{
lock (_lockObject)
{
foreach (var windowInfo in _windowStack.ToList())
{
if (windowInfo.IsTopmost && IsWindow(windowInfo.Handle))
{
// 强制设置窗口为置顶
SetWindowPos(windowInfo.Handle, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
// 确保窗口样式正确
int exStyle = GetWindowLong(windowInfo.Handle, GWL_EXSTYLE);
SetWindowLong(windowInfo.Handle, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
}
}
}
}
}
}
+96
View File
@@ -0,0 +1,96 @@
{
"Version": "1.0",
"LastModified": "2025-01-28T15:30:00",
"Hotkeys": [
{
"Name": "Undo",
"Key": "Z",
"Modifiers": "Control"
},
{
"Name": "Redo",
"Key": "Y",
"Modifiers": "Control"
},
{
"Name": "Clear",
"Key": "E",
"Modifiers": "Control"
},
{
"Name": "Paste",
"Key": "V",
"Modifiers": "Control"
},
{
"Name": "SelectTool",
"Key": "S",
"Modifiers": "Alt"
},
{
"Name": "DrawTool",
"Key": "D",
"Modifiers": "Alt"
},
{
"Name": "EraserTool",
"Key": "E",
"Modifiers": "Alt"
},
{
"Name": "BlackboardTool",
"Key": "B",
"Modifiers": "Alt"
},
{
"Name": "QuitDrawTool",
"Key": "Q",
"Modifiers": "Alt"
},
{
"Name": "Pen1",
"Key": "D1",
"Modifiers": "Alt"
},
{
"Name": "Pen2",
"Key": "D2",
"Modifiers": "Alt"
},
{
"Name": "Pen3",
"Key": "D3",
"Modifiers": "Alt"
},
{
"Name": "Pen4",
"Key": "D4",
"Modifiers": "Alt"
},
{
"Name": "Pen5",
"Key": "D5",
"Modifiers": "Alt"
},
{
"Name": "DrawLine",
"Key": "L",
"Modifiers": "Alt"
},
{
"Name": "Screenshot",
"Key": "C",
"Modifiers": "Alt"
},
{
"Name": "Hide",
"Key": "V",
"Modifiers": "Alt"
},
{
"Name": "Exit",
"Key": "Escape",
"Modifiers": "None"
}
]
}
+4
View File
@@ -128,6 +128,7 @@
<Reference Include="System.Windows.Forms" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Management" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml" />
<Reference Include="UIAutomationClient" />
@@ -157,6 +158,8 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
<PackageReference Include="OSVersionExt" Version="3.0.0" />
<PackageReference Include="AForge.Video" Version="2.2.5" />
<PackageReference Include="AForge.Video.DirectShow" Version="2.2.5" />
</ItemGroup>
<ItemGroup>
<COMReference Include="IWshRuntimeLibrary">
@@ -251,6 +254,7 @@
<Resource Include="Resources\DeveloperAvatars\kengwang.png" />
<Resource Include="Resources\DeveloperAvatars\STBBRD.png" />
<Resource Include="Resources\DeveloperAvatars\WXRIW.png" />
<Resource Include="Resources\DeveloperAvatars\PrefacedCorg.jpg" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Icons-png\down.png" />
@@ -1,449 +0,0 @@
<Project>
<PropertyGroup>
<AssemblyName>InkCanvasForClass</AssemblyName>
<IntermediateOutputPath>obj\Debug\</IntermediateOutputPath>
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<MSBuildProjectExtensionsPath>C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\</MSBuildProjectExtensionsPath>
<_TargetAssemblyProjectName>InkCanvasForClass</_TargetAssemblyProjectName>
<RootNamespace>Ink_Canvas</RootNamespace>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk.WindowsDesktop" />
<PropertyGroup>
<RuntimeIdentifiers>win;win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<OutputType>WinExe</OutputType>
<RootNamespace>Ink_Canvas</RootNamespace>
<TargetFramework>net472</TargetFramework>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>2</ApplicationRevision>
<ApplicationVersion>2.0.2.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>false</BootstrapperEnabled>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<UseWPF>true</UseWPF>
<Configurations>Debug;Release;x86 Debug</Configurations>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>embedded</DebugType>
<OutputPath>bin\$(Configuration)\</OutputPath>
<Prefer32Bit>True</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86 Debug|AnyCPU'">
<DebugType>embedded</DebugType>
<OutputPath>bin\$(Configuration)\</OutputPath>
<Prefer32Bit>True</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>embedded</DebugType>
<OutputPath>bin\$(Configuration)\</OutputPath>
<Prefer32Bit>True</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\icc.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<DebugType>full</DebugType>
<LangVersion>7.3</LangVersion>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86 Debug|x86'">
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<DebugType>full</DebugType>
<LangVersion>7.3</LangVersion>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<DebugType>pdbonly</DebugType>
<LangVersion>7.3</LangVersion>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Title>InkCanvasForClass</Title>
<Version>5.0.4</Version>
<Authors>Dubi906w</Authors>
<Product>InkCanvasForClass</Product>
<Copyright>© Copyright HARKOTEK Studio 2024-now</Copyright>
<PackageProjectUrl>https://icc.bliemhax.com</PackageProjectUrl>
<FileVersion>bundled</FileVersion>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM64'">
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<DebugType>full</DebugType>
<LangVersion>7.3</LangVersion>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86 Debug|ARM64'">
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<DebugType>full</DebugType>
<LangVersion>7.3</LangVersion>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM64'">
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<DebugType>pdbonly</DebugType>
<LangVersion>7.3</LangVersion>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<DebugType>full</DebugType>
<LangVersion>7.3</LangVersion>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='x86 Debug|x64'">
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<DebugType>full</DebugType>
<LangVersion>7.3</LangVersion>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\$(Platform)\$(Configuration)\</OutputPath>
<DebugType>pdbonly</DebugType>
<LangVersion>7.3</LangVersion>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<None Include="app.manifest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
<PackageReference Include="iNKORE.UI.WPF.Modern" Version="0.9.27" />
<PackageReference Include="MdXaml" Version="1.27.0" />
<PackageReference Include="Microsoft.Office.Interop.PowerPoint" Version="15.0.4420.1018" />
<PackageReference Include="MicrosoftOfficeCore" Version="15.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
<PackageReference Include="OSVersionExt" Version="3.0.0" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<COMReference Include="IWshRuntimeLibrary">
<Guid>{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
<COMReference Include="stdole">
<Guid>{00020430-0000-0000-C000-000000000046}</Guid>
<VersionMajor>2</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>primary</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
<COMReference Include="VBIDE">
<Guid>{0002E157-0000-0000-C000-000000000046}</Guid>
<VersionMajor>5</VersionMajor>
<VersionMinor>3</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>primary</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<None Include="Resources\TimerDownNotice.wav" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<Compile Remove="AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="MainWindow.xaml~RF6c3144.TMP" />
<None Remove="Resources\Cursors\Cursor.cur" />
<None Remove="Resources\Cursors\Pen.cur" />
<None Remove="Resources\DeveloperAvatars\aaaaaaccd.jpg" />
<None Remove="Resources\DeveloperAvatars\Alan-CRL.png" />
<None Remove="Resources\DeveloperAvatars\NetheriteBowl.png" />
<None Remove="Resources\DeveloperAvatars\NotYoojun.png" />
<None Remove="Resources\DeveloperAvatars\RaspberryKan.jpg" />
<None Remove="Resources\DeveloperAvatars\wwei.png" />
<None Remove="Resources\DeveloperAvatars\yuwenhui2020.png" />
<None Remove="Resources\icc.ico" />
<None Remove="Resources\Icons-png\AdmoxBooth.png" />
<None Remove="Resources\Icons-png\AdmoxWhiteboard.png" />
<None Remove="Resources\Icons-png\Donview.png" />
<None Remove="Resources\Icons-png\EasiNote3.png" />
<None Remove="Resources\Icons-png\HiteAnnotation.png" />
<None Remove="Resources\Icons-png\HiteLightBoard.png" />
<None Remove="Resources\Icons-png\ica.png" />
<None Remove="Resources\Icons-png\icc-transparent-dark-small.png" />
<None Remove="Resources\Icons-png\icc-transparent-dark.png" />
<None Remove="Resources\Icons-png\icc-transparent.png" />
<None Remove="Resources\Icons-png\icc.png" />
<None Remove="Resources\Icons-png\idt.png" />
<None Remove="Resources\Icons-png\InkCanvas.png" />
<None Remove="Resources\Icons-png\kuanciya.png" />
<None Remove="Resources\Icons-png\kuandogeyuanliangwo.png" />
<None Remove="Resources\Icons-png\kuandoujiyanhuaji.png" />
<None Remove="Resources\Icons-png\kuanneikuhuaji.png" />
<None Remove="Resources\Icons-png\kuanshounvhuaji.png" />
<None Remove="Resources\Icons-png\MaxHubWhiteboard.png" />
<None Remove="Resources\Icons-png\Seewo2Annotation.png" />
<None Remove="Resources\Icons-png\tiebahuaji.png" />
<None Remove="Resources\Icons-png\transparent-grid.png" />
<None Remove="Resources\Icons-png\VComYouJiao.png" />
<None Remove="Resources\Icons-png\WenXiang.png" />
<None Remove="Resources\Icons-png\YiYunVisualPresenter.png" />
<None Remove="Resources\Icons-png\YiYunWhiteboard.png" />
<None Remove="Resources\new-icons\chevron-left.png" />
<None Remove="Resources\new-icons\end-slides-show.png" />
<None Remove="Resources\new-icons\eye.png" />
<None Remove="Resources\new-icons\hand-move.png" />
<None Remove="Resources\new-icons\highlighter-white.png" />
<None Remove="Resources\new-icons\multi-touch.png" />
<None Remove="Resources\new-icons\osu-lazer-triangles.png" />
<None Remove="Resources\new-icons\pen-white.png" />
<None Remove="Resources\new-icons\rotate.png" />
<None Remove="Resources\new-icons\unfold-chevron.png" />
<None Remove="Resources\new-icons\zoom.png" />
<None Remove="Resources\PresentationExample\bottombar-dark.png" />
<None Remove="Resources\PresentationExample\bottombar-white.png" />
<None Remove="Resources\PresentationExample\page.jpg" />
<None Remove="Resources\PresentationExample\sidebar-dark.png" />
<None Remove="Resources\PresentationExample\sidebar-white.png" />
<None Remove="Resources\PresentationExample\toolbar.png" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\hardcodet.notifyicon.wpf\1.1.0\lib\net472\Hardcodet.NotifyIcon.Wpf.dll" />
<ReferencePath Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\IACore.dll" />
<ReferencePath Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\IALoader.dll" />
<ReferencePath Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\IAWinFX.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\avalonedit\6.3.0.90\lib\net462\ICSharpCode.AvalonEdit.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\inkore.ui.wpf.modern\0.9.27\lib\net452\iNKORE.UI.WPF.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\inkore.ui.wpf.modern\0.9.27\lib\net452\iNKORE.UI.WPF.Modern.Controls.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\inkore.ui.wpf.modern\0.9.27\lib\net452\iNKORE.UI.WPF.Modern.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\mdxaml\1.27.0\lib\net462\MdXaml.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\mdxaml.plugins\1.27.0\lib\net462\MdXaml.Plugins.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\Microsoft.CSharp.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\microsoft.office.interop.powerpoint\15.0.4420.1018\lib\net20\Microsoft.Office.Interop.PowerPoint.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\Microsoft.VisualBasic.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\mscorlib.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\Facades\netstandard.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\newtonsoft.json\13.0.3\lib\net45\Newtonsoft.Json.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\nhotkey\3.0.0\lib\net462\NHotkey.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\nhotkey.wpf\3.0.0\lib\net462\NHotkey.Wpf.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\microsoftofficecore\15.0.0\lib\net35\Office.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\osversionext\3.0.0\lib\net462\OSVersionExt.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\PresentationCore.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\PresentationFramework.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Core.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Data.DataSetExtensions.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Data.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Drawing.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.IO.Compression.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.IO.Compression.FileSystem.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Net.Http.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Numerics.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Runtime.Serialization.dll" />
<ReferencePath Include="C:\Users\Administrator\.nuget\packages\system.valuetuple\4.5.0\ref\net47\System.ValueTuple.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Windows.Controls.Ribbon.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Windows.Forms.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Xaml.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Xml.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Xml.Linq.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\UIAutomationClient.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\UIAutomationClientsideProviders.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\UIAutomationProvider.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\UIAutomationTypes.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\WindowsBase.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\WindowsFormsIntegration.dll" />
<ReferencePath Include="C:\Windows\assembly\GAC\stdole\7.0.3300.0__b03f5f7f11d50a3a\stdole.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</ReferencePath>
<ReferencePath Include="C:\Windows\assembly\GAC_MSIL\Microsoft.Vbe.Interop\15.0.0.0__71e9bce111e9429c\Microsoft.Vbe.Interop.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</ReferencePath>
<ReferencePath Include="obj\Debug\net472\Interop.IWshRuntimeLibrary.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</ReferencePath>
</ItemGroup>
<ItemGroup>
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\MainWindow.g.cs" />
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\CountdownTimerWindow.g.cs" />
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\CycleProcessBar.g.cs" />
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\HasNewUpdateWindow.g.cs" />
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\NamesInputWindow.g.cs" />
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\OperatingGuideWindow.g.cs" />
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\RandWindow.g.cs" />
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\Windows\YesOrNoNotificationWindow.g.cs" />
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\App.g.cs" />
<Compile Include="C:\Users\Administrator\Desktop\ICC CE\ICC CE main\community\Ink Canvas\obj\Debug\net472\GeneratedInternalTypeHelper.g.cs" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk.WindowsDesktop" />
</Project>
+1752 -295
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+144 -53
View File
@@ -1,4 +1,6 @@
using System;
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@@ -6,15 +8,16 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Ink_Canvas.Helpers;
using iNKORE.UI.WPF.Modern;
namespace Ink_Canvas {
public partial class MainWindow : Window {
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
public bool isFloatingBarFolded;
private bool isFloatingBarChangingHideMode;
private void CloseWhiteboardImmediately() {
private void CloseWhiteboardImmediately()
{
if (isDisplayingOrHidingBlackboard) return;
isDisplayingOrHidingBlackboard = true;
HideSubPanelsImmediately();
@@ -28,13 +31,15 @@ namespace Ink_Canvas {
BtnSwitch_Click(BtnSwitch, null);
BtnExit.Foreground = Brushes.White;
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
new Thread(() => {
new Thread(() =>
{
Thread.Sleep(200);
Application.Current.Dispatcher.Invoke(() => { isDisplayingOrHidingBlackboard = false; });
}).Start();
}
public async void FoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e) {
public async void FoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e)
{
await FoldFloatingBar(sender);
}
@@ -42,7 +47,8 @@ namespace Ink_Canvas {
{
var isShouldRejectAction = false;
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
if (lastBorderMouseDownObject != null && lastBorderMouseDownObject is Panel)
((Panel)lastBorderMouseDownObject).Background = new SolidColorBrush(Colors.Transparent);
if (sender == Fold_Icon && lastBorderMouseDownObject != Fold_Icon) isShouldRejectAction = true;
@@ -59,7 +65,8 @@ namespace Ink_Canvas {
if (isFloatingBarChangingHideMode) return;
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
InkCanvasForInkReplay.Visibility = Visibility.Collapsed;
InkCanvasGridForInkReplay.Visibility = Visibility.Visible;
InkCanvasGridForInkReplay.IsHitTestVisible = true;
@@ -71,7 +78,8 @@ namespace Ink_Canvas {
isStopInkReplay = true;
});
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
isFloatingBarChangingHideMode = true;
isFloatingBarFolded = true;
if (currentMode != 0) CloseWhiteboardImmediately();
@@ -84,7 +92,8 @@ namespace Ink_Canvas {
await Task.Delay(300);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
@@ -97,12 +106,16 @@ namespace Ink_Canvas {
isFloatingBarChangingHideMode = false;
}
private async void LeftUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e) {
if (Settings.Appearance.IsShowQuickPanel) {
private async void LeftUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e)
{
if (Settings.Appearance.IsShowQuickPanel)
{
HideRightQuickPanel();
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Visible;
await Dispatcher.InvokeAsync(() => {
var marginAnimation = new ThicknessAnimation {
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = new Thickness(-50, 0, 0, -150),
To = new Thickness(-1, 0, 0, -150)
@@ -112,21 +125,27 @@ namespace Ink_Canvas {
});
await Task.Delay(100);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
LeftUnFoldButtonQuickPanel.Margin = new Thickness(-1, 0, 0, -150);
});
}
else {
else
{
UnFoldFloatingBar_MouseUp(sender, e);
}
}
private async void RightUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e) {
if (Settings.Appearance.IsShowQuickPanel) {
private async void RightUnFoldButtonDisplayQuickPanel_MouseUp(object sender, MouseButtonEventArgs e)
{
if (Settings.Appearance.IsShowQuickPanel)
{
HideLeftQuickPanel();
RightUnFoldButtonQuickPanel.Visibility = Visibility.Visible;
await Dispatcher.InvokeAsync(() => {
var marginAnimation = new ThicknessAnimation {
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = new Thickness(0, 0, -50, -150),
To = new Thickness(0, 0, -1, -150)
@@ -136,19 +155,25 @@ namespace Ink_Canvas {
});
await Task.Delay(100);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
RightUnFoldButtonQuickPanel.Margin = new Thickness(0, 0, -1, -150);
});
}
else {
else
{
UnFoldFloatingBar_MouseUp(sender, e);
}
}
private async void HideLeftQuickPanel() {
if (LeftUnFoldButtonQuickPanel.Visibility == Visibility.Visible) {
await Dispatcher.InvokeAsync(() => {
var marginAnimation = new ThicknessAnimation {
private async void HideLeftQuickPanel()
{
if (LeftUnFoldButtonQuickPanel.Visibility == Visibility.Visible)
{
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = new Thickness(-1, 0, 0, -150),
To = new Thickness(-50, 0, 0, -150)
@@ -158,17 +183,22 @@ namespace Ink_Canvas {
});
await Task.Delay(100);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
LeftUnFoldButtonQuickPanel.Margin = new Thickness(0, 0, -50, -150);
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
});
}
}
private async void HideRightQuickPanel() {
if (RightUnFoldButtonQuickPanel.Visibility == Visibility.Visible) {
await Dispatcher.InvokeAsync(() => {
var marginAnimation = new ThicknessAnimation {
private async void HideRightQuickPanel()
{
if (RightUnFoldButtonQuickPanel.Visibility == Visibility.Visible)
{
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
From = new Thickness(0, 0, -1, -150),
To = new Thickness(0, 0, -50, -150)
@@ -178,25 +208,29 @@ namespace Ink_Canvas {
});
await Task.Delay(100);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
RightUnFoldButtonQuickPanel.Margin = new Thickness(0, 0, -50, -150);
RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
});
}
}
private void HideQuickPanel_MouseUp(object sender, MouseButtonEventArgs e) {
private void HideQuickPanel_MouseUp(object sender, MouseButtonEventArgs e)
{
HideLeftQuickPanel();
HideRightQuickPanel();
}
public async void UnFoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e) {
public async void UnFoldFloatingBar_MouseUp(object sender, MouseButtonEventArgs e)
{
await UnFoldFloatingBar(sender);
}
public async Task UnFoldFloatingBar(object sender)
{
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
LeftUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
RightUnFoldButtonQuickPanel.Visibility = Visibility.Collapsed;
});
@@ -208,53 +242,109 @@ namespace Ink_Canvas {
if (isFloatingBarChangingHideMode) return;
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
isFloatingBarChangingHideMode = true;
isFloatingBarFolded = false;
});
await Task.Delay(0);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
// 根据设置决定是否自动切换至批注模式
if (Settings.Automation.IsAutoEnterAnnotationModeWhenExitFoldMode && currentMode == 0)
{
// 切换至批注模式
PenIcon_Click(null, null);
}
if (StackPanelPPTControls.Visibility == Visibility.Visible)
// 只有在PPT放映模式下且页数有效时才显示翻页按钮
if (StackPanelPPTControls.Visibility == Visibility.Visible &&
BtnPPTSlideShowEnd.Visibility == Visibility.Visible &&
PPTManager?.IsInSlideShow == true &&
PPTManager?.SlidesCount > 0)
{
var dops = Settings.PowerPointSettings.PPTButtonsDisplayOption.ToString();
var dopsc = dops.ToCharArray();
if (dopsc[0] == '2' && isDisplayingOrHidingBlackboard == false) AnimationsHelper.ShowWithFadeIn(LeftBottomPanelForPPTNavigation);
if (dopsc[1] == '2' && isDisplayingOrHidingBlackboard == false) AnimationsHelper.ShowWithFadeIn(RightBottomPanelForPPTNavigation);
if (dopsc[2] == '2' && isDisplayingOrHidingBlackboard == false) AnimationsHelper.ShowWithFadeIn(LeftSidePanelForPPTNavigation);
if (dopsc[3] == '2' && isDisplayingOrHidingBlackboard == false) AnimationsHelper.ShowWithFadeIn(RightSidePanelForPPTNavigation);
if (dopsc[0] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(LeftBottomPanelForPPTNavigation);
if (dopsc[1] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(RightBottomPanelForPPTNavigation);
if (dopsc[2] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(LeftSidePanelForPPTNavigation);
if (dopsc[3] == '2' && !isDisplayingOrHidingBlackboard) AnimationsHelper.ShowWithFadeIn(RightSidePanelForPPTNavigation);
LogHelper.WriteLogToFile($"从收纳模式恢复时显示PPT翻页按钮 - 放映状态: {PPTManager?.IsInSlideShow}, 页数: {PPTManager?.SlidesCount}", LogHelper.LogType.Trace);
}
else
{
// 如果条件不满足,确保隐藏翻页按钮
LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed;
LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed;
}
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible)
ViewboxFloatingBarMarginAnimation(60);
else
ViewboxFloatingBarMarginAnimation(100, true);
// 新只在屏幕模式下显示浮动栏
if (currentMode == 0)
{
// 强制更新布局以确保ActualWidth正确
ViewboxFloatingBar.UpdateLayout();
// 等待一小段时间让布局完全更新
Task.Delay(50);
// 再次强制更新布局
ViewboxFloatingBar.UpdateLayout();
// 强制重新测量和排列
ViewboxFloatingBar.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
ViewboxFloatingBar.Arrange(new Rect(ViewboxFloatingBar.DesiredSize));
if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible)
ViewboxFloatingBarMarginAnimation(60);
else
ViewboxFloatingBarMarginAnimation(100, true);
}
SidePannelMarginAnimation(-50, !unfoldFloatingBarByUser);
});
// 修复:在浮动栏展开后,重新设置按钮高亮状态
await Dispatcher.InvokeAsync(async () =>
{
try
{
// 等待UI完全更新
await Task.Delay(100);
// 获取当前选中的模式并重新设置高光位置
string selectedToolMode = GetCurrentSelectedMode();
if (!string.IsNullOrEmpty(selectedToolMode))
{
SetFloatingBarHighlightPosition(selectedToolMode);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"浮动栏展开后重新设置按钮高亮状态失败: {ex.Message}", LogHelper.LogType.Error);
}
});
isFloatingBarChangingHideMode = false;
}
private async void SidePannelMarginAnimation(int MarginFromEdge, bool isNoAnimation = false) // Possible value: -50, -10
{
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
if (MarginFromEdge == -10) LeftSidePanel.Visibility = Visibility.Visible;
var LeftSidePanelmarginAnimation = new ThicknessAnimation {
var LeftSidePanelmarginAnimation = new ThicknessAnimation
{
Duration = isNoAnimation ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(0.175),
From = LeftSidePanel.Margin,
To = new Thickness(MarginFromEdge, 0, 0, -150)
};
LeftSidePanelmarginAnimation.EasingFunction = new CubicEase();
var RightSidePanelmarginAnimation = new ThicknessAnimation {
var RightSidePanelmarginAnimation = new ThicknessAnimation
{
Duration = isNoAnimation ? TimeSpan.FromSeconds(0) : TimeSpan.FromSeconds(0.175),
From = RightSidePanel.Margin,
To = new Thickness(0, 0, MarginFromEdge, -150)
@@ -266,7 +356,8 @@ namespace Ink_Canvas {
await Task.Delay(600);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
LeftSidePanel.Margin = new Thickness(MarginFromEdge, 0, 0, -150);
RightSidePanel.Margin = new Thickness(0, 0, MarginFromEdge, -150);
+14 -8
View File
@@ -1,13 +1,17 @@
using System;
using IWshRuntimeLibrary;
using System;
using System.Windows;
using IWshRuntimeLibrary;
using Application = System.Windows.Forms.Application;
using File = System.IO.File;
namespace Ink_Canvas {
public partial class MainWindow : Window {
public static bool StartAutomaticallyCreate(string exeName) {
try {
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
public static bool StartAutomaticallyCreate(string exeName)
{
try
{
var shell = new WshShell();
var shortcut = (IWshShortcut)shell.CreateShortcut(
Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\" + exeName + ".lnk");
@@ -30,8 +34,10 @@ namespace Ink_Canvas {
return false;
}
public static bool StartAutomaticallyDel(string exeName) {
try {
public static bool StartAutomaticallyDel(string exeName)
{
try
{
File.Delete(Environment.GetFolderPath(Environment.SpecialFolder.Startup) + "\\" + exeName +
".lnk");
return true;
+28 -19
View File
@@ -1,50 +1,55 @@
using System;
using iNKORE.UI.WPF.Modern;
using Microsoft.Win32;
using System;
using System.Windows;
using System.Windows.Media;
using iNKORE.UI.WPF.Modern;
using Microsoft.Win32;
using Application = System.Windows.Application;
namespace Ink_Canvas {
public partial class MainWindow : Window {
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
private Color FloatBarForegroundColor = Color.FromRgb(102, 102, 102);
private void SetTheme(string theme) {
if (theme == "Light") {
private void SetTheme(string theme)
{
if (theme == "Light")
{
var rd1 = new ResourceDictionary
{ Source = new Uri("Resources/Styles/Light.xaml", UriKind.Relative) };
{ Source = new Uri("Resources/Styles/Light.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd1);
var rd2 = new ResourceDictionary
{ Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative) };
{ Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd2);
var rd3 = new ResourceDictionary
{ Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative) };
{ Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd3);
var rd4 = new ResourceDictionary
{ Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative) };
{ Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd4);
ThemeManager.SetRequestedTheme(window, ElementTheme.Light);
FloatBarForegroundColor = (Color)Application.Current.FindResource("FloatBarForegroundColor");
}
else if (theme == "Dark") {
else if (theme == "Dark")
{
var rd1 = new ResourceDictionary { Source = new Uri("Resources/Styles/Dark.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd1);
var rd2 = new ResourceDictionary
{ Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative) };
{ Source = new Uri("Resources/DrawShapeImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd2);
var rd3 = new ResourceDictionary
{ Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative) };
{ Source = new Uri("Resources/SeewoImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd3);
var rd4 = new ResourceDictionary
{ Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative) };
{ Source = new Uri("Resources/IconImageDictionary.xaml", UriKind.Relative) };
Application.Current.Resources.MergedDictionaries.Add(rd4);
ThemeManager.SetRequestedTheme(window, ElementTheme.Dark);
@@ -53,8 +58,10 @@ namespace Ink_Canvas {
}
}
private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) {
switch (Settings.Appearance.Theme) {
private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
{
switch (Settings.Appearance.Theme)
{
case 0:
SetTheme("Light");
break;
@@ -68,9 +75,11 @@ namespace Ink_Canvas {
}
}
private bool IsSystemThemeLight() {
private bool IsSystemThemeLight()
{
var light = false;
try {
try
{
var registryKey = Registry.CurrentUser;
var themeKey =
registryKey.OpenSubKey("software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
+197 -45
View File
@@ -1,19 +1,17 @@
using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
namespace Ink_Canvas {
public partial class MainWindow : Window {
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
private StrokeCollection[] strokeCollections = new StrokeCollection[101];
private bool[] whiteboadLastModeIsRedo = new bool[101];
private StrokeCollection lastTouchDownStrokeCollection = new StrokeCollection();
@@ -23,24 +21,102 @@ namespace Ink_Canvas {
private TimeMachineHistory[][] TimeMachineHistories = new TimeMachineHistory[101][]; //最多99页,0用来存储非白板时的墨迹以便还原
// 保存每页白板图片信息
private void SaveStrokes(bool isBackupMain = false) {
if (isBackupMain) {
private void SaveStrokes(bool isBackupMain = false)
{
// 确保画布上的所有UI元素都被保存到时间机器历史记录中
var currentHistory = timeMachine.ExportTimeMachineHistory();
var elementsInHistory = new HashSet<UIElement>();
// 收集已经在历史记录中的元素
if (currentHistory != null)
{
foreach (var h in currentHistory)
{
if (h.CommitType == TimeMachineHistoryType.ElementInsert &&
h.InsertedElement != null &&
!h.StrokeHasBeenCleared)
{
elementsInHistory.Add(h.InsertedElement);
}
}
}
// 检查画布上的所有UI元素,确保它们都在历史记录中
var missingElements = 0;
foreach (UIElement child in inkCanvas.Children)
{
if (child is Image || child is MediaElement)
{
if (!elementsInHistory.Contains(child))
{
timeMachine.CommitElementInsertHistory(child);
missingElements++;
}
}
}
// 确保画布上的所有墨迹都被保存
if (inkCanvas.Strokes.Count > 0)
{
// 检查是否有墨迹没有在时间机器历史记录中
var strokesInHistory = new HashSet<Stroke>();
if (currentHistory != null)
{
foreach (var h in currentHistory)
{
if (h.CommitType == TimeMachineHistoryType.UserInput &&
h.CurrentStroke != null &&
!h.StrokeHasBeenCleared)
{
foreach (Stroke stroke in h.CurrentStroke)
{
strokesInHistory.Add(stroke);
}
}
}
}
// 收集没有在历史记录中的墨迹
var missingStrokes = new StrokeCollection();
foreach (Stroke stroke in inkCanvas.Strokes)
{
if (!strokesInHistory.Contains(stroke))
{
missingStrokes.Add(stroke);
}
}
if (missingStrokes.Count > 0)
{
timeMachine.CommitStrokeUserInputHistory(missingStrokes);
}
}
if (isBackupMain)
{
var timeMachineHistory = timeMachine.ExportTimeMachineHistory();
TimeMachineHistories[0] = timeMachineHistory;
timeMachine.ClearStrokeHistory();
} else {
}
else
{
var timeMachineHistory = timeMachine.ExportTimeMachineHistory();
TimeMachineHistories[CurrentWhiteboardIndex] = timeMachineHistory;
timeMachine.ClearStrokeHistory();
}
}
private void ClearStrokes(bool isErasedByCode) {
private void ClearStrokes(bool isErasedByCode)
{
_currentCommitType = CommitReason.ClearingCanvas;
if (isErasedByCode) _currentCommitType = CommitReason.CodeInput;
// 取消任何UI元素的选择,隐藏拉伸控件
DeselectUIElement();
// 只清除笔画,不清除图片元素
// 图片元素的清除由调用方决定
@@ -50,40 +126,67 @@ namespace Ink_Canvas {
}
// 恢复每页白板图片信息
private void RestoreStrokes(bool isBackupMain = false) {
try {
private void RestoreStrokes(bool isBackupMain = false)
{
try
{
// 隐藏图片选择工具栏
if (currentSelectedElement != null)
{
// 保存当前编辑模式
var previousEditingMode = inkCanvas.EditingMode;
UnselectElement(currentSelectedElement);
// 恢复编辑模式
inkCanvas.EditingMode = previousEditingMode;
currentSelectedElement = null;
}
var targetIndex = isBackupMain ? 0 : CurrentWhiteboardIndex;
// 先清空当前画布的所有内容(墨迹和图片)
// 这里必须清除图片,因为页面切换时需要完全重置画布状态
// 先清空当前画布的墨迹
inkCanvas.Strokes.Clear();
// 清空当前画布的所有内容(墨迹和图片)
// 这里必须清除图片,因为页面切换时需要完全重置画布状态
inkCanvas.Children.Clear();
// 如果历史记录为空,直接返回(新页面或空页面)
if (TimeMachineHistories[targetIndex] == null) {
if (TimeMachineHistories[targetIndex] == null)
{
timeMachine.ClearStrokeHistory();
return;
}
if (isBackupMain) {
if (isBackupMain)
{
timeMachine.ImportTimeMachineHistory(TimeMachineHistories[0]);
foreach (var item in TimeMachineHistories[0]) ApplyHistoryToCanvas(item);
} else {
}
else
{
timeMachine.ImportTimeMachineHistory(TimeMachineHistories[CurrentWhiteboardIndex]);
// 通过时间机器历史恢复所有内容(墨迹和图片)
foreach (var item in TimeMachineHistories[CurrentWhiteboardIndex]) ApplyHistoryToCanvas(item);
}
}
catch {
catch
{
// ignored
}
}
private async void BtnWhiteBoardPageIndex_Click(object sender, EventArgs e) {
if (sender == BtnLeftPageListWB) {
if (BoardBorderLeftPageListView.Visibility == Visibility.Visible) {
private async void BtnWhiteBoardPageIndex_Click(object sender, EventArgs e)
{
if (sender == BtnLeftPageListWB)
{
if (BoardBorderLeftPageListView.Visibility == Visibility.Visible)
{
AnimationsHelper.HideWithSlideAndFade(BoardBorderLeftPageListView);
} else {
}
else
{
AnimationsHelper.HideWithSlideAndFade(BoardBorderRightPageListView);
RefreshBlackBoardSidePageListView();
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardBorderLeftPageListView);
@@ -92,11 +195,15 @@ namespace Ink_Canvas {
(ListViewItem)BlackBoardLeftSidePageListView.ItemContainerGenerator.ContainerFromIndex(
CurrentWhiteboardIndex - 1), BlackBoardLeftSidePageListScrollViewer);
}
} else if (sender == BtnRightPageListWB)
}
else if (sender == BtnRightPageListWB)
{
if (BoardBorderRightPageListView.Visibility == Visibility.Visible) {
if (BoardBorderRightPageListView.Visibility == Visibility.Visible)
{
AnimationsHelper.HideWithSlideAndFade(BoardBorderRightPageListView);
} else {
}
else
{
AnimationsHelper.HideWithSlideAndFade(BoardBorderLeftPageListView);
RefreshBlackBoardSidePageListView();
AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardBorderRightPageListView);
@@ -109,11 +216,20 @@ namespace Ink_Canvas {
}
private void BtnWhiteBoardSwitchPrevious_Click(object sender, EventArgs e) {
private void BtnWhiteBoardSwitchPrevious_Click(object sender, EventArgs e)
{
if (CurrentWhiteboardIndex <= 1) return;
// 取消任何UI元素的选择
DeselectUIElement();
// 隐藏图片选择工具栏
if (currentSelectedElement != null)
{
// 保存当前编辑模式
var previousEditingMode = inkCanvas.EditingMode;
UnselectElement(currentSelectedElement);
// 恢复编辑模式
inkCanvas.EditingMode = previousEditingMode;
currentSelectedElement = null;
}
SaveStrokes();
@@ -125,19 +241,29 @@ namespace Ink_Canvas {
UpdateIndexInfoDisplay();
}
private void BtnWhiteBoardSwitchNext_Click(object sender, EventArgs e) {
private void BtnWhiteBoardSwitchNext_Click(object sender, EventArgs e)
{
Trace.WriteLine("113223234");
if (Settings.Automation.IsAutoSaveStrokesAtClear &&
inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber) SaveScreenShot(true);
if (CurrentWhiteboardIndex >= WhiteboardTotalCount) {
// 在最后一页时,点击“新页面”按钮直接新增一页
if (CurrentWhiteboardIndex >= WhiteboardTotalCount)
{
// 在最后一页时,点击"新页面"按钮直接新增一页
BtnWhiteBoardAdd_Click(sender, e);
return;
}
// 取消任何UI元素的选择
DeselectUIElement();
// 隐藏图片选择工具栏
if (currentSelectedElement != null)
{
// 保存当前编辑模式
var previousEditingMode = inkCanvas.EditingMode;
UnselectElement(currentSelectedElement);
// 恢复编辑模式
inkCanvas.EditingMode = previousEditingMode;
currentSelectedElement = null;
}
SaveStrokes();
@@ -149,13 +275,22 @@ namespace Ink_Canvas {
UpdateIndexInfoDisplay();
}
private void BtnWhiteBoardAdd_Click(object sender, EventArgs e) {
private void BtnWhiteBoardAdd_Click(object sender, EventArgs e)
{
if (WhiteboardTotalCount >= 99) return;
if (Settings.Automation.IsAutoSaveStrokesAtClear &&
inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber) SaveScreenShot(true);
// 取消任何UI元素的选择
DeselectUIElement();
// 隐藏图片选择工具栏
if (currentSelectedElement != null)
{
// 保存当前编辑模式
var previousEditingMode = inkCanvas.EditingMode;
UnselectElement(currentSelectedElement);
// 恢复编辑模式
inkCanvas.EditingMode = previousEditingMode;
currentSelectedElement = null;
}
SaveStrokes();
ClearStrokes(true);
@@ -177,12 +312,25 @@ namespace Ink_Canvas {
if (WhiteboardTotalCount >= 99) BtnWhiteBoardAdd.IsEnabled = false;
if (BlackBoardLeftSidePageListView.Visibility == Visibility.Visible) {
if (BlackBoardLeftSidePageListView.Visibility == Visibility.Visible)
{
RefreshBlackBoardSidePageListView();
}
}
private void BtnWhiteBoardDelete_Click(object sender, RoutedEventArgs e) {
private void BtnWhiteBoardDelete_Click(object sender, RoutedEventArgs e)
{
// 隐藏图片选择工具栏
if (currentSelectedElement != null)
{
// 保存当前编辑模式
var previousEditingMode = inkCanvas.EditingMode;
UnselectElement(currentSelectedElement);
// 恢复编辑模式
inkCanvas.EditingMode = previousEditingMode;
currentSelectedElement = null;
}
ClearStrokes(true);
if (CurrentWhiteboardIndex != WhiteboardTotalCount)
@@ -200,7 +348,8 @@ namespace Ink_Canvas {
if (WhiteboardTotalCount < 99) BtnWhiteBoardAdd.IsEnabled = true;
}
private void UpdateIndexInfoDisplay() {
private void UpdateIndexInfoDisplay()
{
TextBlockWhiteBoardIndexInfo.Text =
$"{CurrentWhiteboardIndex}/{WhiteboardTotalCount}";
@@ -222,13 +371,16 @@ namespace Ink_Canvas {
BtnWhiteBoardSwitchPrevious.IsEnabled = true;
if (CurrentWhiteboardIndex == 1) {
if (CurrentWhiteboardIndex == 1)
{
BtnWhiteBoardSwitchPrevious.IsEnabled = false;
BtnLeftWhiteBoardSwitchPreviousGeometry.Brush = new SolidColorBrush(Color.FromArgb(127, 24, 24, 27));
BtnLeftWhiteBoardSwitchPreviousLabel.Opacity = 0.5;
BtnRightWhiteBoardSwitchPreviousGeometry.Brush = new SolidColorBrush(Color.FromArgb(127, 24, 24, 27));
BtnRightWhiteBoardSwitchPreviousLabel.Opacity = 0.5;
} else {
}
else
{
BtnLeftWhiteBoardSwitchPreviousGeometry.Brush = new SolidColorBrush(Color.FromArgb(255, 24, 24, 27));
BtnLeftWhiteBoardSwitchPreviousLabel.Opacity = 1;
BtnRightWhiteBoardSwitchPreviousGeometry.Brush = new SolidColorBrush(Color.FromArgb(255, 24, 24, 27));
+371 -338
View File
@@ -1,4 +1,5 @@
using System;
using Ink_Canvas.Helpers;
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
@@ -6,11 +7,13 @@ using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Ink_Canvas.Helpers;
namespace Ink_Canvas {
public partial class MainWindow : Window {
private void BoardChangeBackgroundColorBtn_MouseUp(object sender, RoutedEventArgs e) {
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
private void BoardChangeBackgroundColorBtn_MouseUp(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
// 创建背景选项面板(如果不存在)
@@ -40,6 +43,7 @@ namespace Ink_Canvas {
AnimationsHelper.HideWithSlideAndFade(BoardEraserSizePanel);
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardImageOptionsPanel);
// 显示背景选项面板
AnimationsHelper.ShowWithSlideFromBottomAndFade(BackgroundPalette);
@@ -50,66 +54,68 @@ namespace Ink_Canvas {
// 原有的背景切换代码
Settings.Canvas.UsingWhiteboard = !Settings.Canvas.UsingWhiteboard;
SaveSettingsToFile();
if (Settings.Canvas.UsingWhiteboard) {
if (Settings.Canvas.UsingWhiteboard)
{
if (inkColor == 5) lastBoardInkColor = 0;
ICCWaterMarkDark.Visibility = Visibility.Visible;
ICCWaterMarkWhite.Visibility = Visibility.Collapsed;
// 设置为白板默认背景色
Color defaultWhiteboardColor = Color.FromRgb(234, 235, 237);
if (currentMode == 1) // 白板模式
{
// 设置背景为默认白板背景色
GridBackgroundCover.Background = new SolidColorBrush(defaultWhiteboardColor);
// 更新RGB滑块的值为默认白板背景色
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
{
UpdateRGBSliders(defaultWhiteboardColor);
}
// 更新自定义背景色为默认白板背景色
CustomBackgroundColor = defaultWhiteboardColor;
// 保存到设置
string colorHex = $"#{defaultWhiteboardColor.R:X2}{defaultWhiteboardColor.G:X2}{defaultWhiteboardColor.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
SaveSettingsToFile();
}
// 设置墨迹颜色为黑色
CheckLastColor(0);
forceEraser = false;
}
else {
else
{
if (inkColor == 0) lastBoardInkColor = 5;
ICCWaterMarkWhite.Visibility = Visibility.Visible;
ICCWaterMarkDark.Visibility = Visibility.Collapsed;
// 设置为黑板默认背景色
Color defaultBlackboardColor = Color.FromRgb(22, 41, 36);
if (currentMode == 1) // 黑板模式
{
// 设置背景为默认黑板背景色
GridBackgroundCover.Background = new SolidColorBrush(defaultBlackboardColor);
// 更新RGB滑块的值为默认黑板背景色
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
{
UpdateRGBSliders(defaultBlackboardColor);
}
// 更新自定义背景色为默认黑板背景色
CustomBackgroundColor = defaultBlackboardColor;
// 保存到设置
string colorHex = $"#{defaultBlackboardColor.R:X2}{defaultBlackboardColor.G:X2}{defaultBlackboardColor.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
SaveSettingsToFile();
}
// 设置墨迹颜色为白色
CheckLastColor(5);
forceEraser = false;
@@ -123,7 +129,7 @@ namespace Ink_Canvas {
{
// 确保加载自定义背景色
LoadCustomBackgroundColor();
// 创建一个类似于PenPalette的面板
BackgroundPalette = new Border
{
@@ -137,7 +143,7 @@ namespace Ink_Canvas {
Width = 300,
MaxHeight = 400
};
// 确保面板显示在顶层
Panel.SetZIndex(BackgroundPalette, 1000);
@@ -201,7 +207,7 @@ namespace Ink_Canvas {
contentPanel.Children.Add(modeTitle);
var modePanel = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center };
// 白板按钮
var whiteboardButton = new Border
{
@@ -219,41 +225,42 @@ namespace Ink_Canvas {
VerticalAlignment = VerticalAlignment.Center
};
whiteboardButton.Child = whiteboardText;
whiteboardButton.MouseUp += (s, args) => {
whiteboardButton.MouseUp += (s, args) =>
{
Settings.Canvas.UsingWhiteboard = true;
SaveSettingsToFile();
ICCWaterMarkDark.Visibility = Visibility.Visible;
ICCWaterMarkWhite.Visibility = Visibility.Collapsed;
// 设置为白板默认背景色
Color defaultWhiteboardColor = Color.FromRgb(234, 235, 237);
if (currentMode == 1) // 白板模式
{
// 设置背景为默认白板背景色
GridBackgroundCover.Background = new SolidColorBrush(defaultWhiteboardColor);
// 更新RGB滑块的值为默认白板背景色
UpdateRGBSliders(defaultWhiteboardColor);
// 更新自定义背景色为默认白板背景色
CustomBackgroundColor = defaultWhiteboardColor;
// 保存到设置
string colorHex = $"#{defaultWhiteboardColor.R:X2}{defaultWhiteboardColor.G:X2}{defaultWhiteboardColor.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
SaveSettingsToFile();
}
// 设置墨迹颜色为黑色
CheckLastColor(0);
forceEraser = false;
CheckColorTheme(true);
UpdateBackgroundButtonsState();
};
modePanel.Children.Add(whiteboardButton);
// 黑板按钮
var blackboardButton = new Border
{
@@ -270,264 +277,269 @@ namespace Ink_Canvas {
VerticalAlignment = VerticalAlignment.Center
};
blackboardButton.Child = blackboardText;
blackboardButton.MouseUp += (s, args) => {
blackboardButton.MouseUp += (s, args) =>
{
Settings.Canvas.UsingWhiteboard = false;
SaveSettingsToFile();
ICCWaterMarkWhite.Visibility = Visibility.Visible;
ICCWaterMarkDark.Visibility = Visibility.Collapsed;
// 设置为黑板默认背景色
Color defaultBlackboardColor = Color.FromRgb(22, 41, 36);
if (currentMode == 1) // 黑板模式
{
// 设置背景为默认黑板背景色
GridBackgroundCover.Background = new SolidColorBrush(defaultBlackboardColor);
// 更新RGB滑块的值为默认黑板背景色
UpdateRGBSliders(defaultBlackboardColor);
// 更新自定义背景色为默认黑板背景色
CustomBackgroundColor = defaultBlackboardColor;
// 保存到设置
string colorHex = $"#{defaultBlackboardColor.R:X2}{defaultBlackboardColor.G:X2}{defaultBlackboardColor.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
SaveSettingsToFile();
}
// 设置墨迹颜色为白色
CheckLastColor(5);
forceEraser = false;
CheckColorTheme(true);
UpdateBackgroundButtonsState();
};
modePanel.Children.Add(blackboardButton);
contentPanel.Children.Add(modePanel);
// 添加一条分隔线
var separator = new Border
{
Height = 1,
Background = new SolidColorBrush(Color.FromRgb(0xd4, 0xd4, 0xd8)),
Margin = new Thickness(0, 12, 0, 12)
};
contentPanel.Children.Add(separator);
// 添加RGB颜色选择器部分
var colorTitle = new TextBlock
{
Text = "背景颜色",
Foreground = new SolidColorBrush(Color.FromRgb(0x17, 0x25, 0x54)),
FontSize = 10,
FontWeight = FontWeights.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(0, 4, 0, 8)
};
contentPanel.Children.Add(colorTitle);
// 创建颜色预览
Border colorPreview = new Border
{
Width = 100,
Height = 40,
BorderThickness = new Thickness(1),
BorderBrush = new SolidColorBrush(Color.FromRgb(0xd4, 0xd4, 0xd8)),
Background = new SolidColorBrush(Colors.White),
CornerRadius = new CornerRadius(4),
Margin = new Thickness(0, 0, 0, 10),
HorizontalAlignment = HorizontalAlignment.Center
};
contentPanel.Children.Add(colorPreview);
// 获取当前背景颜色
Color currentBackgroundColor;
if (currentMode == 1) // 白板或黑板模式
{
if (GridBackgroundCover.Background is SolidColorBrush brush)
{
currentBackgroundColor = brush.Color;
}
else
{
// 默认颜色
currentBackgroundColor = Settings.Canvas.UsingWhiteboard ?
Color.FromRgb(234, 235, 237) : // 白板默认颜色
Color.FromRgb(22, 41, 36); // 黑板默认颜色
}
}
else
{
// 默认白色
currentBackgroundColor = Colors.White;
}
// 更新颜色预览
colorPreview.Background = new SolidColorBrush(currentBackgroundColor);
// 先创建所有滑块控件
// R滑块和文本框
var rPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
var rLabel = new TextBlock { Text = "R:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
var rSlider = new Slider
{
Minimum = 0,
Maximum = 255,
Value = currentBackgroundColor.R,
Width = 150,
Margin = new Thickness(5, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
var rValueText = new TextBlock
{
Text = currentBackgroundColor.R.ToString(),
Width = 30,
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Right
};
// G滑块和文本框
var gPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
var gLabel = new TextBlock { Text = "G:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
var gSlider = new Slider
{
Minimum = 0,
Maximum = 255,
Value = currentBackgroundColor.G,
Width = 150,
Margin = new Thickness(5, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
var gValueText = new TextBlock
{
Text = currentBackgroundColor.G.ToString(),
Width = 30,
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Right
};
// B滑块和文本框
var bPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
var bLabel = new TextBlock { Text = "B:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
var bSlider = new Slider
{
Minimum = 0,
Maximum = 255,
Value = currentBackgroundColor.B,
Width = 150,
Margin = new Thickness(5, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
var bValueText = new TextBlock
{
Text = currentBackgroundColor.B.ToString(),
Width = 30,
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Right
};
// 现在添加事件处理程序
rSlider.ValueChanged += (s, e) => {
int value = (int)e.NewValue;
rValueText.Text = value.ToString();
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
};
gSlider.ValueChanged += (s, e) => {
int value = (int)e.NewValue;
gValueText.Text = value.ToString();
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
};
bSlider.ValueChanged += (s, e) => {
int value = (int)e.NewValue;
bValueText.Text = value.ToString();
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
};
// 添加控件到面板
rPanel.Children.Add(rLabel);
rPanel.Children.Add(rSlider);
rPanel.Children.Add(rValueText);
contentPanel.Children.Add(rPanel);
gPanel.Children.Add(gLabel);
gPanel.Children.Add(gSlider);
gPanel.Children.Add(gValueText);
contentPanel.Children.Add(gPanel);
bPanel.Children.Add(bLabel);
bPanel.Children.Add(bSlider);
bPanel.Children.Add(bValueText);
contentPanel.Children.Add(bPanel);
// 应用按钮
var applyButton = new Button
{
Content = "应用颜色",
Margin = new Thickness(0, 10, 0, 0),
Padding = new Thickness(10, 5, 10, 5),
Background = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
Foreground = new SolidColorBrush(Colors.White),
BorderThickness = new Thickness(0),
HorizontalAlignment = HorizontalAlignment.Center
};
applyButton.Click += (s, e) => {
Color selectedColor = Color.FromRgb(
(byte)rSlider.Value,
(byte)gSlider.Value,
(byte)bSlider.Value
);
ApplyCustomBackgroundColor(selectedColor);
};
contentPanel.Children.Add(applyButton);
// 添加一条分隔线
var separator = new Border
{
Height = 1,
Background = new SolidColorBrush(Color.FromRgb(0xd4, 0xd4, 0xd8)),
Margin = new Thickness(0, 12, 0, 12)
};
contentPanel.Children.Add(separator);
// 添加RGB颜色选择器部分
var colorTitle = new TextBlock
{
Text = "背景颜色",
Foreground = new SolidColorBrush(Color.FromRgb(0x17, 0x25, 0x54)),
FontSize = 10,
FontWeight = FontWeights.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(0, 4, 0, 8)
};
contentPanel.Children.Add(colorTitle);
// 创建颜色预览
Border colorPreview = new Border
{
Width = 100,
Height = 40,
BorderThickness = new Thickness(1),
BorderBrush = new SolidColorBrush(Color.FromRgb(0xd4, 0xd4, 0xd8)),
Background = new SolidColorBrush(Colors.White),
CornerRadius = new CornerRadius(4),
Margin = new Thickness(0, 0, 0, 10),
HorizontalAlignment = HorizontalAlignment.Center
};
contentPanel.Children.Add(colorPreview);
// 获取当前背景颜色
Color currentBackgroundColor;
if (currentMode == 1) // 白板或黑板模式
{
if (GridBackgroundCover.Background is SolidColorBrush brush)
{
currentBackgroundColor = brush.Color;
}
else
{
// 默认颜色
currentBackgroundColor = Settings.Canvas.UsingWhiteboard ?
Color.FromRgb(234, 235, 237) : // 白板默认颜色
Color.FromRgb(22, 41, 36); // 黑板默认颜色
}
}
else
{
// 默认白色
currentBackgroundColor = Colors.White;
}
// 更新颜色预览
colorPreview.Background = new SolidColorBrush(currentBackgroundColor);
// 先创建所有滑块控件
// R滑块和文本框
var rPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
var rLabel = new TextBlock { Text = "R:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
var rSlider = new Slider
{
Minimum = 0,
Maximum = 255,
Value = currentBackgroundColor.R,
Width = 150,
Margin = new Thickness(5, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
var rValueText = new TextBlock
{
Text = currentBackgroundColor.R.ToString(),
Width = 30,
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Right
};
// G滑块和文本框
var gPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
var gLabel = new TextBlock { Text = "G:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
var gSlider = new Slider
{
Minimum = 0,
Maximum = 255,
Value = currentBackgroundColor.G,
Width = 150,
Margin = new Thickness(5, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
var gValueText = new TextBlock
{
Text = currentBackgroundColor.G.ToString(),
Width = 30,
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Right
};
// B滑块和文本框
var bPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 5) };
var bLabel = new TextBlock { Text = "B:", Width = 20, VerticalAlignment = VerticalAlignment.Center };
var bSlider = new Slider
{
Minimum = 0,
Maximum = 255,
Value = currentBackgroundColor.B,
Width = 150,
Margin = new Thickness(5, 0, 5, 0),
VerticalAlignment = VerticalAlignment.Center
};
var bValueText = new TextBlock
{
Text = currentBackgroundColor.B.ToString(),
Width = 30,
VerticalAlignment = VerticalAlignment.Center,
TextAlignment = TextAlignment.Right
};
// 现在添加事件处理程序
rSlider.ValueChanged += (s, e) =>
{
int value = (int)e.NewValue;
rValueText.Text = value.ToString();
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
};
gSlider.ValueChanged += (s, e) =>
{
int value = (int)e.NewValue;
gValueText.Text = value.ToString();
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
};
bSlider.ValueChanged += (s, e) =>
{
int value = (int)e.NewValue;
bValueText.Text = value.ToString();
UpdateColorPreview(colorPreview, rSlider, gSlider, bSlider);
};
// 添加控件到面板
rPanel.Children.Add(rLabel);
rPanel.Children.Add(rSlider);
rPanel.Children.Add(rValueText);
contentPanel.Children.Add(rPanel);
gPanel.Children.Add(gLabel);
gPanel.Children.Add(gSlider);
gPanel.Children.Add(gValueText);
contentPanel.Children.Add(gPanel);
bPanel.Children.Add(bLabel);
bPanel.Children.Add(bSlider);
bPanel.Children.Add(bValueText);
contentPanel.Children.Add(bPanel);
// 应用按钮
var applyButton = new Button
{
Content = "应用颜色",
Margin = new Thickness(0, 10, 0, 0),
Padding = new Thickness(10, 5, 10, 5),
Background = new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)),
Foreground = new SolidColorBrush(Colors.White),
BorderThickness = new Thickness(0),
HorizontalAlignment = HorizontalAlignment.Center
};
applyButton.Click += (s, e) =>
{
Color selectedColor = Color.FromRgb(
(byte)rSlider.Value,
(byte)gSlider.Value,
(byte)bSlider.Value
);
ApplyCustomBackgroundColor(selectedColor);
};
contentPanel.Children.Add(applyButton);
stackPanel.Children.Add(contentPanel);
// 将面板添加到父容器
BackgroundPalette.Child = stackPanel;
// 获取主窗口中的根网格,确保面板添加到顶层
Grid mainGrid = FindName("Main_Grid") as Grid;
if (mainGrid != null)
{
// 删除可能已存在的BackgroundPalette
foreach (UIElement element in mainGrid.Children)
{
if (element is Border border && border.Name == "BackgroundPalette")
{
mainGrid.Children.Remove(border);
break;
}
}
// 重新定位面板
BackgroundPalette.HorizontalAlignment = HorizontalAlignment.Center;
BackgroundPalette.VerticalAlignment = VerticalAlignment.Center;
BackgroundPalette.Margin = new Thickness(0, 0, 0, 0);
// 添加到主网格
mainGrid.Children.Add(BackgroundPalette);
// 设置面板位置
var clickElement = FindName("BoardChangeBackgroundColorBtn") as FrameworkElement;
if (clickElement != null)
{
Point position = clickElement.TranslatePoint(new Point(0, 0), mainGrid);
BackgroundPalette.Margin = new Thickness(
position.X - 150,
position.Y + clickElement.ActualHeight + 5,
0, 0);
BackgroundPalette.HorizontalAlignment = HorizontalAlignment.Left;
BackgroundPalette.VerticalAlignment = VerticalAlignment.Top;
}
}
// 将面板添加到父容器
BackgroundPalette.Child = stackPanel;
// 获取主窗口中的根网格,确保面板添加到顶层
Grid mainGrid = FindName("Main_Grid") as Grid;
if (mainGrid != null)
{
// 删除可能已存在的BackgroundPalette
foreach (UIElement element in mainGrid.Children)
{
if (element is Border border && border.Name == "BackgroundPalette")
{
mainGrid.Children.Remove(border);
break;
}
}
// 重新定位面板
BackgroundPalette.HorizontalAlignment = HorizontalAlignment.Center;
BackgroundPalette.VerticalAlignment = VerticalAlignment.Center;
BackgroundPalette.Margin = new Thickness(0, 0, 0, 0);
// 添加到主网格
mainGrid.Children.Add(BackgroundPalette);
// 设置面板位置
var clickElement = FindName("BoardChangeBackgroundColorBtn") as FrameworkElement;
if (clickElement != null)
{
Point position = clickElement.TranslatePoint(new Point(0, 0), mainGrid);
BackgroundPalette.Margin = new Thickness(
position.X - 150,
position.Y + clickElement.ActualHeight + 5,
0, 0);
BackgroundPalette.HorizontalAlignment = HorizontalAlignment.Left;
BackgroundPalette.VerticalAlignment = VerticalAlignment.Top;
}
}
}
// 更新背景按钮状态
private void UpdateBackgroundButtonsState()
{
@@ -541,17 +553,17 @@ namespace Ink_Canvas {
{
var whiteboardButton = modePanel.Children[0] as Border;
var blackboardButton = modePanel.Children[1] as Border;
if (whiteboardButton != null && whiteboardButton.Child is TextBlock whiteboardText)
{
whiteboardButton.Background = Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) :
whiteboardButton.Background = Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Color.FromRgb(0x25, 0x63, 0xeb)) :
new SolidColorBrush(Colors.LightGray);
whiteboardText.Foreground = Settings.Canvas.UsingWhiteboard ?
new SolidColorBrush(Colors.White) :
new SolidColorBrush(Colors.Black);
}
if (blackboardButton != null && blackboardButton.Child is TextBlock blackboardText)
{
blackboardButton.Background = !Settings.Canvas.UsingWhiteboard ?
@@ -566,58 +578,58 @@ namespace Ink_Canvas {
}
}
}
// 添加成员变量保存背景面板引用
private Border BackgroundPalette { get; set; }
// 添加成员变量保存当前自定义背景色
private Color? CustomBackgroundColor { get; set; }
/// <summary>
/// 更新颜色预览框的颜色
/// </summary>
private void UpdateColorPreview(Border colorPreview, Slider rSlider, Slider gSlider, Slider bSlider)
{
Color previewColor = Color.FromRgb(
(byte)rSlider.Value,
(byte)gSlider.Value,
(byte)bSlider.Value
);
colorPreview.Background = new SolidColorBrush(previewColor);
}
/// <summary>
/// 应用自定义背景颜色
/// </summary>
private void ApplyCustomBackgroundColor(Color color)
{
// 保存当前选择的颜色
CustomBackgroundColor = color;
// 将颜色转换为十六进制字符串并保存到设置中
string colorHex = $"#{color.R:X2}{color.G:X2}{color.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
// 只在白板或黑板模式下应用自定义背景色
if (currentMode == 1) // 白板或黑板模式
{
// 设置白板/黑板模式下的背景
GridBackgroundCover.Background = new SolidColorBrush(color);
}
// 保存设置
SaveSettingsToFile();
// 立即更新界面
if (BackgroundPalette != null)
{
UpdateBackgroundButtonsState();
UpdateRGBSliders(color); // 更新RGB滑块的值
}
// 显示提示信息
ShowNotification($"已应用自定义背景色: {colorHex}");
}
// 添加成员变量保存背景面板引用
private Border BackgroundPalette { get; set; }
// 添加成员变量保存当前自定义背景色
private Color? CustomBackgroundColor { get; set; }
/// <summary>
/// 更新颜色预览框的颜色
/// </summary>
private void UpdateColorPreview(Border colorPreview, Slider rSlider, Slider gSlider, Slider bSlider)
{
Color previewColor = Color.FromRgb(
(byte)rSlider.Value,
(byte)gSlider.Value,
(byte)bSlider.Value
);
colorPreview.Background = new SolidColorBrush(previewColor);
}
/// <summary>
/// 应用自定义背景颜色
/// </summary>
private void ApplyCustomBackgroundColor(Color color)
{
// 保存当前选择的颜色
CustomBackgroundColor = color;
// 将颜色转换为十六进制字符串并保存到设置中
string colorHex = $"#{color.R:X2}{color.G:X2}{color.B:X2}";
Settings.Canvas.CustomBackgroundColor = colorHex;
// 只在白板或黑板模式下应用自定义背景色
if (currentMode == 1) // 白板或黑板模式
{
// 设置白板/黑板模式下的背景
GridBackgroundCover.Background = new SolidColorBrush(color);
}
// 保存设置
SaveSettingsToFile();
// 立即更新界面
if (BackgroundPalette != null)
{
UpdateBackgroundButtonsState();
UpdateRGBSliders(color); // 更新RGB滑块的值
}
// 显示提示信息
ShowNotification($"已应用自定义背景色: {colorHex}");
}
/// <summary>
/// 从设置中加载自定义背景色
@@ -635,7 +647,7 @@ namespace Ink_Canvas {
byte r = Convert.ToByte(colorHex.Substring(1, 2), 16);
byte g = Convert.ToByte(colorHex.Substring(3, 2), 16);
byte b = Convert.ToByte(colorHex.Substring(5, 2), 16);
// 保存到内存中
CustomBackgroundColor = Color.FromRgb(r, g, b);
}
@@ -653,7 +665,7 @@ namespace Ink_Canvas {
// 白板模式默认颜色
CustomBackgroundColor = Color.FromRgb(234, 235, 237);
}
// 可以在这里记录日志
Console.WriteLine($"解析自定义背景色失败: {ex.Message}");
}
@@ -672,13 +684,13 @@ namespace Ink_Canvas {
CustomBackgroundColor = Color.FromRgb(234, 235, 237);
}
}
// 只在白板或黑板模式下应用自定义背景色
if (currentMode == 1 && CustomBackgroundColor.HasValue) // 白板或黑板模式
{
// 设置白板/黑板模式下的背景
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
// 更新RGB滑块的值(如果调色板已经创建)
if (BackgroundPalette != null && BackgroundPalette.Visibility == Visibility.Visible)
{
@@ -687,43 +699,59 @@ namespace Ink_Canvas {
}
}
private void BoardLassoIcon_Click(object sender, RoutedEventArgs e) {
private void BoardLassoIcon_Click(object sender, RoutedEventArgs e)
{
forceEraser = false;
forcePointEraser = false;
drawingShapeMode = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.Select;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Select);
SetCursorBasedOnEditingMode(inkCanvas);
}
private void BoardEraserIconByStrokes_Click(object sender, RoutedEventArgs e) {
private void BoardEraserIconByStrokes_Click(object sender, RoutedEventArgs e)
{
//if (BoardEraserByStrokes.Background.ToString() == "#FF679CF4") {
// AnimationsHelper.ShowWithSlideFromBottomAndFade(BoardDeleteIcon);
//}
//else {
forceEraser = true;
forcePointEraser = false;
// 禁用高级橡皮擦系统
DisableAdvancedEraserSystem();
inkCanvas.EraserShape = new EllipseStylusShape(5, 5);
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
drawingShapeMode = 0;
forceEraser = true;
forcePointEraser = false;
inkCanvas_EditingModeChanged(inkCanvas, null);
CancelSingleFingerDragMode();
inkCanvas.EraserShape = new EllipseStylusShape(5, 5);
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.EraseByStroke);
drawingShapeMode = 0;
HideSubPanels("eraserByStrokes");
// 修复:切换到线擦时,确保重置笔的状态
penType = 0;
drawingAttributes.IsHighlighter = false;
drawingAttributes.StylusTip = StylusTip.Ellipse;
inkCanvas_EditingModeChanged(inkCanvas, null);
CancelSingleFingerDragMode();
HideSubPanels("eraserByStrokes");
//}
}
private void BoardSymbolIconDelete_MouseUp(object sender, RoutedEventArgs e) {
private void BoardSymbolIconDelete_MouseUp(object sender, RoutedEventArgs e)
{
PenIcon_Click(null, null);
SymbolIconDelete_MouseUp(null, null);
// 根据设置决定是否清空图片
if (Settings.Canvas.ClearCanvasAlsoClearImages) {
if (Settings.Canvas.ClearCanvasAlsoClearImages)
{
// 如果设置为清空图片,则直接清空所有子元素
Debug.WriteLine("BoardSymbolIconDelete: Clearing all children including images");
inkCanvas.Children.Clear();
} else {
}
else
{
// 保存非笔画元素(如图片)
Debug.WriteLine("BoardSymbolIconDelete: Preserving non-stroke elements (images)");
var preservedElements = PreserveNonStrokeElements();
@@ -738,14 +766,17 @@ namespace Ink_Canvas {
{
PenIcon_Click(null, null);
SymbolIconDelete_MouseUp(null, null);
if (Settings.Canvas.ClearCanvasAndClearTimeMachine == false) timeMachine.ClearStrokeHistory();
if (!Settings.Canvas.ClearCanvasAndClearTimeMachine) timeMachine.ClearStrokeHistory();
// 根据设置决定是否清空图片
if (Settings.Canvas.ClearCanvasAlsoClearImages) {
if (Settings.Canvas.ClearCanvasAlsoClearImages)
{
// 如果设置为清空图片,则直接清空所有子元素
Debug.WriteLine("BoardSymbolIconDeleteInkAndHistories: Clearing all children including images");
inkCanvas.Children.Clear();
} else {
}
else
{
// 保存非笔画元素(如图片)
Debug.WriteLine("BoardSymbolIconDeleteInkAndHistories: Preserving non-stroke elements (images)");
var preservedElements = PreserveNonStrokeElements();
@@ -757,12 +788,14 @@ namespace Ink_Canvas {
}
}
private void BoardLaunchEasiCamera_MouseUp(object sender, MouseButtonEventArgs e) {
private void BoardLaunchEasiCamera_MouseUp(object sender, MouseButtonEventArgs e)
{
ImageBlackboard_MouseUp(null, null);
SoftwareLauncher.LaunchEasiCamera("希沃视频展台");
}
private void BoardLaunchDesmos_MouseUp(object sender, MouseButtonEventArgs e) {
private void BoardLaunchDesmos_MouseUp(object sender, MouseButtonEventArgs e)
{
HideSubPanelsImmediately();
ImageBlackboard_MouseUp(null, null);
Process.Start("https://www.desmos.com/calculator?lang=zh-CN");
@@ -781,7 +814,7 @@ namespace Ink_Canvas {
Slider rSlider = null;
Slider gSlider = null;
Slider bSlider = null;
// 遍历面板查找RGB滑块
foreach (var child in contentPanel.Children)
{
@@ -810,7 +843,7 @@ namespace Ink_Canvas {
}
}
}
// 更新滑块值
if (rSlider != null && gSlider != null && bSlider != null)
{
@@ -0,0 +1,325 @@
using Ink_Canvas.Helpers;
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Forms;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Clipboard = System.Windows.Clipboard;
using ContextMenu = System.Windows.Controls.ContextMenu;
using Cursors = System.Windows.Input.Cursors;
using MenuItem = System.Windows.Controls.MenuItem;
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
private bool isClipboardMonitoringEnabled;
private BitmapSource lastClipboardImage;
// 初始化剪贴板监控
private void InitializeClipboardMonitoring()
{
try
{
// 监听剪贴板变化
ClipboardNotification.ClipboardUpdate += OnClipboardUpdate;
isClipboardMonitoringEnabled = true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"初始化剪贴板监控失败: {ex.Message}", LogHelper.LogType.Error);
}
}
// 剪贴板内容变化事件处理
private void OnClipboardUpdate()
{
try
{
if (Clipboard.ContainsImage())
{
var clipboardImage = Clipboard.GetImage();
if (clipboardImage != null && clipboardImage != lastClipboardImage)
{
lastClipboardImage = clipboardImage;
// 在白板模式下显示粘贴提示
if (currentMode == 1) // 白板模式
{
ShowPasteNotification();
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理剪贴板更新失败: {ex.Message}", LogHelper.LogType.Error);
}
}
// 显示粘贴提示
private void ShowPasteNotification()
{
try
{
Dispatcher.Invoke(() =>
{
ShowNotification("检测到剪贴板中有图片,右键点击白板可粘贴");
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"显示粘贴提示失败: {ex.Message}", LogHelper.LogType.Error);
}
}
// 处理右键菜单显示
private void ShowPasteContextMenu(Point position)
{
try
{
if (!Clipboard.ContainsImage()) return;
// 创建右键菜单
var contextMenu = new ContextMenu();
var pasteMenuItem = new MenuItem
{
Header = "粘贴图片"
};
pasteMenuItem.Click += async (s, e) => await PasteImageFromClipboard(position);
contextMenu.Items.Add(pasteMenuItem);
// 显示菜单
contextMenu.IsOpen = true;
contextMenu.PlacementTarget = inkCanvas;
contextMenu.Placement = PlacementMode.MousePoint;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"显示粘贴菜单失败: {ex.Message}", LogHelper.LogType.Error);
}
}
// 从剪贴板粘贴图片
private async Task PasteImageFromClipboard(Point? position = null)
{
try
{
if (!Clipboard.ContainsImage())
{
ShowNotification("剪贴板中没有图片");
return;
}
var clipboardImage = Clipboard.GetImage();
if (clipboardImage == null)
{
ShowNotification("无法获取剪贴板图片");
return;
}
// 创建Image控件
var image = new Image
{
Source = clipboardImage,
Width = clipboardImage.PixelWidth,
Height = clipboardImage.PixelHeight,
Stretch = Stretch.Fill
};
// 生成唯一名称
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;
}
// 设置图片属性,避免被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);
// 等待图片加载完成后再进行居中处理
image.Loaded += (sender, e) =>
{
// 确保在UI线程中执行
Dispatcher.BeginInvoke(new Action(() =>
{
// 先进行缩放居中处理
CenterAndScaleElement(image);
// 如果有指定位置,调整到指定位置
if (position.HasValue)
{
// 在指定位置居中显示
InkCanvas.SetLeft(image, position.Value.X - image.Width / 2);
InkCanvas.SetTop(image, position.Value.Y - image.Height / 2);
}
// 绑定事件处理器
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;
}
}), DispatcherPriority.Loaded);
};
// 提交到历史记录
timeMachine.CommitElementInsertHistory(image);
// 插入图片后切换到选择模式并刷新浮动栏高光显示
SetCurrentToolMode(InkCanvasEditingMode.Select);
UpdateCurrentToolMode("select");
HideSubPanels("select");
ShowNotification("图片已从剪贴板粘贴");
}
catch (Exception ex)
{
ShowNotification($"粘贴图片失败: {ex.Message}");
LogHelper.WriteLogToFile($"粘贴图片失败: {ex.Message}", LogHelper.LogType.Error);
}
}
// 处理白板右键事件
private void InkCanvas_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
try
{
// 只在白板模式下处理
if (currentMode != 1) return;
// 检查是否有图片在剪贴板中
if (Clipboard.ContainsImage())
{
var position = e.GetPosition(inkCanvas);
ShowPasteContextMenu(position);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理右键事件失败: {ex.Message}", LogHelper.LogType.Error);
}
}
// 处理全局粘贴快捷键
internal async void HandleGlobalPaste(object sender, ExecutedRoutedEventArgs e)
{
try
{
// 只在白板模式下处理
if (currentMode != 1) return;
if (Clipboard.ContainsImage())
{
await PasteImageFromClipboard();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理全局粘贴失败: {ex.Message}", LogHelper.LogType.Error);
}
}
// 清理剪贴板监控
private void CleanupClipboardMonitoring()
{
try
{
if (isClipboardMonitoringEnabled)
{
ClipboardNotification.ClipboardUpdate -= OnClipboardUpdate;
isClipboardMonitoringEnabled = false;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理剪贴板监控失败: {ex.Message}", LogHelper.LogType.Error);
}
}
}
// 剪贴板通知类
public static class ClipboardNotification
{
public static event Action ClipboardUpdate;
private static Timer clipboardTimer;
private static string lastClipboardText = "";
private static bool lastHadImage;
static ClipboardNotification()
{
clipboardTimer = new Timer();
clipboardTimer.Interval = 500; // 每500ms检查一次
clipboardTimer.Tick += CheckClipboard;
clipboardTimer.Start();
}
private static void CheckClipboard(object sender, EventArgs e)
{
try
{
bool currentHasImage = Clipboard.ContainsImage();
string currentText = Clipboard.ContainsText() ? Clipboard.GetText() : "";
if (currentHasImage != lastHadImage || currentText != lastClipboardText)
{
lastHadImage = currentHasImage;
lastClipboardText = currentText;
ClipboardUpdate?.Invoke();
}
}
catch
{
// 忽略剪贴板访问错误
}
}
public static void Stop()
{
clipboardTimer?.Stop();
clipboardTimer?.Dispose();
}
}
}
+146 -66
View File
@@ -1,4 +1,5 @@
using System;
using Ink_Canvas.Helpers;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;
@@ -8,33 +9,45 @@ using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using Ink_Canvas.Helpers;
namespace Ink_Canvas {
public partial class MainWindow : Window {
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
private int inkColor = 1;
private void ColorSwitchCheck() {
private void ColorSwitchCheck()
{
HideSubPanels("color");
if (GridTransparencyFakeBackground.Background == Brushes.Transparent) {
if (currentMode == 1) {
if (GridTransparencyFakeBackground.Background == Brushes.Transparent)
{
if (currentMode == 1)
{
currentMode = 0;
GridBackgroundCover.Visibility = Visibility.Collapsed;
AnimationsHelper.HideWithSlideAndFade(BlackboardLeftSide);
AnimationsHelper.HideWithSlideAndFade(BlackboardCenterSide);
AnimationsHelper.HideWithSlideAndFade(BlackboardRightSide);
// 在PPT模式下隐藏手势面板和手势按钮
AnimationsHelper.HideWithSlideAndFade(TwoFingerGestureBorder);
AnimationsHelper.HideWithSlideAndFade(BoardTwoFingerGestureBorder);
EnableTwoFingerGestureBorder.Visibility = Visibility.Collapsed;
}
BtnHideInkCanvas_Click(BtnHideInkCanvas, null);
}
var strokes = inkCanvas.GetSelectedStrokes();
if (strokes.Count != 0) {
if (strokes.Count != 0)
{
foreach (var stroke in strokes)
try {
try
{
stroke.DrawingAttributes.Color = inkCanvas.DefaultDrawingAttributes.Color;
}
catch {
catch
{
// ignored
}
}
@@ -47,10 +60,12 @@ namespace Ink_Canvas {
item.Value.Clear();
}
}
else {
else
{
inkCanvas.IsManipulationEnabled = true;
drawingShapeMode = 0;
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
// 使用集中化的工具模式切换方法
SetCurrentToolMode(InkCanvasEditingMode.Ink);
CancelSingleFingerDragMode();
CheckColorTheme();
}
@@ -63,14 +78,20 @@ namespace Ink_Canvas {
private int lastDesktopInkColor = 1, lastBoardInkColor = 5;
private int highlighterColor = 102;
private void CheckColorTheme(bool changeColorTheme = false) {
private void CheckColorTheme(bool changeColorTheme = false)
{
if (changeColorTheme)
if (currentMode != 0) {
if (Settings.Canvas.UsingWhiteboard) {
if (currentMode != 0)
{
if (Settings.Canvas.UsingWhiteboard)
{
// 检查是否有自定义背景色,如果有则使用自定义背景色
if (CustomBackgroundColor.HasValue) {
if (CustomBackgroundColor.HasValue)
{
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
} else {
}
else
{
GridBackgroundCover.Background = new SolidColorBrush(Color.FromRgb(234, 235, 237));
}
WaterMarkTime.Foreground = new SolidColorBrush(Color.FromRgb(22, 41, 36));
@@ -78,11 +99,15 @@ namespace Ink_Canvas {
BlackBoardWaterMark.Foreground = new SolidColorBrush(Color.FromRgb(22, 41, 36));
isUselightThemeColor = false;
}
else {
else
{
// 黑板模式下,检查是否有自定义背景色
if (CustomBackgroundColor.HasValue) {
if (CustomBackgroundColor.HasValue)
{
GridBackgroundCover.Background = new SolidColorBrush(CustomBackgroundColor.Value);
} else {
}
else
{
GridBackgroundCover.Background = new SolidColorBrush(Color.FromRgb(22, 41, 36));
}
WaterMarkTime.Foreground = new SolidColorBrush(Color.FromRgb(234, 235, 237));
@@ -92,26 +117,32 @@ namespace Ink_Canvas {
}
}
if (currentMode == 0) {
if (currentMode == 0)
{
isUselightThemeColor = isDesktopUselightThemeColor;
inkColor = lastDesktopInkColor;
}
else {
else
{
inkColor = lastBoardInkColor;
}
double alpha = inkCanvas.DefaultDrawingAttributes.Color.A;
if (penType == 0) {
if (inkColor == 0) {
if (penType == 0)
{
if (inkColor == 0)
{
// Black
inkCanvas.DefaultDrawingAttributes.Color = Color.FromArgb((byte)alpha, 0, 0, 0);
}
else if (inkColor == 5) {
else if (inkColor == 5)
{
// White
inkCanvas.DefaultDrawingAttributes.Color = Color.FromArgb((byte)alpha, 255, 255, 255);
}
else if (isUselightThemeColor) {
else if (isUselightThemeColor)
{
if (inkColor == 1)
// Red
inkCanvas.DefaultDrawingAttributes.Color = Color.FromArgb((byte)alpha, 239, 68, 68);
@@ -134,7 +165,8 @@ namespace Ink_Canvas {
// Orange (亮色)
inkCanvas.DefaultDrawingAttributes.Color = Color.FromArgb((byte)alpha, 249, 115, 22);
}
else {
else
{
if (inkColor == 1)
// Red
inkCanvas.DefaultDrawingAttributes.Color = Color.FromArgb((byte)alpha, 220, 38, 38);
@@ -158,7 +190,8 @@ namespace Ink_Canvas {
inkCanvas.DefaultDrawingAttributes.Color = Color.FromArgb((byte)alpha, 234, 88, 12);
}
}
else if (penType == 1) {
else if (penType == 1)
{
if (highlighterColor == 100)
// Black
inkCanvas.DefaultDrawingAttributes.Color = Color.FromRgb(0, 0, 0);
@@ -191,7 +224,8 @@ namespace Ink_Canvas {
inkCanvas.DefaultDrawingAttributes.Color = Color.FromRgb(249, 115, 22);
}
if (isUselightThemeColor) {
if (isUselightThemeColor)
{
// 亮系
// 亮色的红色
BorderPenColorRed.Background = new SolidColorBrush(Color.FromRgb(239, 68, 68));
@@ -226,7 +260,8 @@ namespace Ink_Canvas {
ColorThemeSwitchTextBlock.Text = "暗系";
BoardColorThemeSwitchTextBlock.Text = "暗系";
}
else {
else
{
// 暗系
// 暗色的红色
BorderPenColorRed.Background = new SolidColorBrush(Color.FromRgb(220, 38, 38));
@@ -305,7 +340,8 @@ namespace Ink_Canvas {
BoardHighlighterPenViewboxBtnColorYellowContent.Visibility = Visibility.Collapsed;
BoardHighlighterPenViewboxBtnColorZincContent.Visibility = Visibility.Collapsed;
switch (inkColor) {
switch (inkColor)
{
case 0:
ViewboxBtnColorBlackContent.Visibility = Visibility.Visible;
BoardViewboxBtnColorBlackContent.Visibility = Visibility.Visible;
@@ -342,7 +378,8 @@ namespace Ink_Canvas {
break;
}
switch (highlighterColor) {
switch (highlighterColor)
{
case 100:
HighlighterPenViewboxBtnColorBlackContent.Visibility = Visibility.Visible;
BoardHighlighterPenViewboxBtnColorBlackContent.Visibility = Visibility.Visible;
@@ -384,20 +421,31 @@ namespace Ink_Canvas {
BoardHighlighterPenViewboxBtnColorOrangeContent.Visibility = Visibility.Visible;
break;
}
// 更新快捷调色盘选择指示器
if (penType == 0)
{
UpdateQuickColorPaletteIndicator(inkCanvas.DefaultDrawingAttributes.Color);
}
}
private void CheckLastColor(int inkColor, bool isHighlighter = false) {
if (isHighlighter) {
private void CheckLastColor(int inkColor, bool isHighlighter = false)
{
if (isHighlighter)
{
highlighterColor = inkColor;
}
else {
else
{
if (currentMode == 0) lastDesktopInkColor = inkColor;
else lastBoardInkColor = inkColor;
}
}
private async void CheckPenTypeUIState() {
if (penType == 0) {
private async void CheckPenTypeUIState()
{
if (penType == 0)
{
DefaultPenPropsPanel.Visibility = Visibility.Visible;
DefaultPenColorsPanel.Visibility = Visibility.Visible;
HighlighterPenColorsPanel.Visibility = Visibility.Collapsed;
@@ -433,7 +481,8 @@ namespace Ink_Canvas {
BoardHighlightPenTabButtonIndicator.Visibility = Visibility.Collapsed;
// PenPalette.Margin = new Thickness(-160, -200, -33, 32);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
@@ -444,7 +493,8 @@ namespace Ink_Canvas {
PenPalette.BeginAnimation(MarginProperty, marginAnimation);
});
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
@@ -462,7 +512,8 @@ namespace Ink_Canvas {
await Dispatcher.InvokeAsync(() => { BoardPenPaletteGrid.Margin = new Thickness(-160, -200, -33, 50); });
}
else if (penType == 1) {
else if (penType == 1)
{
DefaultPenPropsPanel.Visibility = Visibility.Collapsed;
DefaultPenColorsPanel.Visibility = Visibility.Collapsed;
HighlighterPenColorsPanel.Visibility = Visibility.Visible;
@@ -498,7 +549,8 @@ namespace Ink_Canvas {
BoardHighlightPenTabButtonIndicator.Visibility = Visibility.Visible;
// PenPalette.Margin = new Thickness(-160, -157, -33, 32);
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
@@ -509,7 +561,8 @@ namespace Ink_Canvas {
PenPalette.BeginAnimation(MarginProperty, marginAnimation);
});
await Dispatcher.InvokeAsync(() => {
await Dispatcher.InvokeAsync(() =>
{
var marginAnimation = new ThicknessAnimation
{
Duration = TimeSpan.FromSeconds(0.1),
@@ -528,7 +581,8 @@ namespace Ink_Canvas {
}
}
private void SwitchToDefaultPen(object sender, MouseButtonEventArgs e) {
private void SwitchToDefaultPen(object sender, MouseButtonEventArgs e)
{
penType = 0;
CheckPenTypeUIState();
CheckColorTheme();
@@ -538,7 +592,8 @@ namespace Ink_Canvas {
drawingAttributes.IsHighlighter = false;
}
private void SwitchToHighlighterPen(object sender, MouseButtonEventArgs e) {
private void SwitchToHighlighterPen(object sender, MouseButtonEventArgs e)
{
penType = 1;
CheckPenTypeUIState();
CheckColorTheme();
@@ -546,126 +601,150 @@ namespace Ink_Canvas {
drawingAttributes.Height = Settings.Canvas.HighlighterWidth;
drawingAttributes.StylusTip = StylusTip.Rectangle;
drawingAttributes.IsHighlighter = true;
// 确保荧光笔模式切换后正确更新颜色和快捷调色板指示器
ColorSwitchCheck();
}
private void BtnColorBlack_Click(object sender, RoutedEventArgs e) {
private void BtnColorBlack_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(0);
ColorSwitchCheck();
}
private void BtnColorRed_Click(object sender, RoutedEventArgs e) {
private void BtnColorRed_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(1);
ColorSwitchCheck();
}
private void BtnColorGreen_Click(object sender, RoutedEventArgs e) {
private void BtnColorGreen_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(2);
ColorSwitchCheck();
}
private void BtnColorBlue_Click(object sender, RoutedEventArgs e) {
private void BtnColorBlue_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(3);
ColorSwitchCheck();
}
private void BtnColorYellow_Click(object sender, RoutedEventArgs e) {
private void BtnColorYellow_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(4);
ColorSwitchCheck();
}
private void BtnColorWhite_Click(object sender, RoutedEventArgs e) {
private void BtnColorWhite_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(5);
ColorSwitchCheck();
}
private void BtnColorPink_Click(object sender, RoutedEventArgs e) {
private void BtnColorPink_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(6);
ColorSwitchCheck();
}
private void BtnColorOrange_Click(object sender, RoutedEventArgs e) {
private void BtnColorOrange_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(8);
ColorSwitchCheck();
}
private void BtnColorTeal_Click(object sender, RoutedEventArgs e) {
private void BtnColorTeal_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(7);
ColorSwitchCheck();
}
private void BtnHighlighterColorBlack_Click(object sender, RoutedEventArgs e) {
private void BtnHighlighterColorBlack_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(100, true);
penType = 1;
CheckPenTypeUIState();
ColorSwitchCheck();
}
private void BtnHighlighterColorWhite_Click(object sender, RoutedEventArgs e) {
private void BtnHighlighterColorWhite_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(101, true);
penType = 1;
CheckPenTypeUIState();
ColorSwitchCheck();
}
private void BtnHighlighterColorRed_Click(object sender, RoutedEventArgs e) {
private void BtnHighlighterColorRed_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(102, true);
penType = 1;
CheckPenTypeUIState();
ColorSwitchCheck();
}
private void BtnHighlighterColorYellow_Click(object sender, RoutedEventArgs e) {
private void BtnHighlighterColorYellow_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(103, true);
penType = 1;
CheckPenTypeUIState();
ColorSwitchCheck();
}
private void BtnHighlighterColorGreen_Click(object sender, RoutedEventArgs e) {
private void BtnHighlighterColorGreen_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(104, true);
penType = 1;
CheckPenTypeUIState();
ColorSwitchCheck();
}
private void BtnHighlighterColorZinc_Click(object sender, RoutedEventArgs e) {
private void BtnHighlighterColorZinc_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(105, true);
penType = 1;
CheckPenTypeUIState();
ColorSwitchCheck();
}
private void BtnHighlighterColorBlue_Click(object sender, RoutedEventArgs e) {
private void BtnHighlighterColorBlue_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(106, true);
penType = 1;
CheckPenTypeUIState();
ColorSwitchCheck();
}
private void BtnHighlighterColorPurple_Click(object sender, RoutedEventArgs e) {
private void BtnHighlighterColorPurple_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(107, true);
penType = 1;
CheckPenTypeUIState();
ColorSwitchCheck();
}
private void BtnHighlighterColorTeal_Click(object sender, RoutedEventArgs e) {
private void BtnHighlighterColorTeal_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(108, true);
penType = 1;
CheckPenTypeUIState();
ColorSwitchCheck();
}
private void BtnHighlighterColorOrange_Click(object sender, RoutedEventArgs e) {
private void BtnHighlighterColorOrange_Click(object sender, RoutedEventArgs e)
{
CheckLastColor(109, true);
penType = 1;
CheckPenTypeUIState();
ColorSwitchCheck();
}
private Color StringToColor(string colorStr) {
private Color StringToColor(string colorStr)
{
var argb = new byte[4];
for (var i = 0; i < 4; i++) {
for (var i = 0; i < 4; i++)
{
var charArray = colorStr.Substring(i * 2 + 1, 2).ToCharArray();
var b1 = toByte(charArray[0]);
var b2 = toByte(charArray[1]);
@@ -675,7 +754,8 @@ namespace Ink_Canvas {
return Color.FromArgb(argb[0], argb[1], argb[2], argb[3]); //#FFFFFFFF
}
private static byte toByte(char c) {
private static byte toByte(char c)
{
var b = (byte)"0123456789ABCDEF".IndexOf(c);
return b;
}
File diff suppressed because it is too large Load Diff
+286 -144
View File
@@ -1,4 +1,5 @@
using System;
using Ink_Canvas.Helpers;
using System;
using System.Diagnostics;
using System.Linq;
using System.Windows;
@@ -6,10 +7,11 @@ using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using Ink_Canvas.Helpers;
namespace Ink_Canvas {
public partial class MainWindow : Window {
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
// 新橡皮擦系统的核心变量
public bool isUsingAdvancedEraser;
@@ -79,19 +81,21 @@ namespace Ink_Canvas {
/// <summary>
/// 新橡皮擦覆盖层加载事件处理
/// </summary>
private void EraserOverlay_Loaded(object sender, RoutedEventArgs e) {
private void EraserOverlay_Loaded(object sender, RoutedEventArgs e)
{
var border = (Border)sender;
// 初始化覆盖层
InitializeEraserOverlay(border);
Trace.WriteLine("Advanced Eraser: Overlay loaded and initialized");
}
/// <summary>
/// 开始高级橡皮擦操作
/// </summary>
private void StartAdvancedEraserOperation(object sender) {
private void StartAdvancedEraserOperation(object sender)
{
if (isUsingAdvancedEraser) return;
// 设置操作状态
@@ -119,7 +123,8 @@ namespace Ink_Canvas {
/// </summary>
private StylusShape CreateEraserShape()
{
if (isCurrentEraserCircle) {
if (isCurrentEraserCircle)
{
return new EllipseStylusShape(currentEraserSize, currentEraserSize);
}
@@ -130,14 +135,18 @@ namespace Ink_Canvas {
/// <summary>
/// 初始化橡皮擦变换矩阵
/// </summary>
private void InitializeEraserTransform() {
private void InitializeEraserTransform()
{
eraserTransformMatrix = new Matrix();
if (isCurrentEraserCircle) {
if (isCurrentEraserCircle)
{
// 圆形橡皮擦:等比例缩放
var scale = currentEraserSize / 56.0; // 基于56x56的基准尺寸
eraserTransformMatrix.ScaleAt(scale, scale, 0, 0);
} else {
}
else
{
// 矩形橡皮擦:保持传统比例
var scaleX = currentEraserSize / 38.0;
var scaleY = (currentEraserSize * 56 / 38) / 56.0;
@@ -148,11 +157,13 @@ namespace Ink_Canvas {
/// <summary>
/// 更新橡皮擦尺寸
/// </summary>
private void UpdateEraserSize() {
private void UpdateEraserSize()
{
// 使用与原来相同的逻辑计算橡皮擦尺寸
double k = 1.0;
switch (Settings.Canvas.EraserSize) {
switch (Settings.Canvas.EraserSize)
{
case 0: k = Settings.Canvas.EraserShapeType == 0 ? 0.5 : 0.7; break;
case 1: k = Settings.Canvas.EraserShapeType == 0 ? 0.8 : 0.9; break;
case 2: k = 1.0; break;
@@ -162,11 +173,14 @@ namespace Ink_Canvas {
// 更新形状类型
isCurrentEraserCircle = (Settings.Canvas.EraserShapeType == 0);
// 根据形状类型设置尺寸
if (isCurrentEraserCircle) {
if (isCurrentEraserCircle)
{
currentEraserSize = k * 90; // 圆形橡皮擦
} else {
}
else
{
currentEraserSize = k * 90 * 0.6; // 矩形橡皮擦宽度
}
}
@@ -174,7 +188,8 @@ namespace Ink_Canvas {
/// <summary>
/// 结束高级橡皮擦操作
/// </summary>
private void EndAdvancedEraserOperation(object sender) {
private void EndAdvancedEraserOperation(object sender)
{
if (!isUsingAdvancedEraser) return;
// 重置操作状态
@@ -182,7 +197,8 @@ namespace Ink_Canvas {
isEraserVisible = false;
// 释放鼠标捕获
if (sender is Border border) {
if (sender is Border border)
{
border.ReleaseMouseCapture();
}
@@ -190,7 +206,8 @@ namespace Ink_Canvas {
HideEraserFeedback();
// 结束碰撞检测
if (advancedHitTester != null) {
if (advancedHitTester != null)
{
advancedHitTester.EndHitTesting();
advancedHitTester = null;
}
@@ -202,12 +219,17 @@ namespace Ink_Canvas {
/// <summary>
/// 隐藏橡皮擦视觉反馈
/// </summary>
private void HideEraserFeedback() {
try {
if (eraserVisualBorder != null) {
private void HideEraserFeedback()
{
try
{
if (eraserVisualBorder != null)
{
eraserVisualBorder.Visibility = Visibility.Collapsed;
}
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error hiding feedback - {ex.Message}");
}
}
@@ -215,14 +237,19 @@ namespace Ink_Canvas {
/// <summary>
/// 提交橡皮擦历史记录
/// </summary>
private void CommitEraserHistory() {
try {
if (ReplacedStroke != null || AddedStroke != null) {
private void CommitEraserHistory()
{
try
{
if (ReplacedStroke != null || AddedStroke != null)
{
timeMachine.CommitStrokeEraseHistory(ReplacedStroke, AddedStroke);
AddedStroke = null;
ReplacedStroke = null;
}
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error committing history - {ex.Message}");
}
}
@@ -230,8 +257,10 @@ namespace Ink_Canvas {
/// <summary>
/// 高级橡皮擦笔画碰撞事件处理
/// </summary>
private void OnAdvancedEraserStrokeHit(object sender, StrokeHitEventArgs args) {
try {
private void OnAdvancedEraserStrokeHit(object sender, StrokeHitEventArgs args)
{
try
{
var inkCanvas = FindName("inkCanvas") as InkCanvas;
if (inkCanvas == null) return;
@@ -248,15 +277,20 @@ namespace Ink_Canvas {
var filteredResultArray = filteredResult as Stroke[] ?? filteredResult.ToArray();
// 执行笔画替换或删除
if (filteredResultArray.Any()) {
if (filteredResultArray.Any())
{
inkCanvas.Strokes.Replace(
new StrokeCollection(filteredToReplaceArray),
new StrokeCollection(filteredResultArray)
);
} else {
}
else
{
inkCanvas.Strokes.Remove(new StrokeCollection(filteredToReplaceArray));
}
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error in stroke hit - {ex.Message}");
}
}
@@ -264,13 +298,15 @@ namespace Ink_Canvas {
/// <summary>
/// 更新高级橡皮擦位置
/// </summary>
private void UpdateAdvancedEraserPosition(object sender, Point position) {
private void UpdateAdvancedEraserPosition(object sender, Point position)
{
// 移除isUsingAdvancedEraser检查,让视觉反馈始终更新
// if (!isUsingAdvancedEraser) return;
// 性能优化:限制更新频率
var now = DateTime.Now;
if ((now - lastEraserUpdate).TotalMilliseconds < ERASER_UPDATE_INTERVAL) {
if ((now - lastEraserUpdate).TotalMilliseconds < ERASER_UPDATE_INTERVAL)
{
return;
}
lastEraserUpdate = now;
@@ -282,11 +318,15 @@ namespace Ink_Canvas {
UpdateEraserVisualFeedback(position);
// 只有在实际使用橡皮擦时才处理擦除
if (isUsingAdvancedEraser) {
if (isUsingAdvancedEraser)
{
// 处理不同的橡皮擦模式
if (isUsingStrokeEraser) {
if (isUsingStrokeEraser)
{
ProcessStrokeEraserAtPosition(position);
} else {
}
else
{
ProcessGeometryEraserAtPosition(position);
}
}
@@ -295,8 +335,10 @@ namespace Ink_Canvas {
/// <summary>
/// 在指定位置处理笔画橡皮擦
/// </summary>
private void ProcessStrokeEraserAtPosition(Point position) {
try {
private void ProcessStrokeEraserAtPosition(Point position)
{
try
{
var inkCanvas = FindName("inkCanvas") as InkCanvas;
if (inkCanvas == null) return;
@@ -304,10 +346,13 @@ namespace Ink_Canvas {
.Where(stroke => !stroke.ContainsPropertyData(IsLockGuid));
var strokesArray = hitStrokes as Stroke[] ?? hitStrokes.ToArray();
if (strokesArray.Any()) {
if (strokesArray.Any())
{
inkCanvas.Strokes.Remove(new StrokeCollection(strokesArray));
}
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error in stroke eraser - {ex.Message}");
}
}
@@ -315,12 +360,17 @@ namespace Ink_Canvas {
/// <summary>
/// 在指定位置处理几何橡皮擦
/// </summary>
private void ProcessGeometryEraserAtPosition(Point position) {
try {
if (advancedHitTester != null) {
private void ProcessGeometryEraserAtPosition(Point position)
{
try
{
if (advancedHitTester != null)
{
advancedHitTester.AddPoint(position);
}
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error in geometry eraser - {ex.Message}");
}
}
@@ -328,11 +378,15 @@ namespace Ink_Canvas {
/// <summary>
/// 更新橡皮擦视觉反馈
/// </summary>
private void UpdateEraserVisualFeedback(Point position) {
try {
private void UpdateEraserVisualFeedback(Point position)
{
try
{
// 获取或创建橡皮擦视觉反馈Border
if (eraserVisualBorder == null) {
eraserVisualBorder = new Border {
if (eraserVisualBorder == null)
{
eraserVisualBorder = new Border
{
Background = new SolidColorBrush(Colors.Transparent),
BorderBrush = new SolidColorBrush(Colors.Transparent),
BorderThickness = new Thickness(0),
@@ -342,53 +396,62 @@ namespace Ink_Canvas {
Opacity = 1
};
Panel.SetZIndex(eraserVisualBorder, 1001);
// 将Border添加到InkCanvasGridForInkReplay中
var inkCanvasGrid = FindName("InkCanvasGridForInkReplay") as Grid;
if (inkCanvasGrid != null) {
if (inkCanvasGrid != null)
{
inkCanvasGrid.Children.Add(eraserVisualBorder);
Trace.WriteLine("Advanced Eraser: Visual feedback border added to grid");
} else {
}
else
{
Trace.WriteLine("Advanced Eraser: Failed to find InkCanvasGridForInkReplay");
return; // 如果找不到Grid,直接返回
}
}
if (eraserVisualBorder != null) {
if (eraserVisualBorder != null)
{
// 创建橡皮擦视觉反馈
var eraserImage = CreateEraserVisualImage();
// 清除Border的内容并添加新的图像
eraserVisualBorder.Child = eraserImage;
// 更新橡皮擦位置和大小
if (isCurrentEraserCircle) {
if (isCurrentEraserCircle)
{
var radius = currentEraserSize / 2;
eraserVisualBorder.Width = currentEraserSize;
eraserVisualBorder.Height = currentEraserSize;
// 使用Margin来定位,因为Border在Grid中
eraserVisualBorder.Margin = new Thickness(
position.X - radius,
position.Y - radius,
position.X - radius,
position.Y - radius,
0, 0);
} else {
}
else
{
// 矩形橡皮擦,使用与原来相同的逻辑
var height = currentEraserSize / 0.6;
eraserVisualBorder.Width = currentEraserSize;
eraserVisualBorder.Height = height;
// 使用Margin来定位,因为Border在Grid中
eraserVisualBorder.Margin = new Thickness(
position.X - currentEraserSize / 2,
position.Y - height / 2,
position.X - currentEraserSize / 2,
position.Y - height / 2,
0, 0);
}
eraserVisualBorder.Visibility = Visibility.Visible;
Trace.WriteLine($"Advanced Eraser: Visual feedback updated to ({position.X:F1}, {position.Y:F1})");
}
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error updating visual feedback - {ex.Message}");
}
}
@@ -396,14 +459,17 @@ namespace Ink_Canvas {
/// <summary>
/// 创建橡皮擦视觉图像
/// </summary>
private Image CreateEraserVisualImage() {
try {
private Image CreateEraserVisualImage()
{
try
{
// 根据橡皮擦形状选择对应的DrawingGroup资源
string resourceKey = isCurrentEraserCircle ? "EraserCircleDrawingGroup" : "EraserDrawingGroup";
// 尝试从资源字典中获取DrawingGroup
var drawingGroup = TryFindResource(resourceKey) as DrawingGroup;
if (drawingGroup == null) {
if (drawingGroup == null)
{
// 如果找不到资源,创建默认的橡皮擦图像
return CreateDefaultEraserImage();
}
@@ -411,14 +477,17 @@ namespace Ink_Canvas {
// 创建变换后的DrawingGroup
var transformedGroup = new DrawingGroup();
transformedGroup.Children.Add(drawingGroup);
// 应用缩放变换
var transform = new ScaleTransform();
if (isCurrentEraserCircle) {
if (isCurrentEraserCircle)
{
var scale = currentEraserSize / 56.0; // 基于56x56的基准尺寸
transform.ScaleX = scale;
transform.ScaleY = scale;
} else {
}
else
{
var scaleX = currentEraserSize / 38.0;
var scaleY = (currentEraserSize / 0.6) / 56.0;
transform.ScaleX = scaleX;
@@ -428,16 +497,19 @@ namespace Ink_Canvas {
// 创建DrawingImage
var drawingImage = new DrawingImage(transformedGroup);
// 创建Image控件
var image = new Image {
var image = new Image
{
Source = drawingImage,
Stretch = Stretch.None
};
RenderOptions.SetBitmapScalingMode(image, BitmapScalingMode.HighQuality);
return image;
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error creating eraser visual image - {ex.Message}");
return CreateDefaultEraserImage();
}
@@ -446,13 +518,18 @@ namespace Ink_Canvas {
/// <summary>
/// 创建默认的橡皮擦图像(当资源不可用时)
/// </summary>
private Image CreateDefaultEraserImage() {
try {
private Image CreateDefaultEraserImage()
{
try
{
// 创建一个简单的几何图形作为默认橡皮擦
Geometry geometry;
if (isCurrentEraserCircle) {
if (isCurrentEraserCircle)
{
geometry = new EllipseGeometry(new Point(28, 28), 28, 28);
} else {
}
else
{
geometry = new RectangleGeometry(new Rect(0, 0, 38, 56));
}
@@ -465,11 +542,14 @@ namespace Ink_Canvas {
// 应用缩放变换
var transform = new ScaleTransform();
if (isCurrentEraserCircle) {
if (isCurrentEraserCircle)
{
var scale = currentEraserSize / 56.0;
transform.ScaleX = scale;
transform.ScaleY = scale;
} else {
}
else
{
var scaleX = currentEraserSize / 38.0;
var scaleY = (currentEraserSize / 0.6) / 56.0;
transform.ScaleX = scaleX;
@@ -478,13 +558,16 @@ namespace Ink_Canvas {
drawingGroup.Transform = transform;
var drawingImage = new DrawingImage(drawingGroup);
var image = new Image {
var image = new Image
{
Source = drawingImage,
Stretch = Stretch.None
};
return image;
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error creating default eraser image - {ex.Message}");
return null;
}
@@ -494,7 +577,8 @@ namespace Ink_Canvas {
/// 兼容性方法:旧版橡皮擦几何碰撞处理
/// </summary>
[Obsolete("使用 OnAdvancedEraserStrokeHit 替代")]
private void EraserGeometry_StrokeHit(object sender, StrokeHitEventArgs args) {
private void EraserGeometry_StrokeHit(object sender, StrokeHitEventArgs args)
{
OnAdvancedEraserStrokeHit(sender, args);
}
@@ -502,7 +586,8 @@ namespace Ink_Canvas {
/// 兼容性方法:旧版橡皮擦移动处理
/// </summary>
[Obsolete("使用 UpdateAdvancedEraserPosition 替代")]
private void EraserOverlay_PointerMove(object sender, Point pt) {
private void EraserOverlay_PointerMove(object sender, Point pt)
{
UpdateAdvancedEraserPosition(sender, pt);
}
@@ -510,7 +595,8 @@ namespace Ink_Canvas {
/// 兼容性方法:旧版橡皮擦按下处理
/// </summary>
[Obsolete("使用 StartAdvancedEraserOperation 替代")]
private void EraserOverlay_PointerDown(object sender) {
private void EraserOverlay_PointerDown(object sender)
{
StartAdvancedEraserOperation(sender);
}
@@ -518,14 +604,16 @@ namespace Ink_Canvas {
/// 兼容性方法:旧版橡皮擦抬起处理
/// </summary>
[Obsolete("使用 EndAdvancedEraserOperation 替代")]
private void EraserOverlay_PointerUp(object sender) {
private void EraserOverlay_PointerUp(object sender)
{
EndAdvancedEraserOperation(sender);
}
/// <summary>
/// 获取当前橡皮擦状态信息(用于调试)
/// </summary>
public string GetEraserStatusInfo() {
public string GetEraserStatusInfo()
{
return "Advanced Eraser Status:\n" +
$"- Active: {isUsingAdvancedEraser}\n" +
$"- Size: {currentEraserSize:F1}\n" +
@@ -538,22 +626,26 @@ namespace Ink_Canvas {
/// <summary>
/// 重置橡皮擦状态
/// </summary>
public void ResetEraserState() {
public void ResetEraserState()
{
isUsingAdvancedEraser = false;
isEraserVisible = false;
lastEraserPosition = new Point();
if (advancedHitTester != null) {
if (advancedHitTester != null)
{
advancedHitTester.EndHitTesting();
advancedHitTester = null;
}
HideEraserFeedback();
// 清理视觉反馈Border
if (eraserVisualBorder != null) {
if (eraserVisualBorder != null)
{
var inkCanvasGrid = FindName("InkCanvasGridForInkReplay") as Grid;
if (inkCanvasGrid != null) {
if (inkCanvasGrid != null)
{
inkCanvasGrid.Children.Remove(eraserVisualBorder);
}
eraserVisualBorder = null;
@@ -563,8 +655,10 @@ namespace Ink_Canvas {
/// <summary>
/// 应用高级橡皮擦形状到InkCanvas
/// </summary>
public void ApplyAdvancedEraserShape() {
try {
public void ApplyAdvancedEraserShape()
{
try
{
var inkCanvas = FindName("inkCanvas") as InkCanvas;
if (inkCanvas == null) return;
@@ -578,13 +672,18 @@ namespace Ink_Canvas {
inkCanvas.EraserShape = eraserShape;
Trace.WriteLine($"Advanced Eraser: Applied shape - Size: {currentEraserSize}, Circle: {isCurrentEraserCircle}");
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error applying shape - {ex.Message}");
// 回退到传统方法
try {
try
{
ApplyCurrentEraserShape();
} catch (Exception fallbackEx) {
}
catch (Exception fallbackEx)
{
Trace.WriteLine($"Advanced Eraser: Fallback also failed - {fallbackEx.Message}");
}
}
@@ -593,33 +692,42 @@ namespace Ink_Canvas {
/// <summary>
/// 启用高级橡皮擦系统
/// </summary>
public void EnableAdvancedEraserSystem() {
try {
public void EnableAdvancedEraserSystem()
{
try
{
// 获取橡皮擦覆盖层
var eraserOverlay = FindName("AdvancedEraserOverlay") as Border;
if (eraserOverlay != null) {
if (eraserOverlay != null)
{
// 启用覆盖层的交互
eraserOverlay.IsHitTestVisible = true;
// 确保覆盖层在橡皮擦模式下启用
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
eraserOverlay.IsHitTestVisible = true;
Trace.WriteLine("Advanced Eraser: Overlay enabled for eraser mode");
}
// 设置覆盖层的大小以覆盖整个InkCanvas
var inkCanvasControl = FindName("inkCanvas") as InkCanvas;
if (inkCanvasControl != null) {
if (inkCanvasControl != null)
{
eraserOverlay.Width = inkCanvasControl.ActualWidth;
eraserOverlay.Height = inkCanvasControl.ActualHeight;
Trace.WriteLine($"Advanced Eraser: Overlay size set to {eraserOverlay.Width}x{eraserOverlay.Height}");
}
Trace.WriteLine("Advanced Eraser: System enabled successfully");
} else {
}
else
{
Trace.WriteLine("Advanced Eraser: Failed to find eraser overlay");
}
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error enabling system - {ex.Message}");
}
}
@@ -627,69 +735,97 @@ namespace Ink_Canvas {
/// <summary>
/// 初始化橡皮擦覆盖层
/// </summary>
private void InitializeEraserOverlay(Border overlay) {
try {
private void InitializeEraserOverlay(Border overlay)
{
try
{
// 设置覆盖层的基本属性
overlay.Background = new SolidColorBrush(Colors.Transparent);
overlay.IsHitTestVisible = false; // 默认禁用,只在橡皮擦模式下启用
// 绑定事件处理
overlay.MouseDown += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
overlay.MouseDown += (sender, e) =>
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
overlay.CaptureMouse();
StartAdvancedEraserOperation(sender);
// 处理单点擦除
var position = e.GetPosition((UIElement)FindName("inkCanvas"));
UpdateAdvancedEraserPosition(sender, position);
}
};
overlay.MouseUp += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
overlay.MouseUp += (sender, e) =>
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
overlay.ReleaseMouseCapture();
EndAdvancedEraserOperation(sender);
}
};
overlay.MouseMove += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
overlay.MouseMove += (sender, e) =>
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
var position = e.GetPosition((UIElement)FindName("inkCanvas"));
Trace.WriteLine($"Advanced Eraser: Mouse move event triggered at ({position.X:F1}, {position.Y:F1})");
UpdateAdvancedEraserPosition(sender, position);
} else {
}
else
{
Trace.WriteLine($"Advanced Eraser: Mouse move ignored - not in eraser mode, current mode: {inkCanvas.EditingMode}");
}
};
// 触控笔事件
overlay.StylusDown += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
overlay.StylusDown += (sender, e) =>
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
e.Handled = true;
if (e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus) {
if (e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus)
{
overlay.CaptureStylus();
}
StartAdvancedEraserOperation(sender);
// 处理单点擦除
var position = e.GetPosition((UIElement)FindName("inkCanvas"));
UpdateAdvancedEraserPosition(sender, position);
}
};
overlay.StylusUp += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
overlay.StylusUp += (sender, e) =>
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
e.Handled = true;
if (e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus) {
if (e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus)
{
overlay.ReleaseStylusCapture();
}
EndAdvancedEraserOperation(sender);
}
};
overlay.StylusMove += (sender, e) => {
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint) {
overlay.StylusMove += (sender, e) =>
{
if (inkCanvas.EditingMode == InkCanvasEditingMode.EraseByPoint)
{
e.Handled = true;
var position = e.GetPosition((UIElement)FindName("inkCanvas"));
UpdateAdvancedEraserPosition(sender, position);
Trace.WriteLine($"Advanced Eraser: Stylus move at ({position.X:F1}, {position.Y:F1})");
}
};
Trace.WriteLine("Advanced Eraser: Overlay initialized successfully");
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error initializing overlay - {ex.Message}");
}
}
@@ -697,22 +833,27 @@ namespace Ink_Canvas {
/// <summary>
/// 禁用高级橡皮擦系统
/// </summary>
public void DisableAdvancedEraserSystem() {
try {
public void DisableAdvancedEraserSystem()
{
try
{
// 重置橡皮擦状态
ResetEraserState();
// 获取橡皮擦覆盖层并禁用
var eraserOverlay = FindName("AdvancedEraserOverlay") as Border;
if (eraserOverlay != null) {
if (eraserOverlay != null)
{
eraserOverlay.IsHitTestVisible = false;
}
// 确保视觉反馈被隐藏
HideEraserFeedback();
Trace.WriteLine("Advanced Eraser: System disabled successfully");
} catch (Exception ex) {
}
catch (Exception ex)
{
Trace.WriteLine($"Advanced Eraser: Error disabling system - {ex.Message}");
}
}
@@ -720,7 +861,8 @@ namespace Ink_Canvas {
/// <summary>
/// 切换橡皮擦形状(圆形/矩形)
/// </summary>
public void ToggleEraserShape() {
public void ToggleEraserShape()
{
isCurrentEraserCircle = !isCurrentEraserCircle;
// 更新设置
@@ -730,6 +872,6 @@ namespace Ink_Canvas {
ApplyAdvancedEraserShape();
Trace.WriteLine($"Advanced Eraser: Toggled to {(isCurrentEraserCircle ? "Circle" : "Rectangle")}");
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More