项目结构:
先来看看BlueSkin.xaml与YellowSkin.xaml。
BlueSkin.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SkinnedApplication"> <Style TargetType="{x:Type Button}"> <Setter Property="Background" Value="Blue" /> </Style> <Style TargetType="{x:Type local:ChildWindow}"> <Setter Property="Background" Value="Blue" /> </Style> <Style TargetType="{x:Type local:MainWindow}"> <Setter Property="Background" Value="Blue" /> </Style> </ResourceDictionary>
YellowSkin.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SkinnedApplication"> <Style TargetType="{x:Type Button}"> <Setter Property="Background" Value="Yellow" /> </Style> <Style TargetType="{x:Type local:ChildWindow}"> <Setter Property="Background" Value="Yellow" /> </Style> <Style TargetType="{x:Type local:MainWindow}"> <Setter Property="Background" Value="Yellow" /> </Style> </ResourceDictionary>
都是在ResourceDictionary中包含了一些Style。
资源字典(ResourceDictionary)介绍
现在来看看ResourceDictionary是什么?
在WPF中,资源字典(ResourceDictionary)
是一个非常重要的特性,它用于存储和管理可重用的资源,如样式、控件模板、颜色、字体等。通过使用ResourceDictionary,你可以在一个集中的位置定义这些资源,并在整个应用程序中重用它们,这有助于保持应用程序的一致性和减少代码的重复。
资源字典是应用使用的 XAML 资源(如样式)的存储库。 在 XAML 中定义资源,然后可以使用 {StaticResource} 标记扩展和 {ThemeResource} 标记扩展在 XAML 中检索它们。 还可以使用代码访问资源,但这并不常见。 你可以使用资源来强制在整个应用中一致地使用某些值,例如画笔颜色或像素度量。
样式(Style)介绍
知道了ResourceDictionary是什么,现在再来看看Style是什么?
在WPF中,样式(Style)
是一种定义一组属性值的方式,这些属性值可以应用于一个或多个UI元素,以此来改变它们的外观和行为。样式类似于CSS(层叠样式表)中的样式,它允许开发者在一个集中的位置定义UI元素的外观,然后在整个应用程序中重用这些定义。
Style类在类型的不同实例之间共享属性、资源和事件处理程序。
可以这样子使用:
<Style x:Key="Style1"> <Setter Property="Control.Background" Value="Yellow"/> </Style>
应用它,只需要这样子写:
<Label Content="Yellow Background" Style="{StaticResource Style1}" />
还可以使用 TargetType 属性将样式应用于给定类型的所有元素。 将目标类型添加到样式意味着不再需要使用语法完全限定所设置 ClassName.PropertyName
的属性。 以下示例定义将应用于每个 TextBlock 元素的样式。
<Style TargetType="{x:Type TextBlock}"> <Setter Property="FontFamily" Value="Segoe Black" /> <Setter Property="HorizontalAlignment" Value="Center" /> <Setter Property="FontSize" Value="12pt" /> <Setter Property="Foreground" Value="#777777" /> </Style>
以上的BlueSkin.xaml与YellowSkin.xaml就是使用这种用法。
将资源字典加载到应用程序属性字典
App.xaml:
<Application x:Class="SkinnedApplication.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SkinnedApplication" StartupUri="MainWindow.xaml" Startup="App_Startup" />
其中
Startup="App_Startup"
Startup
是Application类的一个事件,当应用程序启动并准备好运行时触发。这个事件在应用程序的主窗口显示之前发生,因此它是执行初始化操作(如加载资源、配置或数据)的理想位置。
App_Startup
是这个事件的处理程序,在App.cs中定义:
private void App_Startup(object sender, StartupEventArgs e) { Properties["Blue"] = (ResourceDictionary) LoadComponent(new Uri("BlueSkin.xaml", UriKind.Relative)); Properties["Yellow"] = (ResourceDictionary) LoadComponent(new Uri("YellowSkin.xaml", UriKind.Relative)); }
这段代码位其目的是在应用程序启动时加载两个资源字典,分别代表两种不同的皮肤或主题,然后将这些资源字典存储在应用程序的属性字典中以供后续使用。
Properties
是Application类的一个属性,它提供了一个键值对的集合,可以用来存储应用程序范围内的数据。
LoadComponent
方法是WPF中用于加载XAML文件并创建相应对象的方法。
在这里我们又遇到了一个新的概念:属性字典(Properties Dictionary)。
属性字典(Properties Dictionary)介绍
在WPF应用程序中,属性字典(Properties Dictionary)是Application类提供的一个功能,允许开发者在应用程序范围内存储和检索键值对数据。这个属性字典可以用来保存应用程序的配置信息、状态数据或者任何需要在不同组件或页面间共享的数据。
属性字典的主要特点包括:
- 应用程序范围:存储在属性字典中的数据在整个应用程序的生命周期内都是可用的,这意味着不同的窗口、页面或控件都可以访问和修改这些数据。
- 键值对存储:属性字典以键值对的形式存储数据,其中键是一个唯一的字符串,值可以是任何类型的对象。
- 动态类型:由于属性字典中的值是以object类型存储的,因此在检索值时通常需要进行类型转换。
属性字典非常适合用来存储那些需要在应用程序的多个部分之间共享的数据,但是它不适合用来存储大量的数据或者需要频繁更新的数据,因为这可能会影响应用程序的性能。
切换样式的实现
MainWindow.xaml如下:
<Window x:Class="SkinnedApplication.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:SkinnedApplication" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <StackPanel> <Button Name="newChildWindowButton" Click="newChildWindowButton_Click">New ChildWindow</Button> <StackPanel Orientation="Horizontal"> <Label>Skin:</Label> <ComboBox Name="skinComboBox" /> </StackPanel> </StackPanel> </Window>
MainWindow.cs如下:
using System.Windows; using System.Windows.Controls; namespace SkinnedApplication { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // Add choices to combo box skinComboBox.Items.Add("Blue"); skinComboBox.Items.Add("Yellow"); skinComboBox.SelectedIndex = 0; // Set initial skin Application.Current.Resources = (ResourceDictionary) Application.Current.Properties["Blue"]; // Detect when skin changes skinComboBox.SelectionChanged += skinComboBox_SelectionChanged; } private void newChildWindowButton_Click(object sender, RoutedEventArgs e) { // Create a new skind child window var window = new ChildWindow(); window.Show(); } private void skinComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { // Change the skin var selectedValue = (string) e.AddedItems[0]; Application.Current.Resources = (ResourceDictionary) Application.Current.Properties[selectedValue]; } } }
Application.Current.Resources = (ResourceDictionary) Application.Current.Properties["Blue"];
初始化一个主题或者皮肤。
Application.Current.Resources = (ResourceDictionary) Application.Current.Properties[selectedValue];
切换样式。
最终实现的效果如下所示:
切换到:
总结
通过这个例子我们简单了解了WPF中的资源字典(ResourceDictionary)、样式(Style)、属性字典(Properties Dictionary)以及一种实现WPF应用更换主题的思路,希望对你有所帮助。
参考
1、ResourceDictionary 类 (Windows.UI.Xaml) - Windows UWP applications | Microsoft Learn
2、Style 类 (System.Windows) | Microsoft Learn
3、Application.Properties 属性 (System.Windows) | Microsoft Learn
代码来源
[WPF-Samples/Application Management/SkinnedApplication at main · microsoft/WPF-Samples (github.com)]