# WPF_Learn **Repository Path**: verifieroiz/wpf_-learn ## Basic Information - **Project Name**: WPF_Learn - **Description**: WPF学习 - **Primary Language**: C# - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-06-02 - **Last Updated**: 2025-10-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # WPF 学习项目 这是一个基于 .NET 8 和 WPF 的学习项目集合,展示了现代 WPF 应用开发的最佳实践和常见 UI 模式实现。 ## 项目概览 本仓库包含两个独立的 WPF 示例项目: 1. **DrawerMenu** - 抽屉式侧边栏菜单应用 2. **MiniTheme** - 主题切换和多语言支持应用 两个项目都基于相同的现代化技术栈和架构模式,展示了企业级 WPF 应用的开发方法。 ## 核心技术栈 ### 共同技术栈 - **.NET 8.0** - 最新的 .NET 框架 - **WPF (Windows Presentation Foundation)** - 微软的桌面 UI 框架 - **MVVM 模式** - Model-View-ViewModel 架构 - **Microsoft.Extensions.Hosting** - 通用主机(Generic Host) - **Microsoft.Extensions.DependencyInjection** - 依赖注入容器 - **CommunityToolkit.Mvvm** - MVVM 工具包(使用 Source Generator) ### 架构设计原则 - **依赖注入(DI)**:所有服务通过构造函数注入 - **关注点分离**:清晰的 View、ViewModel、Service 分层 - **接口抽象**:面向接口编程,便于测试和扩展 - **SOLID 原则**:单一职责、开闭原则、依赖倒置等 - **Source Generator**:利用编译时代码生成减少样板代码 ## 项目一:DrawerMenu(抽屉菜单) ### 设计思路 **目标**:实现一个带有平滑动画效果的抽屉式侧边栏菜单,展示现代化的导航模式。 ### 核心功能 - 抽屉式侧边栏导航 - 自定义窗口样式(无边框、圆角、阴影) - 自定义窗口控制按钮 - 多页面导航系统 - 平滑的动画过渡效果 ### 实现思路 #### 1. 抽屉菜单实现 ``` 思路:使用 Canvas.Left 属性 + DispatcherTimer 实现平滑动画 - 抽屉默认位置在屏幕左侧外(-280px) - 打开时动画到 0px - 关闭时动画回 -280px - 使用缓动函数(EaseOut)实现平滑过渡 ``` **关键代码逻辑**: ```csharp // 使用 DispatcherTimer 实现帧动画 private void AnimateDrawer(bool open) { var timer = new DispatcherTimer(); timer.Tick += (s, e) => { // 计算当前位置 double progress = elapsed / duration; double easedProgress = 1 - Math.Pow(1 - progress, 3); // EaseOut double newLeft = startLeft + (targetLeft - startLeft) * easedProgress; Canvas.SetLeft(drawer, newLeft); }; } ``` #### 2. 自定义窗口样式 ``` 思路:使用 WindowStyle="None" + AllowsTransparency + Border 实现 - WindowStyle="None":移除默认标题栏和边框 - AllowsTransparency="True":允许透明和圆角 - Border + DropShadowEffect:实现圆角和阴影 - 手动实现窗口控制按钮(最小化、最大化、关闭) ``` **UI 结构**: ``` Window (Transparent Background) └─ Border (White, 8px CornerRadius, DropShadow) ├─ 自定义标题栏 │ ├─ 菜单按钮 │ ├─ 标题文字 │ └─ 窗口控制按钮(-/□/×) └─ 内容区域 ``` #### 3. 导航服务实现 ``` 思路:通过 ContentControl 绑定动态切换页面 - INavigationService 接口定义导航方法 - NavigationService 维护 CurrentView 属性 - 通过 IServiceProvider 解析 View 实例 - ViewModel 类型映射到 View 类型 ``` **导航流程**: ``` 用户点击菜单项 → ViewModel 调用 NavigateTo() → NavigationService 解析 HomePage 实例 → 更新 CurrentView 属性 → UI 自动更新(通过 INotifyPropertyChanged) ``` #### 4. 依赖注入架构 ```csharp // App.xaml.cs 中配置服务 var builder = Host.CreateApplicationBuilder(); // 注册服务(单例) builder.Services.AddSingleton(provider => new NavigationService(provider)); // 注册 ViewModels(单例) builder.Services.AddSingleton(); builder.Services.AddTransient(); // 注册 Views(瞬态,每次导航创建新实例) builder.Services.AddTransient(); // 构建宿主 _host = builder.Build(); // 显示主窗口 var mainWindow = _host.Services.GetRequiredService(); mainWindow.Show(); ``` ### 技术亮点 1. **帧动画实现**:使用 DispatcherTimer 而非 Storyboard,更灵活的动画控制 2. **服务定位模式**:NavigationService 通过 IServiceProvider 动态解析视图 3. **通用主机模式**:借鉴 ASP.NET Core 的宿主模式,统一生命周期管理 4. **Source Generator**:CommunityToolkit.Mvvm 自动生成属性和命令代码 --- ## 项目二:MiniTheme(主题切换) ### 设计思路 **目标**:实现一个支持动态主题切换、系统主题跟随和多语言切换的应用,展示 WPF 资源字典动态管理。 ### 核心功能 - 亮色/暗色主题切换 - 自动跟随系统主题 - 多语言动态切换(中/英/日/韩) - 自定义标题栏(跟随主题) - 配置持久化 ### 实现思路 #### 1. 主题系统架构 ``` 思路:使用 ResourceDictionary 动态加载主题资源 - 定义主题资源字典(LightTheme.xaml / DarkTheme.xaml) - 使用 DynamicResource 绑定主题颜色 - 运行时动态切换 MergedDictionaries ``` **主题资源结构**: ```xml ... ``` **切换逻辑**: ```csharp // ThemeService.cs public void ApplyTheme(ThemeMode mode) { // 1. 移除旧主题 var oldTheme = Application.Current.Resources.MergedDictionaries .FirstOrDefault(d => d.Source?.OriginalString.Contains("Theme.xaml")); if (oldTheme != null) { Application.Current.Resources.MergedDictionaries.Remove(oldTheme); } // 2. 加载新主题 var themePath = mode == ThemeMode.Light ? "Themes/LightTheme.xaml" : "Themes/DarkTheme.xaml"; var newTheme = new ResourceDictionary { Source = new Uri(themePath, UriKind.Relative) }; // 3. 应用新主题 Application.Current.Resources.MergedDictionaries.Add(newTheme); } ``` #### 2. 系统主题监控 ``` 思路:监听 Windows 注册表变化 - 监控路径:HKEY_CURRENT_USER\Software\Microsoft\Windows\ CurrentVersion\Themes\Personalize - 监控键值:AppsUseLightTheme - 值变化时触发主题切换事件 ``` **实现代码**: ```csharp // SystemThemeMonitor.cs public class SystemThemeMonitor { private RegistryMonitor? _monitor; public event EventHandler? ThemeChanged; public void Start() { _monitor = new RegistryMonitor( RegistryHive.CurrentUser, @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ); _monitor.RegChanged += OnRegistryChanged; _monitor.Start(); } private void OnRegistryChanged(object sender, EventArgs e) { var isLight = GetSystemTheme(); ThemeChanged?.Invoke(this, isLight ? ThemeMode.Light : ThemeMode.Dark); } } ``` #### 3. 多语言支持 ``` 思路:使用 ResourceDictionary 存储语言资源 - 每种语言一个 XAML 文件(zh-CN.xaml, en-US.xaml...) - 使用 DynamicResource 绑定文本 - 运行时切换语言资源字典 ``` **语言资源示例**: ```xml 主题切换演示 主题设置 ... ``` **XAML 使用**: ```xml ``` #### 4. 配置持久化 ``` 思路:使用 Options Pattern + JSON 配置文件 - appsettings.json 存储配置 - IOptionsMonitor 读取配置 - ConfigService 负责配置的读写 ``` **配置流程**: ``` 用户切换主题 → ThemeService.ApplyTheme() → ConfigService.SaveThemeMode() → 更新 appsettings.json → IOptionsMonitor 触发配置变更事件 ``` **配置代码**: ```csharp // ConfigService.cs public void SaveThemeMode(ThemeMode mode) { var settings = _options.CurrentValue; settings.ThemeMode = mode; var json = JsonSerializer.Serialize(new { AppSettings = settings }); File.WriteAllText("appsettings.json", json); // Options 系统会自动重新加载配置 } ``` #### 5. ApplicationHostService ``` 思路:使用 IHostedService 统一管理启动流程 - 实现 StartAsync:初始化服务、应用配置、显示窗口 - 实现 StopAsync:清理资源、停止监控 ``` **启动流程**: ```csharp public async Task StartAsync(CancellationToken cancellationToken) { // 1. 应用保存的语言配置 _languageService.ApplyLanguage(_settings.Language); // 2. 应用保存的主题配置 await ApplyThemeAsync(_settings.ThemeMode); // 3. 启动系统主题监控(如果是 Auto 模式) if (_settings.ThemeMode == ThemeMode.Auto) { _systemThemeMonitor.Start(); } // 4. 初始化 ViewModel(首次导航) _mainViewModel.Initialize(); // 5. 显示主窗口 _mainWindow.Show(); } ``` #### 6. 自定义标题栏 ``` 思路:使用 WindowChrome 实现自定义标题栏 - WindowChrome.IsHitTestVisibleInChrome="True":允许标题栏交互 - 手动实现窗口控制按钮 - 按钮颜色跟随主题变化 ``` **XAML 结构**: ```xml