其他
.NET MAUI 应用亮/暗模式与运行时主题切换:用…
在 .NET MAUI 应用里加亮/暗模式、自定义品牌主题和运行时切换主题(含系统主题检测与用户偏好持久化)时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 48/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 54/100 ×0.15
适用场景
.NET MAUI 应用亮/暗模式与运行通用
解决思路
- 先检测项目现有的主题方案(AppThemeBinding、ResourceDictionary 或完全没有),再据此选策略:只需跟随系统的亮/暗用 AppThemeBinding;需要自定义品牌主题、超过两套主题或用户可选主题用 ResourceDictionary 交换;两者都要则组合使用。
- 在 App.xaml 的 Application.Resources 里集中定义原始调色板(如 LightPageBackground / DarkPageBackground / LightPrimaryText / DarkPrimaryText 等 Color 键),再用隐式 Style(如 TargetType="ContentPage" ApplyToDerivedTypes="True"、TargetType="Label")以 AppThemeBinding 把亮暗两色绑定一次,让所有页面自动继承,页面本身不写任何主题标记。
- 只在真正的一次性场景使用内联 AppThemeBinding(如 TextColor="{AppThemeBinding Light=Green, Dark=Red}"),并且优先引用 {StaticResource} 键而不是字面量十六进制值。
- 在 C# 里设置主题颜色时,同时给出 SetAppThemeColor(property, light, dark)(针对 Color 属性)和泛型 SetAppTheme<T>(property, light, dark)(任何可绑定属性,如 FontSize、ImageSource);注意 BindableProperty 必须属于所调用的对象(Image.SourceProperty 用在 Image 上,不能用在 Label 上)。
- 用 ResourceDictionary 做自定义主题时:每套主题建一个 XAML 字典文件,键名完全一致、只有值不同;若字典使用 x:Class 编译 XAML,需配套 code-behind 调用 InitializeComponent(),而通过 Source 加载且无 x:Class 的字典不需要 code-behind。
- 消费主题资源时一律用 {DynamicResource Key},不要用 {StaticResource Key},因为 StaticResource 只在加载时解析一次,字典交换后不会更新。
- 运行时切换主题:用一个 _currentTheme 字段记录当前主题,交换时只 MergedDictionaries.Remove(_currentTheme) 然后 merged.Add(newTheme);绝不能调用 MergedDictionaries.Clear(),否则会连带删掉模板自带的 Colors.xaml / Styles.xaml,所有隐式样式、画刷和颜色静默失效。
- 读取当前主题:Application.Current.RequestedTheme(返回 AppTheme.Light、AppTheme.Dark 或 AppTheme.Unspecified)。
- 强制或恢复主题:Application.Current.UserAppTheme = AppTheme.Dark 覆盖系统设置;设回 AppTheme.Unspecified 重新跟随系统,不要手动逐个重设颜色。
- 订阅 Application.Current.RequestedThemeChanged += (s, e) => ...,用 e.RequestedTheme 更新 UI 或交换 ResourceDictionary。
- 组合两种方案:用 {AppThemeBinding Light={DynamicResource LightPrimary}, Dark={DynamicResource DarkPrimary}} 让嵌套的 DynamicResource 保持活跃;或在 RequestedThemeChanged 里按 e.RequestedTheme == AppTheme.Dark 调用 ApplyTheme 切换整套字典。
- 用户偏好持久化:用 Preferences.Set("AppTheme", "Dark") 保存选择;在启动时(App 构造函数或 CreateWindow)用 Preferences.Get("AppTheme", "System") 读取,并 switch 映射到 AppTheme.Light / AppTheme.Dark / AppTheme.Unspecified 赋给 Application.Current.UserAppTheme。
- Android 必须在 MainActivity 的 [Activity] 特性里把 ConfigChanges.UiMode 加入 ConfigurationChanges 列表,并与 RequestedThemeChanged 的处理逻辑配套;否则切换系统深色模式会重启 Activity、丢失导航状态并看起来像崩溃。
- 排查并消除硬编码颜色:把 <Label TextColor="#333333" /> 之类改成 {DynamicResource PrimaryTextColor};诊断 DynamicResource 不刷新时,要同时给出字典交换代码和系统主题钩子,否则绑定修对了也不会更新。
- 不要在用户不需要另一种方案能力(超过两套主题、用户可选主题)时,把已经可用的 AppThemeBinding 方案换成 ResourceDictionary 交换,或反之;UiMode 和字典交换只对运行时切换有意义,静态 AppThemeBinding 场景不要牵扯。
- 注意 CSS 样式主题无法在运行时交换,运行时切换必须用 ResourceDictionary;并确保每个 x:Key 在所有主题字典中都存在,缺键会静默回退默认值导致外观不一致。
- 在至少一个目标平台上实测亮/暗两套主题,确认所有 UI 元素正确响应;平台最低版本为 iOS 13+、Android 10+(API 29)、macOS Catalyst 10.15+、Windows 10+。
适用边界 · 注意事项
- 本地化或语言切换——参见 .NET MAUI localization 文档。
- 无障碍相关的视觉调整,以及应用图标/启动画面配置——参见 .NET MAUI accessibility 与 app icons 文档。
- Bootstrap 风格的 class 主题——改用 Plugin.Maui.BootstrapTheme NuGet 包。