diff --git a/Ink Canvas/InkCanvasForClass.csproj b/Ink Canvas/InkCanvasForClass.csproj
index 80df8ba1..5de9bf16 100644
--- a/Ink Canvas/InkCanvasForClass.csproj
+++ b/Ink Canvas/InkCanvasForClass.csproj
@@ -111,6 +111,7 @@
+
diff --git a/Ink Canvas/Windows/SettingsViews2/SettingsWindow2.xaml b/Ink Canvas/Windows/SettingsViews2/SettingsWindow2.xaml
index 7de67c6b..e0b270be 100644
--- a/Ink Canvas/Windows/SettingsViews2/SettingsWindow2.xaml
+++ b/Ink Canvas/Windows/SettingsViews2/SettingsWindow2.xaml
@@ -8,6 +8,7 @@
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
Title="InkCanvasForClass 设置"
Width="1138" Height="750"
+ MinWidth="800" MinHeight="600"
WindowStartupLocation="CenterScreen"
ui:ThemeManager.IsThemeAware="True"
ui:TitleBar.ExtendViewIntoTitleBar="True"
@@ -15,6 +16,7 @@
ui:WindowHelper.UseModernWindowStyle="True"
ui:TitleBar.Height="48"
mc:Ignorable="d">
+
@@ -22,6 +24,7 @@
+
+
-
+
+ FontSize="20"
+ FontWeight="SemiBold"/>
+
+ TextChanged="OnControlsSearchBoxTextChanged">
-
+
+
+
+
+
+
+ Tag="Iconography"
+ ToolTipService.ToolTip="图标设置">
-
-
+
+
+ Tag="Typography"
+ ToolTipService.ToolTip="排版设置">
-
-
+
+
+
+
+ Tag="Theme"
+ ToolTipService.ToolTip="主题设置">
-
-
+
+
+ Tag="Colors"
+ ToolTipService.ToolTip="颜色设置">
-
-
+
+
+ Tag="Fonts"
+ ToolTipService.ToolTip="字体设置">
-
+
@@ -170,7 +189,7 @@
-
+
diff --git a/Ink Canvas/Windows/SettingsViews2/SettingsWindow2.xaml.cs b/Ink Canvas/Windows/SettingsViews2/SettingsWindow2.xaml.cs
index 71d93cdf..f547b119 100644
--- a/Ink Canvas/Windows/SettingsViews2/SettingsWindow2.xaml.cs
+++ b/Ink Canvas/Windows/SettingsViews2/SettingsWindow2.xaml.cs
@@ -4,20 +4,39 @@ using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Navigation;
-using System.Windows.Forms;
+using System.Windows.Interop;
+using System.Windows.Input;
+using System.ComponentModel.Composition;
+using System.ComponentModel.Composition.Hosting;
+using System.Linq;
+using MessageBox = System.Windows.MessageBox;
+using Screen = System.Windows.Forms.Screen;
namespace Ink_Canvas.Windows.SettingsViews2
{
+ // 插件设置页面契约接口,所有插件必须实现此接口即可自动接入
+ public interface IPluginSettingsPage
+ {
+ string PageTag { get; } // 页面唯一标识,不可与内置页面重复
+ string PageTitle { get; } // 导航菜单显示的标题
+ string PageIconCode { get; } // Segoe MDL2 Assets 图标字符,例:"\xE713"(设置图标)
+ Type PageType { get; } // 插件设置页面的类型(继承自Page)
+ bool IsFooterItem { get; } // 是否放在导航底部菜单
+ }
+
public partial class SettingsWindow2 : Window
{
- private Dictionary _pageTypes;
- private Dictionary _pages = new Dictionary();
+ private readonly Dictionary _pageTypes;
+ private readonly Dictionary _pages = new Dictionary();
+
+ [ImportMany(typeof(IPluginSettingsPage))]
+ private IEnumerable _pluginPages; // 自动导入所有插件页面
public SettingsWindow2()
{
InitializeComponent();
- // 初始化页面类型映射
+ // 初始化内置页面映射
_pageTypes = new Dictionary
{
{ "Basic", typeof(Basic) },
@@ -34,157 +53,260 @@ namespace Ink_Canvas.Windows.SettingsViews2
{ "Settings", typeof(SettingsPage) }
};
- // 默认选中第一个项目
+ // 加载插件页面
+ LoadPluginSettingsPages();
+ // 初始化导航菜单(内置+插件)
+ InitializeNavigationMenu();
+
+ // 默认选中第一个菜单项
if (NavigationViewControl.MenuItems.Count > 0)
{
NavigationViewControl.SelectedItem = NavigationViewControl.MenuItems[0];
}
- // 窗口加载完成后设置最大尺寸,确保能正确获取 DPI 缩放因子
+ // 窗口生命周期事件注册
this.Loaded += (sender, e) =>
{
- SetMaxWindowSize();
+ SetMaxSizeAndCenter();
+ RegisterDpiChangedListener();
+ };
+
+ // 窗口拖动到其他屏幕时自动适配
+ this.LocationChanged += (sender, e) =>
+ {
+ SetMaxSizeAndCenter();
+ };
+
+ // 窗口关闭时释放资源
+ this.Closed += (sender, e) =>
+ {
+ UnregisterDpiChangedListener();
+ _pages.Clear();
+ _pageTypes.Clear();
+ };
+
+ // 修复触摸屏操作后鼠标指针消失的问题
+ FixTouchScreenCursorIssue();
+ }
+
+ #region 修复触摸屏鼠标指针消失问题
+ private void FixTouchScreenCursorIssue()
+ {
+ // 触摸结束时强制显示鼠标指针
+ this.TouchUp += (s, e) =>
+ {
+ ShowCursor(true);
+ };
+
+ // 鼠标进入窗口时确保指针可见
+ this.MouseEnter += (s, e) =>
+ {
+ ShowCursor(true);
+ };
+
+ // 窗口激活时确保指针可见
+ this.Activated += (s, e) =>
+ {
+ ShowCursor(true);
};
}
- private void SetMaxWindowSize()
+ [System.Runtime.InteropServices.DllImport("user32.dll")]
+ private static extern int ShowCursor(bool bShow);
+ #endregion
+
+ #region 插件化动态设置页面核心逻辑
+ private void LoadPluginSettingsPages()
{
- // 设置最大高度和宽度为工作区的高度和宽度,分别减去 40 和 10,并考虑 DPI 缩放
- var workingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
-
- // 获取 DPI 缩放因子
+ try
+ {
+ // 扫描程序目录下Plugins文件夹中的插件dll
+ var pluginCatalog = new DirectoryCatalog("./Plugins", "*.dll");
+ var container = new CompositionContainer(pluginCatalog);
+ container.ComposeParts(this);
+
+ // 将插件页面注册到页面映射字典
+ foreach (var pluginPage in _pluginPages)
+ {
+ if (!_pageTypes.ContainsKey(pluginPage.PageTag))
+ {
+ _pageTypes.Add(pluginPage.PageTag, pluginPage.PageType);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // 插件加载失败不影响主程序运行,仅输出调试日志
+ System.Diagnostics.Debug.WriteLine($"插件加载失败: {ex.Message}");
+ }
+ }
+
+ private void InitializeNavigationMenu()
+ {
+ // 自动将插件页面添加到导航菜单
+ foreach (var pluginPage in _pluginPages)
+ {
+ var navItem = new NavigationViewItem
+ {
+ Tag = pluginPage.PageTag,
+ Content = pluginPage.PageTitle,
+ Icon = new FontIcon { Glyph = pluginPage.PageIconCode }
+ };
+
+ if (pluginPage.IsFooterItem)
+ {
+ NavigationViewControl.FooterMenuItems.Add(navItem);
+ }
+ else
+ {
+ NavigationViewControl.MenuItems.Add(navItem);
+ }
+ }
+ }
+ #endregion
+
+ #region 高DPI/多屏自适应窗口控制
+ private void SetMaxSizeAndCenter()
+ {
+ if (!this.IsLoaded) return;
+
+ // 1. 获取窗口当前所在屏幕(而非固定主屏,彻底解决多屏问题)
+ var windowHandle = new WindowInteropHelper(this).Handle;
+ var currentScreen = Screen.FromHandle(windowHandle);
+ var workingArea = currentScreen.WorkingArea;
+ var screenBounds = currentScreen.Bounds;
+
+ // 2. 获取当前窗口的DPI缩放因子
+ var source = PresentationSource.FromVisual(this);
double dpiScaleX = 1.0;
double dpiScaleY = 1.0;
- var source = System.Windows.PresentationSource.FromVisual(this);
- if (source != null)
+
+ if (source?.CompositionTarget != null)
{
dpiScaleX = source.CompositionTarget.TransformToDevice.M11;
dpiScaleY = source.CompositionTarget.TransformToDevice.M22;
}
-
- // 先将物理像素转换为逻辑像素,再减去边距
- this.MaxWidth = (workingArea.Width / dpiScaleX) - 10;
- this.MaxHeight = (workingArea.Height / dpiScaleY) - 40;
-
- // 确保窗口居中显示
- this.Left = (workingArea.Width / dpiScaleX - this.Width) / 2;
- this.Top = (workingArea.Height / dpiScaleY - this.Height) / 2;
+
+ // 3. 物理像素 → WPF设备无关像素(DIP)转换
+ double workAreaWidthDip = workingArea.Width / dpiScaleX;
+ double workAreaHeightDip = workingArea.Height / dpiScaleY;
+ double screenLeftDip = screenBounds.Left / dpiScaleX;
+ double screenTopDip = screenBounds.Top / dpiScaleY;
+
+ // 4. 设置窗口最大尺寸(保留你原有的边距)
+ this.MaxWidth = workAreaWidthDip - 10;
+ this.MaxHeight = workAreaHeightDip - 40;
+
+ // 5. 窗口在当前屏幕居中(解决副屏居中跑偏问题)
+ this.Left = screenLeftDip + (workAreaWidthDip - this.ActualWidth) / 2;
+ this.Top = screenTopDip + (workAreaHeightDip - this.ActualHeight) / 2;
}
+ #region DPI/系统缩放变化监听
+ private HwndSource _hwndSource;
+ private void RegisterDpiChangedListener()
+ {
+ _hwndSource = PresentationSource.FromVisual(this) as HwndSource;
+ _hwndSource?.AddHook(DpiChangedWndProc);
+ }
+
+ private void UnregisterDpiChangedListener()
+ {
+ _hwndSource?.RemoveHook(DpiChangedWndProc);
+ _hwndSource = null;
+ }
+
+ private IntPtr DpiChangedWndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
+ {
+ const int WM_DPICHANGED = 0x02E0;
+ // 系统DPI/缩放变化时自动重新计算窗口参数
+ if (msg == WM_DPICHANGED)
+ {
+ SetMaxSizeAndCenter();
+ handled = true;
+ }
+ return IntPtr.Zero;
+ }
+ #endregion
+ #endregion
+
+ #region 导航逻辑优化(含页面缓存)
private void OnNavigationViewSelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
+ // 处理自带的设置项导航
if (args.IsSettingsSelected)
{
- // 导航到设置页面
NavigateToPage("Settings");
+ NavigationViewControl.Header = "设置";
+ return;
}
- else if (args.SelectedItem is iNKORE.UI.WPF.Modern.Controls.NavigationViewItem item)
+
+ // 处理普通导航项
+ if (args.SelectedItem is NavigationViewItem selectedItem)
{
- // 检查是否有Tag,如果有则导航
- string tag = item.Tag as string;
- if (!string.IsNullOrEmpty(tag))
+ string tag = selectedItem.Tag as string;
+ if (!string.IsNullOrEmpty(tag) && _pageTypes.ContainsKey(tag))
{
- // 检查当前页面是否已经是目标页面,避免重复导航
+ // 避免重复导航到当前页面
if (rootFrame.SourcePageType != _pageTypes[tag])
{
NavigateToPage(tag);
}
+ NavigationViewControl.Header = selectedItem.Content;
}
- // 父级导航项(有子菜单)会自动展开,不需要额外处理
}
}
public void NavigateToPage(string pageTag)
{
- if (_pageTypes.TryGetValue(pageTag, out Type pageType))
+ if (!_pageTypes.TryGetValue(pageTag, out Type pageType)) return;
+
+ try
{
- try
+ // 页面缓存:已创建的页面直接复用,保留状态,避免重复初始化
+ if (!_pages.TryGetValue(pageTag, out var cachedPage))
{
- // 使用Type参数导航,这样可以正确记录导航历史
- rootFrame.Navigate(pageType);
- // 更新标题
- if (NavigationViewControl.SelectedItem is iNKORE.UI.WPF.Modern.Controls.NavigationViewItem selectedItem)
- {
- NavigationViewControl.Header = selectedItem.Content;
- }
- }
- catch (Exception ex)
- {
- iNKORE.UI.WPF.Modern.Controls.MessageBox.Show($"导航到页面时出错: {ex.Message}", "错误");
+ cachedPage = Activator.CreateInstance(pageType);
+ _pages.Add(pageTag, cachedPage);
}
+
+ // 导航到缓存页面
+ rootFrame.Navigate(cachedPage.GetType(), cachedPage);
}
- }
-
- private void OnControlsSearchBoxQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
- {
- string query = args.QueryText.ToLower();
- List