using System.Windows; using System.Windows.Media.Animation; using System.Windows.Controls; using System.Windows.Input; using XFEToolBox.Client.Model; using XFEToolBox.Client.Profiles.CrossVersionProfiles; using XFEToolBox.Client.Utilities; using XFEToolBox.Client.Utilities.Server; using XFEToolBox.WpfCore.Tutorial; using XFEToolBox.WpfCore.Windowing; using MainWindowViewModel = XFEToolBox.Client.ViewModel.Windows.MainWindowViewModel; namespace XFEToolBox.Client.Views.Windows; /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { public static MainWindow? Current { get; private set; } public MainWindowViewModel ViewModel { get; private set; } private readonly ITutorialService tutorialService = new TutorialService(); private readonly CancellationTokenSource tutorialCancellationSource = new(); private bool firstRunTutorialChecked; public MainWindow() { InitializeComponent(); WindowWorkAreaHelper.Attach(this); ViewModel = new MainWindowViewModel(this); DataContext = ViewModel; Current = this; Width = SystemProfile.MainWindowWidth; Height = SystemProfile.MainWindowHeight; WindowState = SystemProfile.StartWithMaximize ? WindowState.Maximized : WindowState.Normal; } private void CaptionBar_CloseRequested(object? sender, EventArgs e) => MainWindowViewModel.CloseWindow(); private async void Window_Loaded(object sender, RoutedEventArgs e) { mainButton.IsChecked = true; ViewModel.GetDPIScale(); if (firstRunTutorialChecked) return; firstRunTutorialChecked = true; await Dispatcher.InvokeAsync(() => { }, System.Windows.Threading.DispatcherPriority.ContextIdle); if (!SystemProfile.MainTutorialCompleted) { var result = await StartMainTutorialAsync(tutorialCancellationSource.Token); if (result is TutorialResult.Completed or TutorialResult.Skipped) SystemProfile.MainTutorialCompleted = true; } if (SystemProfile.CheckForUpdatesOnStartup) _ = UpgradeService.CheckForUpdatesAsync(userInitiated: false, owner: this); } /// /// 启动主窗口教程。除首次自动调用外,设置页或帮助菜单也可以直接复用此入口。 /// public Task StartMainTutorialAsync(CancellationToken cancellationToken = default) => tutorialService.StartAsync(TutorialHost, CreateMainTutorialSteps(), new TutorialOptions { CalloutWidth = 350, MotionDuration = TimeSpan.FromMilliseconds(340), FinishText = "开始探索" }, cancellationToken); private IReadOnlyList CreateMainTutorialSteps() { var steps = new List { new() { Key = "welcome", Title = "欢迎使用 XFE·工具箱", Description = "用一分钟认识窗口操作和主要功能。教程会跟随目标平滑移动,窗口尺寸变化时也会自动重新定位。", Placement = TutorialPlacement.Center, SpotlightPadding = new Thickness(0), Hint = "可随时点击“跳过教程”;之后仍可通过 StartMainTutorialAsync 接口重新启动。" }, new() { Key = "caption-bar", Title = "拖动、最大化与窗口控制", Description = "按住上方中间的短横线即可移动窗口;双击同一区域可最大化,再次双击可还原。右侧还可以最小化或关闭软件。", Target = CaptionBar.DragSurfaceElement, Placement = TutorialPlacement.Bottom, SpotlightPadding = new Thickness(5, 3, 5, 4), SpotlightCornerRadius = 12, AllowTargetInteraction = true, Hint = "高亮区域当前可直接操作,现在就可以拖动或双击试试看。" }, new() { Key = "home", Title = "首页", Description = "通过统一搜索、快速访问、继续工作和活动中心,快速回到正在处理的内容。", Target = mainButton, Placement = TutorialPlacement.Right, SpotlightPadding = new Thickness(6), SpotlightCornerRadius = 17 }, new() { Key = "toolbox", Title = "工具箱", Description = "集中浏览本地与服务器工具。列表会先读取缓存立即显示,再在后台异步刷新。", Target = toolBoxButton, Placement = TutorialPlacement.Right, SpotlightPadding = new Thickness(6), SpotlightCornerRadius = 17 }, new() { Key = "chat", Title = "聊天", Description = "登录后可添加好友、发现或创建群聊,并发送文字与文件消息。", Target = chatButton, Placement = TutorialPlacement.Right, SpotlightPadding = new Thickness(6), SpotlightCornerRadius = 17 }, new() { Key = "console", Title = "C# 控制台", Description = "快速编写和运行 C# 代码片段,适合验证想法、调试表达式与处理临时代码。", Target = consoleButton, Placement = TutorialPlacement.Right, SpotlightPadding = new Thickness(6), SpotlightCornerRadius = 17 }, new() { Key = "workshop", Title = "工具工坊", Description = "所有用户都可以创建、预览、运行和导出本地 WPF 工具;服务器发布仍由管理员控制。", Target = workshopButton, Placement = TutorialPlacement.Right, SpotlightPadding = new Thickness(6), SpotlightCornerRadius = 17 }, new() { Key = "download", Title = "下载专区", Description = "查找常用开发软件并管理下载;支持多线程下载、进度控制和下载后操作。", Target = downloadButton, Placement = TutorialPlacement.Right, SpotlightPadding = new Thickness(6), SpotlightCornerRadius = 17 }, new() { Key = "settings", Title = "选项设置", Description = "调整启动方式、下载目录、并发数量等客户端行为,让工具箱更符合你的使用习惯。", Target = settingButton, Placement = TutorialPlacement.Right, SpotlightPadding = new Thickness(6), SpotlightCornerRadius = 17 }, new() { Key = "account", Title = "账户与个人中心", Description = "点击左下角账户卡片登录或注册;登录后可查看资料、管理账户并使用服务器相关能力。", Target = AccountEntry, Placement = TutorialPlacement.Right, SpotlightPadding = new Thickness(6), SpotlightCornerRadius = 17 } }; if (serverManagementButton.IsVisible) { steps.Add(new TutorialStep { Key = "server-management", Title = "服务器管理", Description = "管理员可以在这里查看服务器状态,并管理用户、工具与下载资源。普通账户不会显示此入口。", Target = serverManagementButton, Placement = TutorialPlacement.Right, SpotlightPadding = new Thickness(6), SpotlightCornerRadius = 17 }); } steps.Add(new TutorialStep { Key = "finish", Title = "准备就绪", Description = "现在可以从左侧选择任意功能开始使用。教程系统也可复用于任意页面、弹窗或新功能的分步说明。", Placement = TutorialPlacement.Center, SpotlightPadding = new Thickness(0), NextButtonText = "开始探索", Hint = "按 ← / → 切换步骤,按 Esc 可跳过教程。" }); return steps; } private void ContentFrame_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) { var storyboard = new Storyboard(); var fadeIn = new DoubleAnimation { From = 0, To = 1, Duration = new Duration(TimeSpan.FromMilliseconds(300)), EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } }; Storyboard.SetTarget(fadeIn, contentFrame); Storyboard.SetTargetProperty(fadeIn, new PropertyPath("Opacity")); storyboard.Children.Add(fadeIn); storyboard.Begin(); } private void WindowResizeGrip_ResizeCompleted(object? sender, EventArgs e) { SystemProfile.MainWindowWidth = Width; SystemProfile.MainWindowHeight = Height; } private void BackTabBorder_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) => NavigationCenter.GoBack(); private void AccountEntry_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { e.Handled = true; ViewModel.NavigateToPageCommand.Execute("profile"); } public void NavigateAndSelect(string pageTag) { ViewModel.NavigateToPageCommand.Execute(pageTag); mainButton.IsChecked = pageTag == "home"; chatButton.IsChecked = pageTag == "chat"; toolBoxButton.IsChecked = pageTag == "tool"; workshopButton.IsChecked = pageTag == "workshop"; consoleButton.IsChecked = pageTag == "console"; downloadButton.IsChecked = pageTag == "download"; settingButton.IsChecked = pageTag == "setting"; serverManagementButton.IsChecked = pageTag is "serverManagement" or "serverOverview" or "userManagement" or "toolManagement" or "softwareManagement"; } }