?WPF 實現更換主題色
WPF 使用 WPFDevelopers.Minimal 如何更換主題色
作者:WPFDevelopersOrg
原文鏈接: ? ?https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal
框架使用大于等于
.NET40
;Visual Studio 2022
;項目使用 MIT 開源許可協議;
Nuget
Install-Package WPFDevelopers.Minimal
3.2.6-preview定義一個公共顏色資源文件,所有控件都動態資源引用DynamicResource顏色的Key,然后這樣修改一個資源文件的Key ?SolidColorBrush的Color顏色值就可以實現更換主題色;
此篇主要是定義了多個公共顏色資源文件如下,選擇不同的資源文件進行更換App.xaml的資源字典實現更換主題色;
在Application.Current.Resources.MergedDictionaries.Remove(Blue) 字典尋找Blue,然后
移除
在Application.Current.Resources.MergedDictionaries.Add(Green); 就完成更換主題色;創建 Light.Blue;
創建 Light.Green;
創建 Light.Orange;
創建 Light.Pink;
創建 Light.Purple;
創建 Light.Purple;
使用 WPFDevelopers.Minimal 如何更換主題色Nuget包大于等于3.2.3如下;
新增主題色如下;
新建資源文件Light.Carmine.xaml如下,自己更換顏色值即可;
<ResourceDictionary?xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"?po:Freeze="True"><!--Default顏色--><Color?x:Key="DefaultBorderBrushColor"?po:Freeze="True">#CD7474</Color><SolidColorBrush?x:Key="DefaultBorderBrushSolidColorBrush"?Color="{StaticResource?DefaultBorderBrushColor}"?po:Freeze="True"></SolidColorBrush><Color?x:Key="DefaultBackgroundColor"?po:Freeze="True">#CFA0A0</Color><SolidColorBrush?x:Key="DefaultBackgroundSolidColorBrush"?Color="{StaticResource?DefaultBackgroundColor}"?po:Freeze="True"></SolidColorBrush><Color?x:Key="DefaultBackgroundPressedColor"?po:Freeze="True">#B70404</Color><SolidColorBrush?x:Key="DefaultBackgroundPressedSolidColorBrush"?Color="{StaticResource?DefaultBackgroundPressedColor}"?po:Freeze="True"></SolidColorBrush><!--Primary顏色--><Color?x:Key="PrimaryNormalColor"?po:Freeze="True">#B31B1B</Color><SolidColorBrush?x:Key="PrimaryNormalSolidColorBrush"?Color="{StaticResource?PrimaryNormalColor}"?po:Freeze="True"></SolidColorBrush><SolidColorBrush?x:Key="WindowBorderBrushSolidColorBrush"?Color="{StaticResource?PrimaryNormalColor}"?po:Freeze="True"></SolidColorBrush><Color?x:Key="PrimaryMouseOverColor"?po:Freeze="True">#BB5F5F</Color><SolidColorBrush?x:Key="PrimaryMouseOverSolidColorBrush"?Color="{StaticResource?PrimaryMouseOverColor}"?po:Freeze="True"></SolidColorBrush><Color?x:Key="PrimaryPressedColor"?po:Freeze="True">#B70404</Color><SolidColorBrush?x:Key="PrimaryPressedSolidColorBrush"?Color="{StaticResource?PrimaryPressedColor}"?po:Freeze="True"></SolidColorBrush></ResourceDictionary>
1)把上面新增的資源添加App.xaml中即可;
<Application?x:Class="WPFDevelopers.Minimal.Sample.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:ws="https://github.com/WPFDevelopersOrg.WPFDevelopers.Minimal"StartupUri="ExampleViews\MainView.xaml"?ShutdownMode="OnMainWindowClose"><Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary?Source="pack://application:,,,/WPFDevelopers.Minimal.Sample.Net40;component/Light.Carmine.xaml"/><!--需要注意ws:Resources?必須再配色主題后,Theme="Dark"?為黑色皮膚?--><ws:Resources?Theme="Light"/><ResourceDictionary?Source="pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Theme.xaml"/></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources>
</Application>
2)或者把資源添加到ThemesCollection集合中;
if?(ThemesCollection?!=?null)ThemesCollection.Add(new?ThemeModel{Color?=?"#B31B1B",ResourcePath?="pack://application:,,,/WPFDevelopers.Minimal.Sample.Net40;component/Light.Carmine.xaml"});
重啟應用效果如下;


