- Fluent Design 风格 UI,深色/浅色主题切换 - 平滑过渡动画(页面切换、按钮交互、状态变化) - 五大核心功能:安装管理、墨迹管理、格式转换、触摸设置、激活管理 - 基于 dotnetCampus.EasiPlugin.Sdk v2.1.1-alpha.3 插件框架 - WPF 安装向导(欢迎页 -> 安装页 -> 完成页)
351 lines
12 KiB
C#
351 lines
12 KiB
C#
/*
|
||
* MainWindow.xaml.cs - Better-Seewo 管理器主窗口逻辑
|
||
* 负责自定义标题栏拖动、窗口控制、页面导航、主题切换和动画效果
|
||
* 作者:雾启工作室
|
||
*/
|
||
|
||
using System;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Input;
|
||
using System.Windows.Media;
|
||
using System.Windows.Media.Animation;
|
||
|
||
namespace Better_Seewo.Manager
|
||
{
|
||
/// <summary>
|
||
/// MainWindow 的交互逻辑 - Fluent Design 风格主窗口
|
||
/// </summary>
|
||
public partial class MainWindow : Window
|
||
{
|
||
/// <summary>
|
||
/// 导航指示条动画时长(毫秒)
|
||
/// </summary>
|
||
private const int IndicatorAnimationDuration = 200;
|
||
|
||
/// <summary>
|
||
/// 页面淡入淡出动画时长(毫秒)
|
||
/// </summary>
|
||
private const int PageTransitionDuration = 300;
|
||
|
||
/// <summary>
|
||
/// 指示条目标宽度
|
||
/// </summary>
|
||
private const double IndicatorTargetWidth = 3;
|
||
|
||
/// <summary>
|
||
/// 当前选中的导航按钮名称
|
||
/// </summary>
|
||
private string _currentNavTag = "Install";
|
||
|
||
/// <summary>
|
||
/// 构造函数,初始化窗口组件
|
||
/// </summary>
|
||
public MainWindow()
|
||
{
|
||
InitializeComponent();
|
||
// 默认选中安装页面
|
||
SetActiveNavigation("BtnInstall", "IndicatorInstall");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 窗口加载完成事件处理 - 触发入场动画和初始页面加载
|
||
/// </summary>
|
||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
// 入场动画已通过 XAML Trigger 触发
|
||
// 导航到默认页面
|
||
NavigateToPage("Install");
|
||
}
|
||
|
||
#region ===== 自定义标题栏拖动 =====
|
||
|
||
/// <summary>
|
||
/// 标题栏鼠标左键按下 - 实现窗口拖动
|
||
/// </summary>
|
||
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||
{
|
||
// 如果点击的是按钮区域则不触发拖动
|
||
if (e.Source is Button) return;
|
||
|
||
// 调用 DragMove 实现窗口拖动
|
||
if (e.LeftButton == MouseButtonState.Pressed)
|
||
{
|
||
DragMove();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 标题栏鼠标双击 - 切换最大化/还原
|
||
/// </summary>
|
||
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
|
||
{
|
||
base.OnMouseLeftButtonDown(e);
|
||
|
||
// 双击标题栏区域切换窗口状态
|
||
if (e.ClickCount == 2)
|
||
{
|
||
ToggleMaximize();
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region ===== 窗口控制按钮 =====
|
||
|
||
/// <summary>
|
||
/// 最小化按钮点击
|
||
/// </summary>
|
||
private void BtnMinimize_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
// 将窗口最小化到任务栏
|
||
WindowState = WindowState.Minimized;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 最大化/还原按钮点击
|
||
/// </summary>
|
||
private void BtnMaximize_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
ToggleMaximize();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭按钮点击 - 关闭应用程序
|
||
/// </summary>
|
||
private void BtnClose_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
Application.Current.Shutdown();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换窗口最大化/还原状态
|
||
/// </summary>
|
||
private void ToggleMaximize()
|
||
{
|
||
if (WindowState == WindowState.Maximized)
|
||
{
|
||
WindowState = WindowState.Normal;
|
||
// 更新按钮图标为最大化图标
|
||
BtnMaximize.Content = "\uE922";
|
||
}
|
||
else
|
||
{
|
||
WindowState = WindowState.Maximized;
|
||
// 更新按钮图标为还原图标
|
||
BtnMaximize.Content = "\uE923";
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region ===== 导航按钮切换 =====
|
||
|
||
/// <summary>
|
||
/// 导航按钮点击事件 - 切换Frame页面内容
|
||
/// </summary>
|
||
private void NavButton_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (sender is Button btn && btn.Tag is string tag)
|
||
{
|
||
// 如果点击的是当前已选中项,则忽略
|
||
if (tag == _currentNavTag) return;
|
||
|
||
// 更新当前选中项
|
||
_currentNavTag = tag;
|
||
|
||
// 根据按钮名称设置选中状态和指示条动画
|
||
SetActiveNavigation(btn.Name, GetIndicatorName(btn.Name));
|
||
|
||
// 导航到对应页面
|
||
NavigateToPage(tag);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据按钮名称获取对应的指示条名称
|
||
/// </summary>
|
||
/// <param name="buttonName">按钮名称</param>
|
||
/// <returns>指示条名称</returns>
|
||
private string GetIndicatorName(string buttonName)
|
||
{
|
||
return buttonName switch
|
||
{
|
||
"BtnInstall" => "IndicatorInstall",
|
||
"BtnInk" => "IndicatorInk",
|
||
"BtnConvert" => "IndicatorConvert",
|
||
"BtnTouch" => "IndicatorTouch",
|
||
"BtnActivate" => "IndicatorActivate",
|
||
_ => "IndicatorInstall"
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置当前选中的导航按钮,并触发指示条动画
|
||
/// </summary>
|
||
/// <param name="activeButtonName">选中的按钮名称</param>
|
||
/// <param name="activeIndicatorName">对应的指示条名称</param>
|
||
private void SetActiveNavigation(string activeButtonName, string activeIndicatorName)
|
||
{
|
||
// 所有导航按钮列表
|
||
string[] buttonNames = { "BtnInstall", "BtnInk", "BtnConvert", "BtnTouch", "BtnActivate" };
|
||
// 所有指示条列表
|
||
string[] indicatorNames = { "IndicatorInstall", "IndicatorInk", "IndicatorConvert", "IndicatorTouch", "IndicatorActivate" };
|
||
|
||
for (int i = 0; i < buttonNames.Length; i++)
|
||
{
|
||
// 查找按钮
|
||
var btn = FindName(buttonNames[i]) as Button;
|
||
// 查找指示条
|
||
var indicator = FindName(indicatorNames[i]) as Border;
|
||
|
||
if (btn == null || indicator == null) continue;
|
||
|
||
if (buttonNames[i] == activeButtonName)
|
||
{
|
||
// 选中状态:高亮前景色,显示指示条
|
||
btn.Foreground = (Brush)FindResource("AccentBrush");
|
||
|
||
// 播放指示条展开动画(宽度从0到3px)
|
||
AnimateIndicatorWidth(indicator, IndicatorTargetWidth);
|
||
}
|
||
else
|
||
{
|
||
// 非选中状态:恢复默认前景色,隐藏指示条
|
||
btn.Foreground = (Brush)FindResource("TextSecondaryBrush");
|
||
|
||
// 播放指示条收缩动画(宽度从当前值到0)
|
||
AnimateIndicatorWidth(indicator, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 为导航指示条播放宽度动画
|
||
/// </summary>
|
||
/// <param name="indicator">指示条控件</param>
|
||
/// <param name="targetWidth">目标宽度</param>
|
||
private void AnimateIndicatorWidth(Border indicator, double targetWidth)
|
||
{
|
||
if (indicator == null) return;
|
||
|
||
// 创建宽度动画
|
||
var widthAnimation = new DoubleAnimation
|
||
{
|
||
From = indicator.Width,
|
||
To = targetWidth,
|
||
Duration = TimeSpan.FromMilliseconds(IndicatorAnimationDuration),
|
||
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
|
||
};
|
||
|
||
// 应用动画到指示条的Width属性
|
||
indicator.BeginAnimation(WidthProperty, widthAnimation);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region ===== 页面导航 =====
|
||
|
||
/// <summary>
|
||
/// 导航到指定页面,带淡入淡出动画效果
|
||
/// </summary>
|
||
/// <param name="pageTag">页面标识(Install/Ink/Convert/Touch/Activate)</param>
|
||
private void NavigateToPage(string pageTag)
|
||
{
|
||
// 创建淡出动画(当前页面)
|
||
var fadeOutAnimation = new DoubleAnimation
|
||
{
|
||
From = 1.0,
|
||
To = 0.0,
|
||
Duration = TimeSpan.FromMilliseconds(PageTransitionDuration / 2),
|
||
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseIn }
|
||
};
|
||
|
||
// 淡出动画完成后切换页面
|
||
fadeOutAnimation.Completed += (s, e) =>
|
||
{
|
||
// 根据页面标识加载对应页面
|
||
// TODO: 创建各个功能页面后取消注释
|
||
// switch (pageTag)
|
||
// {
|
||
// case "Install":
|
||
// ContentFrame.Navigate(new Pages.InstallPage());
|
||
// break;
|
||
// case "Ink":
|
||
// ContentFrame.Navigate(new Pages.InkPage());
|
||
// break;
|
||
// case "Convert":
|
||
// ContentFrame.Navigate(new Pages.ConvertPage());
|
||
// break;
|
||
// case "Touch":
|
||
// ContentFrame.Navigate(new Pages.TouchPage());
|
||
// break;
|
||
// case "Activate":
|
||
// ContentFrame.Navigate(new Pages.ActivatePage());
|
||
// break;
|
||
// }
|
||
|
||
// 创建淡入动画(新页面)
|
||
var fadeInAnimation = new DoubleAnimation
|
||
{
|
||
From = 0.0,
|
||
To = 1.0,
|
||
Duration = TimeSpan.FromMilliseconds(PageTransitionDuration / 2),
|
||
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
|
||
};
|
||
|
||
// 应用淡入动画到Frame
|
||
ContentFrame.BeginAnimation(OpacityProperty, fadeInAnimation);
|
||
};
|
||
|
||
// 应用淡出动画到Frame
|
||
ContentFrame.BeginAnimation(OpacityProperty, fadeOutAnimation);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region ===== 主题切换 =====
|
||
|
||
/// <summary>
|
||
/// 主题切换按钮点击事件
|
||
/// </summary>
|
||
private void BtnThemeToggle_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
// 切换到相反主题
|
||
bool newIsDark = !App.IsDarkTheme;
|
||
|
||
// 调用 App.SwitchTheme 切换主题资源
|
||
App.SwitchTheme(newIsDark);
|
||
|
||
// 更新主题图标
|
||
UpdateThemeIcon(newIsDark);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据当前主题更新切换按钮的图标
|
||
/// </summary>
|
||
/// <param name="isDark">当前是否为深色主题</param>
|
||
private void UpdateThemeIcon(bool isDark)
|
||
{
|
||
if (ThemeIcon == null) return;
|
||
|
||
if (isDark)
|
||
{
|
||
// 深色主题时显示太阳图标(提示可以切换到浅色)
|
||
ThemeIcon.Data = Geometry.Parse(
|
||
"M12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,2L14.39,5.42C13.73,5.14 13,5 12,5C11,5 10.27,5.14 9.61,5.42L12,2M3.34,7L7.5,6.35C7,7 6.63,7.73 6.43,8.5L3.34,7M3.36,17L6.44,15.5C6.64,16.28 7,17 7.5,17.65L3.36,17M20.64,7L17.55,8.5C17.36,7.72 17,7 16.5,6.35L20.64,7M20.65,17L16.5,17.65C17,17 17.37,16.27 17.57,15.5L20.65,17M12,22L9.61,18.58C10.27,18.86 11,19 12,19C13,19 13.73,18.86 14.39,18.58L12,22Z"
|
||
);
|
||
}
|
||
else
|
||
{
|
||
// 浅色主题时显示月亮图标(提示可以切换到深色)
|
||
ThemeIcon.Data = Geometry.Parse(
|
||
"M17.75,4.09L15.22,6.03L16.13,9.09L13.5,7.28L10.87,9.09L11.78,6.03L9.25,4.09L12.44,4L13.5,1L14.56,4L17.75,4.09M21.25,11L19.61,12.25L20.2,14.23L18.5,13.06L16.8,14.23L17.39,12.25L15.75,11L17.81,10.95L18.5,9L19.19,10.95L21.25,11M18.97,15.95C19.8,15.87 20.69,17.05 20.16,17.8C19.84,18.25 19.38,18.57 18.97,18.95C16.73,21.04 14.04,22.5 11,22.5C7.96,22.5 5.27,21.04 3.03,18.95C2.62,18.57 2.16,18.25 1.84,17.8C1.31,17.05 2.2,15.87 3.03,15.95C7.33,16.37 10.17,12.37 11,9.42C11.83,12.37 14.67,16.37 18.97,15.95Z"
|
||
);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|