Files
community/Ink Canvas/Windows/SettingsViews2/SettingsWindow2.xaml.cs
T
2026-04-05 17:27:00 +08:00

535 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Ink_Canvas.Windows.SettingsViews2.Pages;
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Navigation;
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 readonly Dictionary<string, Type> _pageTypes;
private readonly Dictionary<string, object> _pages = new Dictionary<string, object>();
[ImportMany(typeof(IPluginSettingsPage))]
private IEnumerable<IPluginSettingsPage> _pluginPages; // 自动导入所有插件页面
// 保存窗口原始位置和大小
private double _originalLeft;
private double _originalTop;
private double _originalWidth;
private double _originalHeight;
// 标记窗口是否曾经最大化过
private bool _wasMaximized = false;
public SettingsWindow2()
{
InitializeComponent();
// 初始化内置页面映射
_pageTypes = new Dictionary<string, Type>
{
{ "HomePage", typeof(HomePage) },
{ "BasicPage", typeof(BasicPage) },
{ "Page2Page", typeof(Page2Page) },
{ "DesignPage", typeof(DesignPage) },
{ "AppearancePage", typeof(AppearancePage) },
{ "IconographyPage", typeof(IconographyPage) },
{ "TypographyPage", typeof(TypographyPage) },
{ "ThemePage", typeof(ThemePage) },
{ "ColorsPage", typeof(ColorsPage) },
{ "FontsPage", typeof(FontsPage) },
{ "StartupPage", typeof(StartupPage) },
{ "AboutPage", typeof(AboutPage) },
{ "Settings", typeof(SettingsPage) }
};
// 加载插件页面
LoadPluginSettingsPages();
// 初始化导航菜单(内置+插件)
InitializeNavigationMenu();
// 默认选中首页
if (NavigationViewControl.MenuItems.Count > 0)
{
// 首先导航到首页
NavigateToPage("HomePage");
// 然后选中首页菜单项
NavigationViewControl.SelectedItem = NavigationViewControl.MenuItems[0];
NavigationViewControl.Header = "首页";
}
// 初始化标题栏边距
UpdateAppTitleBarMargin();
// 窗口生命周期事件注册
this.Loaded += (sender, e) =>
{
SetMaxSizeAndCenter();
RegisterDpiChangedListener();
};
// 窗口关闭时释放资源
this.Closed += (sender, e) =>
{
UnregisterDpiChangedListener();
_pages.Clear();
_pageTypes.Clear();
};
// 修复触摸屏操作后鼠标指针消失的问题
FixTouchScreenCursorIssue();
// 窗口状态改变时调整大小限制
this.StateChanged += (sender, e) =>
{
if (this.WindowState == WindowState.Maximized)
{
// 保存窗口原始位置和大小
_originalLeft = this.Left;
_originalTop = this.Top;
_originalWidth = this.Width;
_originalHeight = this.Height;
// 标记窗口曾经最大化过
_wasMaximized = true;
// 最大化时清除最大尺寸限制
this.MaxWidth = double.PositiveInfinity;
this.MaxHeight = double.PositiveInfinity;
}
else if (this.WindowState == WindowState.Normal && _wasMaximized)
{
// 从最大化恢复到正常状态时,恢复窗口原始位置和大小
this.Left = _originalLeft;
this.Top = _originalTop;
this.Width = _originalWidth;
this.Height = _originalHeight;
// 重置标记
_wasMaximized = false;
// 只设置最大尺寸,不改变窗口位置
SetMaxSizeOnly();
}
else if (this.WindowState == WindowState.Normal)
{
// 正常状态下只设置最大尺寸限制
SetMaxSizeOnly();
}
// 窗口状态改变时更新标题栏显示
UpdateAppTitleBarMargin();
};
// 窗口大小改变时更新标题栏显示
this.SizeChanged += (sender, e) =>
{
if (NavigationViewControl.DisplayMode == NavigationViewDisplayMode.Minimal)
{
UpdateAppTitleBarMargin();
}
};
}
#region
private void FixTouchScreenCursorIssue()
{
// 触摸结束时强制显示鼠标指针
this.TouchUp += (s, e) =>
{
ShowCursor(true);
};
// 鼠标进入窗口时确保指针可见
this.MouseEnter += (s, e) =>
{
ShowCursor(true);
};
// 窗口激活时确保指针可见
this.Activated += (s, e) =>
{
ShowCursor(true);
};
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int ShowCursor(bool bShow);
#endregion
#region
private void LoadPluginSettingsPages()
{
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/
/// <summary>
/// 获取当前窗口所在屏幕的工作区尺寸(DIP单位)
/// </summary>
private void GetWorkAreaSize(out double workAreaWidthDip, out double workAreaHeightDip, out double screenLeftDip, out double screenTopDip)
{
// 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;
if (source?.CompositionTarget != null)
{
dpiScaleX = source.CompositionTarget.TransformToDevice.M11;
dpiScaleY = source.CompositionTarget.TransformToDevice.M22;
}
// 3. 物理像素 → WPF设备无关像素(DIP)转换
workAreaWidthDip = workingArea.Width / dpiScaleX;
workAreaHeightDip = workingArea.Height / dpiScaleY;
screenLeftDip = screenBounds.Left / dpiScaleX;
screenTopDip = screenBounds.Top / dpiScaleY;
}
private void SetMaxSizeAndCenter()
{
if (!this.IsLoaded) return;
GetWorkAreaSize(out double workAreaWidthDip, out double workAreaHeightDip, out double screenLeftDip, out double screenTopDip);
// 设置窗口最大尺寸
this.MaxWidth = workAreaWidthDip;
this.MaxHeight = workAreaHeightDip;
// 窗口在当前屏幕居中(解决副屏居中跑偏问题)
this.Left = screenLeftDip + (workAreaWidthDip - this.ActualWidth) / 2;
this.Top = screenTopDip + (workAreaHeightDip - this.ActualHeight) / 2;
}
private void SetMaxSizeOnly()
{
if (!this.IsLoaded) return;
GetWorkAreaSize(out double workAreaWidthDip, out double workAreaHeightDip, out _, out _);
// 只设置窗口最大尺寸,不改变窗口位置
this.MaxWidth = workAreaWidthDip;
this.MaxHeight = workAreaHeightDip;
}
#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;
}
// 处理普通导航项
if (args.SelectedItem is NavigationViewItem selectedItem)
{
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)) return;
try
{
// 页面缓存:已创建的页面直接复用,保留状态,避免重复初始化
if (!_pages.TryGetValue(pageTag, out var cachedPage))
{
cachedPage = Activator.CreateInstance(pageType);
_pages.Add(pageTag, cachedPage);
}
// 导航到缓存页面
rootFrame.Navigate(cachedPage.GetType(), cachedPage);
}
catch (Exception ex)
{
MessageBox.Show($"导航到页面时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void OnNavigationViewBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
{
if (rootFrame.CanGoBack) rootFrame.GoBack();
}
private void OnRootFrameNavigated(object sender, NavigationEventArgs e)
{
// 导航后同步导航项选中状态
Type currentPageType = rootFrame.SourcePageType;
// 处理设置项的选中状态
if (currentPageType == typeof(SettingsPage))
{
NavigationViewControl.SelectedItem = NavigationViewControl.SettingsItem;
NavigationViewControl.Header = "设置";
return;
}
// 同步其他页面的选中状态
foreach (var kvp in _pageTypes)
{
if (kvp.Value == currentPageType)
{
var targetItem = FindNavigationViewItemByTag(kvp.Key);
if (targetItem != null && NavigationViewControl.SelectedItem != targetItem)
{
NavigationViewControl.SelectedItem = targetItem;
NavigationViewControl.Header = targetItem.Content;
}
break;
}
}
}
private void NavigationViewControl_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
{
UpdateAppTitleBarMargin(sender);
}
private void UpdateAppTitleBarMargin()
{
UpdateAppTitleBarMargin(NavigationViewControl);
}
private void UpdateAppTitleBarMargin(NavigationView sender)
{
Thickness currMargin = AppTitleBar.Margin;
if (sender.DisplayMode == NavigationViewDisplayMode.Minimal)
{
AppTitleBar.Margin = new Thickness((sender.CompactPaneLength * 2), currMargin.Top, currMargin.Right, currMargin.Bottom);
// 当窗口宽度非常小时,隐藏图标和应用设置文字
if (this.ActualWidth < 400)
{
AppTitle.Visibility = Visibility.Collapsed;
}
else
{
AppTitle.Visibility = Visibility.Visible;
}
}
else
{
AppTitleBar.Margin = new Thickness(sender.CompactPaneLength, currMargin.Top, currMargin.Right, currMargin.Bottom);
AppTitle.Visibility = Visibility.Visible;
}
AppTitleBar.Visibility = sender.PaneDisplayMode == NavigationViewPaneDisplayMode.Top ? Visibility.Collapsed : Visibility.Visible;
}
private NavigationViewItem FindNavigationViewItemByTag(string tag)
{
// 遍历主菜单
foreach (var item in NavigationViewControl.MenuItems)
{
if (item is NavigationViewItem navItem)
{
if (navItem.Tag as string == tag)
return navItem;
// 遍历子菜单,自动展开父项
foreach (var childItem in navItem.MenuItems)
{
if (childItem is NavigationViewItem childNavItem && childNavItem.Tag as string == tag)
{
navItem.IsExpanded = true;
return childNavItem;
}
}
}
}
// 遍历底部菜单
foreach (var item in NavigationViewControl.FooterMenuItems)
{
if (item is NavigationViewItem navItem && navItem.Tag as string == tag)
{
return navItem;
}
}
return null;
}
#endregion
#region
private void OnControlsSearchBoxQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (string.IsNullOrWhiteSpace(args.QueryText)) return;
string query = args.QueryText.Trim().ToLower();
var allNavItems = GetAllNavigationItems();
var targetItem = allNavItems.FirstOrDefault(item =>
item.Content?.ToString().ToLower().Contains(query) == true);
if (targetItem != null)
{
NavigationViewControl.SelectedItem = targetItem;
}
}
private void OnControlsSearchBoxTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if (args.Reason != AutoSuggestionBoxTextChangeReason.UserInput) return;
string query = sender.Text.Trim().ToLower();
var suggestions = new List<string>();
if (!string.IsNullOrEmpty(query))
{
var allNavItems = GetAllNavigationItems();
suggestions = allNavItems
.Where(item => item.Content?.ToString().ToLower().Contains(query) == true)
.Select(item => item.Content.ToString())
.ToList();
}
sender.ItemsSource = suggestions;
}
// 统一获取所有导航项(主菜单+子菜单+底部菜单+插件页面)
private List<NavigationViewItem> GetAllNavigationItems()
{
var items = new List<NavigationViewItem>();
// 主菜单+子菜单
foreach (var item in NavigationViewControl.MenuItems)
{
if (item is NavigationViewItem navItem)
{
items.Add(navItem);
foreach (var child in navItem.MenuItems)
{
if (child is NavigationViewItem childNavItem)
items.Add(childNavItem);
}
}
}
// 底部菜单
foreach (var item in NavigationViewControl.FooterMenuItems)
{
if (item is NavigationViewItem navItem)
items.Add(navItem);
}
return items;
}
#endregion
}
}