1)ThemeControl.cs代碼如下;
using?System;
using?System.Collections.Generic;
using?System.Collections.ObjectModel;
using?System.Collections.Specialized;
using?System.ComponentModel;
using?System.Linq;
using?System.Windows;
using?System.Windows.Controls;
using?WPFDevelopers.Minimal.Helpers;
using?WPFDevelopers.Minimal.Models;namespace?WPFDevelopers.Minimal.Controls
{public?class?ThemeControl?:?Control{public?static?readonly?DependencyProperty?ItemsSourceProperty?=DependencyProperty.Register("ItemsSource",?typeof(ObservableCollection<ThemeModel>),?typeof(ThemeControl),new?PropertyMetadata(null));static?ThemeControl(){DefaultStyleKeyProperty.OverrideMetadata(typeof(ThemeControl),new?FrameworkPropertyMetadata(typeof(ThemeControl)));}public?ObservableCollection<ThemeModel>?ItemsSource{get?=>?(ObservableCollection<ThemeModel>)GetValue(ItemsSourceProperty);set?=>?SetValue(ItemsSourceProperty,?value);}public?override?void?OnApplyTemplate(){base.OnApplyTemplate();ItemsSource?=?new?ObservableCollection<ThemeModel>();ItemsSource.Add(new?ThemeModel{Color?=?"#409EFF",ResourcePath?=?"pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Blue.xaml"});ItemsSource.Add(new?ThemeModel{Color?=?"#FF033E",ResourcePath?=?"pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Red.xaml"});ItemsSource.Add(new?ThemeModel{Color?=?"#A21BFC",ResourcePath?=?"pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Purple.xaml"});ItemsSource.Add(new?ThemeModel{Color?=?"#FE9426",ResourcePath?=?"pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Orange.xaml"});ItemsSource.Add(new?ThemeModel{Color?=?"#00B050",ResourcePath?=?"pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Green.xaml"});ItemsSource.Add(new?ThemeModel{Color?=?"#FF007F",ResourcePath?=?"pack://application:,,,/WPFDevelopers.Minimal;component/Themes/Light.Pink.xaml"});if?(ThemeCache.ThemesDictCache.Count?>?0)foreach?(var?item?in?ThemeCache.ThemesDictCache)if?(ItemsSource.Any(x?=>?x.Color?!=?item.Key))ItemsSource.Add(item.Value);SelectChecked();ItemsSource.CollectionChanged?+=?ItemsSource_CollectionChanged;foreach?(var?theme?in?ItemsSource)theme.PropertyChanged?+=?Theme_PropertyChanged;}private?void?SelectChecked(){var?existsTheme?=?ItemsSource.FirstOrDefault(y?=>Application.Current.Resources.MergedDictionaries.ToList().Exists(j?=>j.Source?!=?null?&&?y.ResourcePath.Contains(j.Source.AbsoluteUri)));if?(existsTheme?!=?null)existsTheme.IsChecked?=?true;}private?void?ItemsSource_CollectionChanged(object?sender,?NotifyCollectionChangedEventArgs?e){switch?(e.Action){case?NotifyCollectionChangedAction.Add:foreach?(ThemeModel?item?in?e.NewItems){if?(!ThemeCache.ThemesDictCache.ContainsKey(item.Color))ThemeCache.ThemesDictCache.Add(item.Color,?item);item.PropertyChanged?+=?Theme_PropertyChanged;SelectChecked();if?(!item.IsChecked)?return;ReviseTheme(item);}break;case?NotifyCollectionChangedAction.Remove:foreach?(ThemeModel?item?in?e.NewItems)if?(ThemeCache.ThemesDictCache.ContainsKey(item.Color))ThemeCache.ThemesDictCache.Remove(item.Color);break;case?NotifyCollectionChangedAction.Replace:break;case?NotifyCollectionChangedAction.Move:break;case?NotifyCollectionChangedAction.Reset:break;}}private?void?Theme_PropertyChanged(object?sender,?PropertyChangedEventArgs?e){if?(e.PropertyName?==?nameof(ThemeModel.IsChecked)){var?theme?=?sender?as?ThemeModel;if?(!theme.IsChecked)?return;ReviseTheme(theme);}}private?void?ReviseTheme(ThemeModel?theme){if?(theme?==?null)?return;var?old?=?ItemsSource.FirstOrDefault(x?=>?x.IsChecked?&&?x.Color?!=?theme.Color);if?(old?!=?null){ItemsSource.Where(y?=>?!y.Color.Equals(theme.Color)?&&?y.IsChecked).ToList().ForEach(h?=>?h.IsChecked?=?false);var?existingResourceDictionary?=Application.Current.Resources.MergedDictionaries.FirstOrDefault(x?=>x.Source?!=?null?&&?x.Source.Equals(old.ResourcePath));if?(existingResourceDictionary?!=?null)Application.Current.Resources.MergedDictionaries.Remove(existingResourceDictionary);var?newResource?=Application.Current.Resources.MergedDictionaries.FirstOrDefault(x?=>x.Source?!=?null?&&?x.Source.Equals(theme.ResourcePath));if?(newResource?!=?null)?return;var?newResourceDictionary?=?new?ResourceDictionary?{?Source?=?new?Uri(theme.ResourcePath)?};Application.Current.Resources.MergedDictionaries.Insert(0,?newResourceDictionary);ControlHelper.ThemeRefresh();}}}public?class?ThemeCache{public?static?Dictionary<string,?ThemeModel>?ThemesDictCache?=?new?Dictionary<string,?ThemeModel>();}
}
2)Styles.ThemeControl.xaml代碼如下;
<ResourceDictionary?xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:sys="clr-namespace:System;assembly=mscorlib"xmlns:wpfsc="clr-namespace:WPFDevelopers.Minimal.Controls"xmlns:model="clr-namespace:WPFDevelopers.Minimal.Models"><ResourceDictionary.MergedDictionaries><ResourceDictionary?Source="../Themes/Basic/ControlBasic.xaml"/><ResourceDictionary?Source="../Themes/Basic/Animations.xaml"/></ResourceDictionary.MergedDictionaries><Style?TargetType="{x:Type?wpfsc:ThemeControl}"><Setter?Property="Template"><Setter.Value><ControlTemplate?TargetType="{x:Type?wpfsc:ThemeControl}"><ItemsControl?ItemsSource="{Binding?ItemsSource,RelativeSource={RelativeSource?AncestorType=wpfsc:ThemeControl}}"><ItemsControl.ItemTemplate><DataTemplate><RadioButton?Height="40"?Width="40"?Margin="4,0"Cursor="Hand"?IsChecked="{Binding?IsChecked}"><RadioButton.Style><Style?TargetType="{x:Type?RadioButton}"><Setter?Property="Template"><Setter.Value><ControlTemplate?TargetType="{x:Type?RadioButton}"><Border?x:Name="PART_Border"Padding="2"?BorderThickness="0"BorderBrush="{Binding?Color}"><Grid?Background="{x:Null}"><Rectangle?x:Name="PART_Rectangle"?Fill="{Binding?Color}"/><Path?Data="{StaticResource?PathCheckMark}"Stretch="Fill"?Fill="{DynamicResource?BackgroundSolidColorBrush}"VerticalAlignment="Bottom"HorizontalAlignment="Right"Height="10"?Width="12"Margin="0,0,4,4"Visibility="{Binding?IsChecked,Converter={StaticResource?bool2VisibilityConverter}}"/></Grid></Border><ControlTemplate.Triggers><Trigger?Property="IsMouseOver"?Value="True"><Setter?Property="Opacity"?Value=".8"?TargetName="PART_Rectangle"/><Setter?Property="BorderThickness"?Value="1"?TargetName="PART_Border"/></Trigger><Trigger?Property="IsChecked"?Value="True"><Setter?Property="BorderThickness"?Value="1"?TargetName="PART_Border"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter></Style></RadioButton.Style></RadioButton></DataTemplate></ItemsControl.ItemTemplate><ItemsControl.ItemsPanel><ItemsPanelTemplate><WrapPanel/></ItemsPanelTemplate></ItemsControl.ItemsPanel></ItemsControl></ControlTemplate></Setter.Value></Setter></Style>
</ResourceDictionary>
3)ThemeControlExample.xaml代碼如下;
<!--命名空間-->xmlns:ws="https://github.com/WPFDevelopersOrg.WPFDevelopers.Minimal"<TextBlock?Text="Theme"?FontSize="20"?Margin="0,20,0,0"/><ws:ThemeControl?Margin="0,10"?ItemsSource="{Binding?ThemesCollection,RelativeSource={RelativeSource?AncestorType=local:MainView},Mode=OneWayToSource}"/>
ThemeControl|Github[1]
ThemeControl|碼云[2]
Styles.ThemeControl.xaml|Github[3]
Styles.ThemeControl.xaml|碼云[4]
參考資料
[1]
ThemeControl|Github: https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal/blob/main/src/WPFDevelopers.Minimal/WPFDevelopers.Minimal.Shared/Controls/ThemeControl.cs
[2]ThemeControl|碼云: https://gitee.com/WPFDevelopersOrg/WPFDevelopers.Minimal/blob/main/src/WPFDevelopers.Minimal/WPFDevelopers.Minimal.Shared/Controls/ThemeControl.cs
[3]Styles.ThemeControl.xaml|Github: https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal/blob/main/src/WPFDevelopers.Minimal/WPFDevelopers.Minimal.Shared/Styles/Styles.ThemeControl.xaml
[4]Styles.ThemeControl.xaml|碼云: https://gitee.com/WPFDevelopersOrg/WPFDevelopers.Minimal/blob/main/src/WPFDevelopers.Minimal/WPFDevelopers.Minimal.Shared/Styles/Styles.ThemeControl.xaml