- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
🍟 程序的本质 - 数据结构 + 算法 🍟 。
🥑本篇为学习李应保老师所著的《WPF专业编程指南》并搭配 WPF 开发圣经《WPF编程宝典第4版》以及痕迹大佬《WPF入门基础教程系列》文章所作笔记,对应《WPF专业编程指南》第 9 章之间内容,主要概述 WPF 中关于样式的相关内容,希望可以帮到大家💖 。
从按钮、文本框到下拉框、列表框, WPF 提供了一系列常用控件,每个控件都有自己独特的特性和用途。通过灵活的布局容器,如网格、堆栈面板和换行面板,我们可以将这些控件组合在一起,实现复杂的界面布局。而通过样式和模板,我们可以轻松地定制控件的外观和行为,以符合我们的设计需求.
在 WPF 中 UI (User Interface)是通过数据来驱动的,数据是核心, UI 从属于数据并表达数据,这和传统的windows图形界面开发(比如``Winform )有很大的区别。 WPF 中能够展示数据、响应用户操作的 UI`元素称为 控件 (Control),同时控件也是数据和行为的载体, 它们被设计成总是无外观的(lookless) 。控件中展现的内容称之为“ 数据内容 ”,响应用户操作后执行的方法(Method)或事件(Event)称之为“行为”.
WPF 革命性的概念就是把 UI 元素(控件)的特性和显示方式分开。控件在用户界面上的样子是由控件模板决定的, WPF 为每个控件提供了默认的控件模板和相应的特性,这些默认模板提供了初始情况下控件的默认外观。同时,用户也可以用自己的模板来替换 WPF 默认提供的模板,每个控件都可以成为开发者自己的个性化控件.
在 WPF 中,有两个类似的类继承树:一个与界面(UI)相关,一个与内容(Content)相关,这种界面与内容分离设计使得 WPF 能够更好地处理 UI 元素和内容元素的不同需求.
UI 元素的类继承树以 UIElement 为基础,它是所有可视化 UI 元素的基类,可以理解为控件.
UIElement 提供了处理输入事件、布局、渲染等 UI 相关功能的基本支持。从 UIElement 派生出了 FrameworkElement ,它进一步扩展了 UI 元素的功能,包括数据绑定、样式、模板等。 Control 类则是 FrameworkElement 中比较重要的子类,它提供了一些常见控件的默认外观和行为.
内容元素的类继承树以 ContentElement 为基础,它用于处理 内容相关 的功能,例如文本内容的显示和处理.
与之相对应的是 FrameworkContentElement ,它从 ContentElement 派生出来,提供了更多的内容相关功能.
需要注意的是, ContentElement 与内容控件(Content Controls)是不一样的: ContentElement 主要用于处理文本内容,而内容控件则是一种控件,用于展示和管理单个内容元素.
控件图示,比着痕迹g的图画的:
回到文章的标题上来, WPF 中的样式(Style)是一种用于定义控件外观和行为的重要机制。它允许开发人员将属性设置封装到可重用的命名容器中,以便在应用程序中多次应用。在概念上 WPF 的样式和 HTML 中的 CSS 类似,但由于 WPF 中依赖属性的强大功能,使得 WPF 可以通过样式完成 CSS 所不具备的功能.
WPF 中的模板(Template)则是一种更高级别的样式,它可以完全重写控件的外观和行为,包括其布局和交互。通过使用模板,开发人员可以创建自定义控件,这些控件可能与基本控件有完全不同的外观和交互模型.
在 WPF 中,"相关属性(Related Properties)"是指与控件或元素的属性之间存在一定关联或依赖关系的属性。这些属性的值通常会相互影响,当一个属性的值发生变化时,其他相关属性的值也可能会跟着改变.
常见的相关属性包括:
Width
和 Height
:这两个属性定义了控件或元素的宽度和高度。它们通常是相互关联的,当其中一个属性的值发生变化时,另一个属性的值也可能会受到影响。 Margin
和 Padding
: Margin
属性定义了控件或元素与其容器之间的空白区域,而 Padding
属性定义了控件或元素内部 内容 与其边界之间的空白区域。它们的值也可能会相互影响。 IsEnabled
和 Opacity
: IsEnabled
属性用于指示控件或元素是否处于启用状态,而 Opacity
属性用于定义控件或元素的不透明度。当 IsEnabled
属性的值为 False
时,通常会将控件或元素的 Opacity
属性设置为较低的值,以表示禁用状态。 IsChecked
、 IsSelected
和 Visibility
:这些属性常用于复选框、单选按钮、列表框等控件中。它们表示控件或元素的选中状态或可见性。当其中一个属性的值发生变化时,可能会触发其他相关属性的变化。 这些是一些常见的相关属性,具体的相关属性取决于控件或元素的类型和功能。了解这些相关属性之间的关系,可以帮助我们更好地使用和控制控件或元素的行为和外观.
样式(Style) ,负责控制控件元素的外观以及行为,是可用于元素的属性值集合,可以把样式(Style)看成一种将一组属性值应用到多个元素的便捷方法, 使用资源的最常见原因之一就是保存样式 ,有点类似与 Web 中的 css 文件,但是 WPF 中的样式 Style 还支持触发器(Trigger),比如当元素属性发生变化时,可通过触发器改变控件样式,这使得 WPF 可以通过样式可以完成 CSS 所不具备的功能.
简单来说,样式就是控件的外观、风格,在 WPF 中,样式就像是控件身上的衣服.
样式可以应用于单个控件或整个应用程序范围内的所有控件。通过定义样式,可以设置控件的属性、视觉效果、动画、模板等内容,从而改变控件的外观和行为。此外,样式是组织和重用格式化选项的重要工具,我们都知道,重复的代码就是问题,不提倡在 xaml 中使用重复的属性填充控件,而应该创建一系列封装了这些细节要点的样式,在需要的控件上应用该样式.
WPF 中每个控件都有 Style 属性用来设定样式:
转到 Style 定义可以知道,样式类 Style 位于 System.Windows 命名空间下:
在 Style 类里面,定义了几个重要的属性:
TargetType :设置样式所针对的控件类型,设置该属性后,在 XAML 中设置 Setters 或 Triggers 中元素的 Property 属性时,可以不用添加类作用域限定(这个后面部分会提到) 。
Setters :属性设置器 SetterBase 对象集合 - SetterBase 类主要用于控制控件的静态外观风格 。
Triggers :条件触发器 TriggerBase 对象集合 - TriggerBase 类主要用于控制控件的动态行为风格 。
BaseOn :在已有样式的基础上继承另一个样式 。
Resources :资源字典 ResourceDictionary 对象集合 。
IsSealed :是否允许“派生”出其他样式 。
接下来我们来具体看看每个属性都是用来做什么的以及如何使用这些属性,近距离感受样式的神奇力量.
Setter (设置器)是 Style 类中的一个重要属性,其类型是 SetterBaseCollection ,一个可以放入 SetterBase 类型对象的容器,在 Style 中 Setter 属性用于设置目标对象的属性值。 Setter 通常用于定义样式中的属性设置,以统一控件的外观和行为.
Setter 具有两个 主要 属性:
Property
(属性):指定要设置的属性名称。可以是任何依赖属性(DependencyProperty)或依赖对象(DependencyObject)的属性[超前警告⚠]。 Value
(值):指定要为属性设置的值。 Setter 的作用是在样式中定义属性设置规则,使得适用于该样式的目标对象会继承这些属性设置。当样式应用于目标对象时, Setter 将设置指定属性的值为所定义的值。在实际应用中,我们很少对某一个控件使用样式,使用样式的目的是:当改变某个样式时,希望所有使用该样式的控件都会改变它们的表现形式,从而不必对某控件逐一进行修改.
例如,可以使用 Setter 在样式中设置 Button 控件的背景颜色、字体大小、边距等属性。当应用该样式于 Button 控件时,这些属性将自动应用,并使得所有的 Button 控件具有相同的外观和行为.
Setter 可以在样式的 <Style> 标签中使用多个,以定义多个属性的设置。这样可以一次性为目标对象设置多个属性,提高代码的可读性和可维护性.
🆗具体案例:比如现在有三个外观风格一模一样的红绿配色按钮:
<Window x:Class="HELLOWPF.ControlWindow"
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:HELLOWPF"
mc:Ignorable="d"
Title="ControlWindow" Height="450" Width="800">
<Grid>
<StackPanel>
<Button Background="Green" Margin="5" FontSize="16" Content="Bite Me!" Foreground="Red"/>
<Button Background="Green" Margin="5" FontSize="16" Content="Bite Me!" Foreground="Red"/>
<Button Background="Green" Margin="5" FontSize="16" Content="Bite Me!" Foreground="Red"/>
</StackPanel>
</Grid>
</Window>
有需求需要三个按钮的风格必须是一样的,如果我们像上面 xaml 里的方式控制这三个按钮的样子的话,在需要修改的时候就会很痛苦,需要挨个调整属性值,通过样式就可以很好的解决这个问题,我们可以把三个按钮预先定义在样式中:
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="Green" />
<Setter Property="Margin" Value="5"/>
<Setter Property="FontSize" Value="16" />
<Setter Property="Content" Value="Bite Me!"/>
<Setter Property="Foreground" Value="Red"/>
</Style>
</Window.Resources>
然后把三个 Button 里面的属性都删掉,可以发现 Button 的样子和之前一个一个属性设置是一样的,但是这种方式相较于之前就显得灵活了很多,当需要调整的时候我们只需要调整样式中的对应属性,就可以让三个按钮跟着变化了:
<Window x:Class="HELLOWPF.ControlWindow"
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:HELLOWPF"
mc:Ignorable="d"
Title="ControlWindow" Height="450" Width="800">
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="Green" />
<Setter Property="Margin" Value="5"/>
<Setter Property="FontSize" Value="16" />
<Setter Property="Content" Value="Bite Me!"/>
<Setter Property="Foreground" Value="Red"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Button />
<Button />
<Button />
</StackPanel>
</Grid>
</Window>
🔜🔜 Setter 元素里只能指定一组属性值,可以通过多个 Setter 来设置多个属性值 。
TargetType 是 Style 类中的一个属性用来说明所定义的样式要施加的对象 🔙🔙 。
在上述样式中,使用了多个 Setter 元素来设置按钮的属性:
<Window.Resources>
表示在窗口的资源部分开始定义资源,其中包含样式。 <Style TargetType="Button">
表示定义一个针对Button控件的样式。 <Setter Property="Background" Value="Green" />
设置Button的背景颜色为绿色。 <Setter Property="Margin" Value="5"/>
设置Button的边距为5个单位。 <Setter Property="FontSize" Value="16" />
设置Button的字体大小为16。 <Setter Property="Content" Value="Bite Me!"/>
设置Button的内容为"Bite Me!"。 <Setter Property="Foreground" Value="Red"/>
设置Button的前景色(文本颜色)为红色。 为什么要在 Resources 中定义样式呢?显然我们不能只在某个特定的控件中使用样式,当然这在 WPF 中也是可行的:
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Green" />
<Setter Property="Margin" Value="5"/>
<Setter Property="FontSize" Value="16" />
<Setter Property="Content" Value="Bite Me!"/>
<Setter Property="Foreground" Value="Red"/>
</Style>
</Button.Style>
</Button>
但是这种定义方式和一开始我们分别定义每个按钮外观的方法如出一辙,显然失去了使用样式的意义。把样式定义在 Window.Resources 中,它将适用于当前 Window(窗体) 中所有的 Button 控件。这意味着所有的按钮都会有绿色的背景、5个单位的边距、字体大小为16、显示文本为"Bite Me!"以及红色的前景颜色。这个时候通过修改样式中的相应属性的 Value 就可以直接修改 Button 的样式了,不需要为每个 Button 控件都单独设置这些属性,可以大大简化界面设计和维护工作.
❗❗❗表转折:就像上面说的,现在定义的样式效果意味着所有的按钮都会有绿色的背景、5个单位的边距、字体大小为16、显示文本为"Bite Me!"以及红色的前景颜色,那如果有一个按钮不需要设定成这种样式或者它是别的样子怎么办呢?别急,有三种解决方法:
Button
重新都设置需要的属性覆盖掉所设置的样式(属性的优先级),这样显然是有悖于我们使用样式的初衷的 {x:Null}
显式清空 Style
x:key
,当需要的时候通过这个名字来找到它 {StaticResource keyValue}
,这在为同一控件定义不同的样式时,非常方便。比如我们可以创建两种不同风格的 Button
:
<Window x:Class="HELLOWPF.ControlWindow"
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:HELLOWPF"
mc:Ignorable="d"
Title="ControlWindow" Height="450" Width="800">
<Window.Resources>
<Style x:Key="ButtonStyle1" TargetType="Button">
<Setter Property="Background" Value="Green" />
<Setter Property="Margin" Value="5"/>
<Setter Property="FontSize" Value="16" />
<Setter Property="Content" Value="Bite Me!"/>
<Setter Property="Foreground" Value="Red"/>
</Style>
<Style x:Key="ButtonStyle2" TargetType="Button">
<Setter Property="Background" Value="LightGreen" />
<Setter Property="Content" Value="Dude!"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Button Style="{StaticResource ButtonStyle1}" />
<Button Style="{StaticResource ButtonStyle2}" />
<Button Style="{x:Null}" Content="No Way!"/>
</StackPanel>
</Grid>
</Window>
以上内容就是 Setter 的用法以及样式的一般定义和使用方法.
BaseOn 也是样式中几个重要属性之一,用于指定当前样式基于哪个已存在的样式进行继承和扩展。通过设置 BaseOn 属性,可以创建一个新的样式,并在现有样式的基础上进行修改或添加新的设置。通过 BaseOn 属性的巧妙运用,我们能够建立起一座座视觉上的宫殿,让用户陶醉其中。无论是继承经典、扩展创新,还是重塑风貌BaseOn`属性都是我们的得力助手.
基本语法如下所示:
<Style x:Key="NewStyle" TargetType="Button" BasedOn="{StaticResource ExistingStyle}">
<!-- 新样式的设置 -->
</Style>
声明的 NewStyle 样式会继承 ExistingStyle 中已定义的所有设置,然后可以在 NewStyle 中添加、修改或 覆盖 需要的属性设置。使用 BasedOn 属性可以提高样式的重用性,通过基于现有样式创建新的样式,可以在整个应用程序中一致地应用样式,并在需要时进行统一的更改.
比如上面例子中的样式 ButtonStyle1 ,我们需要一个在此基础上显示内容为斜体加粗的按钮,创建一个全新的样式当然没问题,但是设置背景色、调整间距、字体大小、字体颜色又得写一遍,用最多的时间创造最低的价值🙋,这个时候就可以通过继承 ButtonStyle1 样式,加上额外的样式:
<Window x:Class="HELLOWPF.ControlWindow"
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:HELLOWPF"
mc:Ignorable="d"
Title="ControlWindow" Height="450" Width="800">
<Window.Resources>
<Style x:Key="ButtonStyle1" TargetType="Button">
<Setter Property="Background" Value="Green" />
<Setter Property="Margin" Value="5"/>
<Setter Property="FontSize" Value="16" />
<Setter Property="Content" Value="Bite Me!"/>
<Setter Property="Foreground" Value="Red"/>
</Style>
<Style x:Key="ButtonStyle2" TargetType="Button">
<Setter Property="Background" Value="LightGreen" />
<Setter Property="Content" Value="Dude!"/>
</Style>
<Style x:Key="ButtonStyle3" TargetType="Button" BasedOn="{StaticResource ButtonStyle1}">
<Setter Property="FontStyle" Value="Italic" />
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Button Style="{StaticResource ButtonStyle1}" />
<Button Style="{StaticResource ButtonStyle2}" />
<Button Style="{StaticResource ButtonStyle3}" Content="No Way!"/>
</StackPanel>
</Grid>
</Window>
红配绿,冒傻气 。
尽管乍一看通过 BaseOn 进行样式继承看起来非常方便,但它也存在一些缺点需要注意:
BasedOn
属性继承样式时,子样式会紧密地依赖于父样式。这意味着如果父样式发生了变化,子样式可能也会受到影响,导致意外的样式改变。这种紧密的样式耦合可能会增加代码维护的复杂性。 BasedOn
属性继承样式时,子样式可能会继承了一些不必要的属性或样式,导致样式冗余。这可能会增加界面的渲染时间和内存消耗,对性能产生一定的影响。同时,样式继承层级的增加也可能会导致样式的解析和应用变慢。 样式继承所产生的依赖性会使程序变得更脆弱,上面演示的实例倒还好说,但是,通常,根据不同的内容类型以及内容所扮演的角色会出现各类的样式,通过样式继承之后往往会得到一个更复杂的模型,并且真正重复使用的样式设置少之又少。 除非有特殊原因要求一个样式继承自另一个样式(比如,第二个样式是第一个样式的特例,并且只改变了继承来的大量设置中的几个特征),否则不建议使用样式继承.
触发器(Triggers)用于在特定条件满足时改变控件的外观或行为。它们是一种强大的工具,用于响应用户交互、数据变化或其他事件,从而实现动态的控件效果.
触发器可以在控件模板的 Style 或 ControlTemplate 中定义。它们基于属性的值来触发特定的动作或设置.
WPF中有几种类型的触发器,包括:
Trigger
:用于在属性值满足特定条件时触发动作或设置。例如,当按钮被点击时改变其背景色。 MultiTrigger
:与 Trigger
类似,但可以同时满足多个属性的条件。 DataTrigger
:根据数据绑定的值触发动作或设置。例如,当绑定的数据达到某个特定值时隐藏控件。 MultiDataTrigger
:与 DataTrigger
类似,但可以同时满足多个数据绑定的条件。 EventTrigger
:在特定事件发生时触发动作或设置。例如,当鼠标移入控件时改变其透明度。 这几种类型的触发器都是从 TriggerBase 类中派生出来的。 DataTrigger 和 MultiDataTrigger 是一对数据触发器,两者的区别是在 DataTrigger 中只能说明一个条件,而 MultiDataTrigger 中则可以说明多个条件。 Trigger 和 MultiTrigger 也是一对触发器,和 DataTrigger 相似, Trigger 中只能说明一个条件,而 MultiTrigger 里可以说明多个条件。 DataTrigger 和 Trigger 的不同在于, DataTrigger 中带有 Banding 属性,即 DataTrigger 支持数据绑定.
触发器通常与 Setter 一起使用,以在触发时改变控件的属性。我们可以在触发器中设置新的属性值,也可以应用动画效果或其他更复杂的操作.
浅浅尝试一下触发器:
最简单,也是最基础的触发器,我们用一个小例子来演示一下,也作为样式的一次小复习,比如实现一个鼠标移过文本字体变大的效果:
<Window
x:Class="HELLOWPF.ControlWindow"
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:local="clr-namespace:HELLOWPF"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="ControlWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.Resources>
<Style x:Key="smallTrigger" TargetType="TextBlock">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="5"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="FontSize" Value="18"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<TextBlock Text="有情武术届的麦克阿瑟 - 黑虎阿福:"/>
<TextBlock Style="{StaticResource smallTrigger}">大象踢腿</TextBlock>
<TextBlock Style="{StaticResource smallTrigger}">狮子拜天</TextBlock>
<TextBlock Style="{StaticResource smallTrigger}">二龙戏珠</TextBlock>
<TextBlock Style="{StaticResource smallTrigger}">龙卷风摧毁停车场</TextBlock>
<TextBlock Style="{StaticResource smallTrigger}">乌鸦坐飞机</TextBlock>
<TextBlock Style="{StaticResource smallTrigger}">佛朗明哥舞步</TextBlock>
</StackPanel>
</Grid>
</Window>
注意在使用触发器时要避免死循环,不要将触发器中设定的相关属性作为触发器的条件,即:改变相关属性A引起相关属性B发生改变,而相关属性B改变又引发相关属性A改变的情况.
实现一个输入效果,当输入的时候,边框变厚以提醒输入状态,同时背景颜色也发生变化:
<Window
x:Class="HELLOWPF.ControlWindow"
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:local="clr-namespace:HELLOWPF"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="ControlWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.Resources>
<Style x:Key="smallTrigger" TargetType="TextBox">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsFocused" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background" Value="LightPink" />
<Setter Property="BorderThickness" Value="5" />
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<TextBox
Width="150"
Height="50"
Style="{StaticResource smallTrigger}" />
</StackPanel>
</Grid>
</Window>
DataTrigger 和 MultiDataTrigger 这一对触发器和 Trigger 和 MultiTrigger 非常类似,但是它们多了一个 Binding 属性,需要用到数据绑定,这个后面再说.
事件触发器类似 winform 中的小闪电,就是通过触发特定的事件执行相应的动作,我们借助动画做一个小演示:
<Window
x:Class="HELLOWPF.ControlWindow"
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:local="clr-namespace:HELLOWPF"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="ControlWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.Resources>
<Style x:Key="smallTrigger" TargetType="{x:Type CheckBox}">
<Setter Property="Foreground" Value="SaddleBrown" />
<Style.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Width"
To="150"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Width"
To="70"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<CheckBox
Width="80"
FontSize="15"
Style="{StaticResource smallTrigger}">
佛朗明哥舞步
</CheckBox>
</StackPanel>
</Grid>
</Window>
格式化之后的代码看起来有点长😇.
上面所演示的例子中,样式都是定义在目标控件所在的页面的资源里的,只有当前页面上的控件可以应用样式,这样当出现跨页面的时候样式的定义方式就显得有点无力了。想象一下,你的软件有多个页面,每个页面里面又有多个按钮、文本框、标签等控件,如果每个控件都手动设置它们的属性值来达到一致的外观,这将非常繁琐和容易出错。在大型软件工程中,当你需要开发多个 WPF 应用程序或 DLL 时,确保这些应用程序具有一致的外观是非常重要的。这也是样式(Styles)的真正强大之处所在.
现在的程序一般不都是会带有主题自定义的功能吗,可以通过调整给出的配置项自由修改页面效果,还有明暗主题切换。虽然 WPF 没有 Theme 、 Skin 之类的东西,但是我们可以通过样式很好的完成这项工作捏🍗 。
尝试一下实现一个简单的明暗主题切换:
来看看怎么实现的:
比较无聊,提取 Style 切换颜色,也不知道标准否,文件结构大概如图所示:
分别创建了两个资源字典: DictionaryDarkStyle.xaml 、 DictionaryDefaultStyle.xaml 用来存放默认主题以及深色主题,对应 xaml 代码如下:
Default
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="BorderDefaultStyle" TargetType="Border">
<Setter Property="Background" Value="#fafafa" />
<Setter Property="BorderThickness" Value="0" />
</Style>
<Style x:Key="ButtonDefaultStyle" TargetType="Button">
<Setter Property="Background" Value="#fafafa" />
<Setter Property="Foreground" Value="#333333" />
<Setter Property="Margin" Value="5" />
</Style>
<Style x:Key="LabelDefaultStyle" TargetType="Label">
<Setter Property="Background" Value="#fafafa" />
<Setter Property="Foreground" Value="#333333" />
<Setter Property="FontSize" Value="16" />
</Style>
</ResourceDictionary>
Dark
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="BorderDarkStyle" TargetType="Border">
<Setter Property="Background" Value="#292d3e" />
<Setter Property="BorderThickness" Value="0" />
</Style>
<Style x:Key="ButtonDarkStyle" TargetType="Button">
<Setter Property="Background" Value="#292d3e" />
<Setter Property="Foreground" Value="#a9a9b3" />
<Setter Property="Margin" Value="5" />
<Setter Property="FontSize" Value="16" />
</Style>
<Style x:Key="LabelDarkStyle" TargetType="Label">
<Setter Property="Background" Value="#292d3e" />
<Setter Property="Foreground" Value="#a9a9b3" />
<Setter Property="FontSize" Value="16" />
</Style>
</ResourceDictionary>
在 App.xaml 中添加程序的资源,也就是我们定义的资源字典:
<Application x:Class="ThemeSwitchDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ThemeSwitchDemo"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DictionaryDefaultStyle.xaml"/>
<ResourceDictionary Source="DictionaryDarkStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
默认页面的 xaml 如下:
<Window x:Class="ThemeSwitchDemo.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:local="clr-namespace:ThemeSwitchDemo"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Grid>
<Border x:Name="border" Style="{StaticResource BorderDefaultStyle}">
<StackPanel>
<Label Name="label1"
Height="100"
Content="你想和老爹斗吗?"
Style="{StaticResource LabelDefaultStyle}" />
<Label Name="label2"
Height="200"
Content="还有一件事!永远不要怀疑老爹的话!"
Style="{StaticResource LabelDefaultStyle}" />
<Button Name="button"
Height="100"
Click="button_Click"
Content="SwitchTheme"
Style="{StaticResource ButtonDefaultStyle}" />
</StackPanel>
</Border>
</Grid>
</Window>
通过按钮的点击事件来切换样式:
using System.Windows;
namespace ThemeSwitchDemo
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
bool isDarkStyleApplied = false;
private void button_Click(object sender, RoutedEventArgs e)
{
if (isDarkStyleApplied)
{
// 如果当前是黑色样式,则切换回默认样式
border.Style = (Style)Application.Current.Resources["BorderDefaultStyle"];
label1.Style = (Style)Application.Current.Resources["LabelDefaultStyle"];
label2.Style = (Style)Application.Current.Resources["LabelDefaultStyle"];
button.Style = (Style)Application.Current.Resources["ButtonDefaultStyle"];
isDarkStyleApplied = false;
}
else
{
// 如果当前是默认样式,则切换到黑色样式
border.Style = (Style)Application.Current.Resources["BorderDarkStyle"];
label1.Style = (Style)Application.Current.Resources["LabelDarkStyle"];
label2.Style = (Style)Application.Current.Resources["LabelDarkStyle"];
button.Style = (Style)Application.Current.Resources["ButtonDarkStyle"];
isDarkStyleApplied = true;
}
}
}
}
That’s all. 。
可能是初学的缘故,我觉得应该有比我这样更好的实现方式,欢迎大家留言讨论,毕竟我是初学。还有一件事,牛战士是从来不会摘下面具的🦬.
最后此篇关于WPF入门笔记-03-样式基础的文章就讲到这里了,如果你想了解更多关于WPF入门笔记-03-样式基础的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我喜欢调整 目录的样式(例如背景颜色、字体)预订 , Gitbook 风格 HTML 文档。 这可能吗?如果是这样,有人可以善意地指出我可以开始这样做的地方吗? 谢谢你。 最佳答案 两个步骤: 1)
是否可以使用纯 CSS 选择器根据子节点的兄弟节点数量为节点子节点(在我的例子中为 UL)提供不同的属性,特别是高度? 例如,如果一个节点有 1 个子节点,则 UL 的高度是自动的,但是如果该节点有
我正在与 Vala 一起工作,它首先编译为 C,然后正常从 C 编译。 valac 的一项功能(Vala 编译器)是为 .vala 生成“fast-vapi”文件。 fast-vapi 本质上是为 .
我有两个具有 .body 类的 div,但是,一个位于另一个具有 .box 类的 div 中 - 如下所示: 我只想为 .box 内部的 .body 设置样式...但我在下面所
**注意所有 <> 标签已被删除以允许代码显示**我已经玩了好几个小时了,如果不在设计结束时使用解决方法(即 Corel 绘图),我就无法真正让它工作 *在我继续之前, 首先,网站 URL 是 Adv
我从一个服务中接收到一个字符串,该字符串显然使用 UTF-32 编码对其 unicode 字符进行编码,例如:\U0001B000(C 风格的 unicode 编码)。但是,为了在 JSON 中序列化
我在应用程序资源中有一种样式,我想将其应用于许多不同的饼图。样式如下所示: 为了简单起见,我排除了更多的属性。这一切都很好。现在,我的一些馅饼需要有一个不同的“模型
想象一下,我有一个名为“MyCheckBoxStyle”的 CheckBox 自定义样式。 如何制作基于 MyCheckBoxStyle 嵌入自定义 DataGridCheckBoxColumn 样式
我有一个 Button我在 WPF 中开发的样式,如 this question 中所述.我想用这种风格做的另一件事是拥有 Button缩小一点点,使其看起来像被点击一样被点击。现在,转换代码如下所示
我为超链接控件创建了一个样式:
不知道为什么,但我的 typeahead.js 远程自动完成停止工作。我没有更改任何关于 typeahead.js 的代码,但既然它坏了,我一定是错的。你能看看我的site here吗? ?我会创建
有没有办法创建扩展当前样式的样式,即不是特定样式? 我有一个 WPF 应用程序,我在其中创建样式来设置一些属性,例如边框或验证。 现在我想尝试一些主题,看看哪
我正在为一个网站提出问题,并希望 var reltext 中的正确/再试消息具有不同的颜色,即绿色表示正确,红色表示错误,并且每个旁边可能有一个小 png。 有什么想法吗? A local co
我想到达列表的父节点(使用 id 选择器)并使用纯 JavaScript 添加背景颜色来设置其样式。这是我的代码,但不起作用。 var listParentNode; listPare
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 4 年前。 Improve th
过去几天我一直在与这段代码作斗争,我真的不知道该如何处理它。 基本上,当用户将鼠标滚动到主导航菜单中的某个 LI 元素上时,就会运行一个 Javascript 函数,并根据触发该函数的元素将链接放入下
使用这个可爱的 html 和 css 作为指南,我能够在我的照片上显示我的姓名首字母。 这很好,但是,如果图像不存在,我只想显示首字母;如果图像存在,则不应渲染 peron 首字母。 换句话说,当该图
使用这个可爱的 html 和 css 作为指南,我能够在我的照片上显示我的姓名首字母。 这很好,但是,如果图像不存在,我只想显示首字母;如果图像存在,则不应渲染 peron 首字母。 换句话说,当该图
是否有人尝试过将 JButton 设计为看起来像 NetBeans 工具栏按钮?这将只显示一张图片,当您将鼠标悬停在它上面时,会显示 1px 圆形角灰色边框,并且按钮顶部和底部的背景不同......似
在 Ax2012 中使用图表,它们工作正常。但我想更改它在启动时显示的图表类型,例如“样条”图表,而不是默认的“柱状图”图表。 这是我现在拥有的: http://i.stack.imgur.com/R
我是一名优秀的程序员,十分优秀!