WPF之UI进阶--控件样式与样式模板及词典

WPF的优势之一就是能够更加容易快捷的对窗体和控件的外面进行改造,换句话说,那就是UI设计个性化更加容易。主要是借助了样式、模板及词典来实现的。那么本篇博文就一一对他们进行介绍。

文章目录

文章原出处: https://blog.csdn.net/haigear/article/details/142632573
首先我们来看看通过简单修改而得到的这么一个控件效果,如下图,我们对radiobutton、checkbox、button进行了改造:

由于录屏软件无法录到鼠标的点击,所以大家看到的就是突然的控件操作。
最左边的实际是radiobutton,我们将圆形按钮修改为带字母的按钮,这个改造很适合做考试系统的选择题的项目中。
没有对控件进行改造之前,效果是这样的,大家可以对比一下:

文章原出处: https://blog.csdn.net/haigear/article/details/142632573

一、样式

样式的实现和我们在web前端做css其实差不多的,只不过这里采用了xaml,也就是xml的一种标签语言来实现。

在WPF中,创建并应用样式(Style)通常涉及以下步骤,这里以在定义在窗口级别Window.Resources中,这样整个窗体中所有的对应的目标控件都有效,如果定义在应用程序级别<Application.Resources>,那就对于整个应用程序中的目标控件都有效。下面我们来看如何定义样式。

1: 定义样式

在Window.Resources中定义样式,指定TargetType为要应用样式的控件类型。我们需要给哪种控件写样式那这里的TargetType的值就些什么类型的控件,比如:

csharp 复制代码
  <Style TargetType="{x:Type CheckBox}">
csharp 复制代码
 <Style TargetType="{x:Type RadioButton}">
csharp 复制代码
  <Style TargetType="{x:Type Button}">

2: 使用Setter设置属性

通过Setter元素为控件的属性设置默认值。每个Setter包含Property和Value属性。比如,这里我们就修改了控件的背景和前景以及字体大小background,foreground、fontsize。

csharp 复制代码
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="16"/> <Setter Property="Padding" Value="10"/>

记住每种控件的属性都必须使用一个setter配合property、value来实现。

关于Property和Value

从上面的代码可知,wpf的样式中我们使用setter中的property来指定修改控件的相应属性,直接写在setter标签内。很自然和属性对应的值就用value来表达了。

关于<Setter.Value>

当要设置的Value比较复杂时,例如设置一个模板或者一个复杂对象时,会使用<Setter.Value>这种嵌套的形式,但它仍然是标签的一部分。例如:

xml 复制代码
   <Style x:Key="MyButtonStyle" TargetType="Button">
       <Setter Property="Template">
           <Setter.Value>
               <ControlTemplate TargetType="Button">
                   <!-- 这里是模板的定义 -->
               </ControlTemplate>
           </Setter.Value>
       </Setter>
   </Style>

在这个例子中,<Setter.Value>是在标签内部,用于指定Template属性的值(这里是一个ControlTemplate)。它不是一个独立于的标签,而是用于处理复杂属性值设置的一种嵌套语法结构。

3: 应用触发器(可选)

如果需要根据控件的状态改变样式,可以使用Style.Triggers定义一个或多个Trigger。每个Trigger可以包含一个或多个Setter,这些Setter仅在触发器条件满足时应用。

csharp 复制代码
<Style.Triggers>
                <!-- 鼠标悬停时改变背景色 -->
  <Trigger Property="IsMouseOver" Value="True">
         <Setter Property="Background" Value="Gray"/>
     </Trigger>
     <!-- 按钮被按下时改变前景色 -->
     <Trigger Property="IsPressed" Value="True">
         <Setter Property="Foreground" Value="White"/>
     </Trigger>
   </Style.Triggers>

从上面的代码实例中我们可以发现,每个Trigger标签的子标签可以是Setter标签,也就是说,Trigger可以用来修改控件的属性。

注意事项:

确保TargetType正确指向你想要应用样式的控件类型。

Setter的Property属性必须是目标类型的一个依赖属性。

Style.Triggers中的Trigger可以基于控件的属性值变化或状态变化(如IsMouseOver、IsChecked等)来触发。可以理解为类似于监听变化的作用。

如果样式定义在Window.Resources中,它将自动应用于所有匹配TargetType的控件。如果需要对特定控件应用不同的样式,可以使用StaticResource显式引用样式。

以下是一个在Window.Resources中定义按钮样式的示例,包括Setter和Style.Triggers的使用:

xml 复制代码
<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <!-- 定义按钮样式 -->
        <Style TargetType="{x:Type Button}">
            <!-- 设置按钮的默认属性 -->
            <Setter Property="Background" Value="LightGray"/>
            <Setter Property="Foreground" Value="Black"/>
            <Setter Property="FontSize" Value="16"/>
            <Setter Property="Padding" Value="10"/>
            
            <!-- 应用触发器 -->
            <Style.Triggers>
                <!-- 鼠标悬停时改变背景色 -->
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="Gray"/>
                </Trigger>
                <!-- 按钮被按下时改变前景色 -->
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="Foreground" Value="White"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <!-- 应用样式到按钮 -->
    <Button Content="Click Me" Style="{StaticResource {x:Type Button}}"/>
</Window>

在这个例子中:

我们定义了一个针对Button类型的样式,并设置了按钮的默认背景色、前景色、字体大小和内边距。

使用Style.Triggers定义了两个触发器:一个用于鼠标悬停时改变背景色,另一个用于按钮被按下时改变前景色。

最后,我们在Button控件上通过Style属性应用了这个样式。

通过这种方式,你可以为WPF应用程序中的按钮定制外观,并根据用户的交互动态改变样式

4:带属性的setter和style.triggle标签中的setter

两者都是用于设置属性值的。无论是在Style标签下的普通Setter还是在Style.Triggers中的Setter,其最终目的都是改变某个控件的属性值,以达到改变控件外观或行为的效果。但他们还是有所不同。

触发条件

普通 Setter:在Style中的普通Setter会在样式应用到控件时无条件地设置属性值。只要控件使用了这个样式,对应的属性就会被设置为Setter中指定的值。例如,上面的第一个例子中,只要按钮应用了MyButtonStyle样式,其背景色就会被设置为蓝色。

Style.Triggers 中的 Setter:只有在满足特定的触发条件时才会设置属性值。在上面的第二个例子中,只有当鼠标悬停在按钮上(IsMouseOver = "True")这个触发条件满足时,按钮的背景色才会被设置为黄色。

灵活性和动态性

普通 Setter:相对固定,一旦样式应用到控件,属性值就固定下来(除非有其他机制修改它,如数据绑定等)。它主要用于设置控件的初始外观或默认状态下的属性值。

Style.Triggers 中的 Setter:更具动态性,可以根据控件的状态变化(如鼠标交互、数据变化等)来改变属性值,从而提供更丰富的用户交互体验。例如,可以根据按钮是否被按下、是否获得焦点等不同状态动态地改变按钮的颜色、大小等属性。

应用范围

普通 Setter:通常用于设置一些基本的、不依赖于动态状态变化的属性。例如,设置按钮的字体、字号、默认背景色等基本属性。

Style.Triggers 中的 Setter:专注于处理与特定触发条件相关的属性设置,如响应鼠标事件(悬停、按下等)、数据验证结果(如数据错误时改变控件外观)等情况下的属性调整。

5:高级货ControlTemplate

ControlTemplate用于定义控件的可视化结构。它允许你完全自定义控件的外观,覆盖控件的默认视觉呈现。例如,你可以使用ControlTemplate来重新定义一个Button看起来像一个圆形图标,而不是默认的矩形按钮。

ControlTemplate定义了控件的整体视觉结构,而ContentPresenter是这个结构中的一部分,专门负责显示控件的内容。ControlTemplate可以包含各种元素来构建控件的外观,如Border、Grid、Ellipse等,ContentPresenter则确保控件原本的内容(如Button的文本或ComboBox的下拉选项等)能够正确地在这个自定义的视觉结构中显示。

ContentPresenter

ContentPresenter是一个在ControlTemplate中非常重要的元素,可以说是关键元素。没有它就无法在自定义的控件模板(ControlTemplate)中呈现控件的内容。例如,对于一个Button控件,其Content(可能是文本或者图标等)就是通过ContentPresenter在自定义模板中显示出来的。

在ControlTemplate中,通常会将ContentPresenter放置在合适的位置,以确定内容显示的位置。我们通常都会将它置于装饰容器Border内,例如:

xml 复制代码
     <ControlTemplate TargetType="Button">
         <Border BorderBrush="Black" BorderThickness="1">
             <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
         </Border>
     </ControlTemplate>

获取外部资源TemplateBinding

TemplateBinding可以用于在ControlTemplate中绑定属性到外部的源。例如,当自定义Button模板时,可以使用TemplateBinding将Button的某些属性与模板内部的元素相关联。对于ContentPresenter,如果想要根据外部Button的属性来调整内容的显示方式,可以使用TemplateBinding。

假设想要根据Button的Foreground属性来设置ContentPresenter中的内容颜色,可以这样做

xml 复制代码
 <ControlTemplate TargetType="Button">
     <Border BorderBrush="Black" BorderThickness="1">
         <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{TemplateBinding Foreground}"/>
     </Border>
 </ControlTemplate>

在ControlTemplate中,TemplateBinding是一种在模板内部元素和外部控件属性之间建立联系的方式。它使得模板可以根据外部控件的状态或属性进行自适应调整。

在了解完样式后,模板和样式词典就很容易了。

5:绘图属性

上面代码中有的用到了一些绘图属性如rectangle.fill,这里将一些常用的绘图属性列表如下,供初学者查阅。

属性 作用 用法示例
Fill 指定图形内部区域的填充内容,可以是颜色、渐变或图案等 <Rectangle Width="100" Height="50"><Rectangle.Fill><SolidColorBrush Color="Blue"/></Rectangle.Fill></Rectangle>
Stroke 定义图形轮廓(边框)的颜色 <Rectangle Width="100" Height="50"><Rectangle.Stroke><SolidColorBrush Color="Black"/></Rectangle.Stroke><Rectangle.StrokeThickness>2</Rectangle.StrokeThickness></Rectangle>
StrokeThickness 指定图形轮廓(描边)的宽度 <Rectangle Width="100" Height="50"><Rectangle.Stroke><SolidColorBrush Color="Black"/></Rectangle.Stroke><Rectangle.StrokeThickness>2</Rectangle.StrokeThickness></Rectangle>
Opacity 控制图形的透明度,取值范围0 - 1 <Rectangle Width="100" Height="50"><Rectangle.Fill><SolidColorBrush Color="Blue"><SolidColorBrush.Opacity>0.5</SolidColorBrush.Opacity></SolidColorBrush></Rectangle.Fill></Rectangle>
CornerRadius 将矩形的角变为圆角,可统一指定或分别指定四个角的半径 <Rectangle Width="100" Height="50" CornerRadius="10"><Rectangle.Fill><SolidColorBrush Color="Green"/></Rectangle.Fill></Rectangle>
Width 指定图形的宽度 <Rectangle Width="100" Height="50"><Rectangle.Fill><SolidColorBrush Color="Red"/></Rectangle.Fill></Rectangle>
Height 指定图形的高度 <Rectangle Width="100" Height="50"><Rectangle.Fill><SolidColorBrush Color="Red"/></Rectangle.Fill></Rectangle>
RenderTransform 对图形进行平移、旋转、缩放等变换操作,常见变换类型有TranslateTransform、RotateTransform、ScaleTransform等 <Rectangle Width="100" Height="50"><Rectangle.RenderTransform><TranslateTransform X = "20" Y = "30"/></Rectangle.RenderTransform><Rectangle.Fill><SolidColorBrush Color="Yellow"/></Rectangle.Fill></Rectangle>
Clip 用于裁剪图形的显示区域,可使用几何形状定义裁剪区域 <Rectangle Width="100" Height="50"><Rectangle.Clip><EllipseGeometry Center="50,25" RadiusX="30" RadiusY="20"/></Rectangle.Clip><Rectangle.Fill><SolidColorBrush Color="Purple"/></Rectangle.Fill></Rectangle>
Stretch(针对可拉伸图形如Image等) 确定当图形尺寸与容器尺寸不匹配时如何调整图形大小,取值有None、Fill、Uniform、UniformToFill等 <Image Source="myImage.jpg" Stretch="Uniform"/>

文章原出处:https://blog.csdn.net/haigear/article/details/142632573

二、模板

在 WPF 中,样式(Style)和控件模板(ControlTemplate)是定义控件外观和行为的关键工具。样式通常用于设置控件的属性,而控件模板则用于定义控件的结构和布局。编写样式模板的步骤:

1. 定义控件模板(ControlTemplate)

控件模板允许你完全自定义控件的视觉结构。你可以通过 ControlTemplate 元素定义控件的外观和行为。

步骤 1: 定义控件模板

xml 复制代码
<ControlTemplate TargetType="{x:Type Button}">
    <Border Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Border>
    <ControlTemplate.Triggers>
        <!-- 定义触发器,例如当按钮被按下时改变背景 -->
        <Trigger Property="IsPressed" Value="True">
            <Setter TargetName="border" Property="Background" Value="LightGray"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

在这个模板中,我们定义了一个按钮的外观,使用 Border 和 ContentPresenter 来布局按钮的背景和内容。我们还定义了一个触发器,当按钮被按下时改变背景色。

2. 创建样式(Style)

样式用于封装控件的属性设置和模板。样式可以包含 Setter 元素来设置属性,以及 ControlTemplate 来定义控件的外观。

步骤 2: 创建样式

xml 复制代码
<Style TargetType="{x:Type Button}">
    <Setter Property="Template" Value="{DynamicResource YourButtonTemplate}"/>
    <Setter Property="Background" Value="LightBlue"/>
    <Setter Property="Foreground" Value="White"/>
    <Setter Property="FontSize" Value="16"/>
</Style>

在这个样式中,我们引用了上面定义的控件模板,并设置了按钮的默认背景、前景色和字体大小。

3. 应用样式和模板

将样式应用到控件上,可以通过 Style 属性直接应用,或者通过资源字典(ResourceDictionary)引用。

假设我们想要自定义一个按钮的外观和行为,以下是具体的步骤和代码示例:

步骤 3: 应用样式

xml 复制代码
<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="200">
    <Window.Resources>
        <!-- 在这里定义控件模板和样式 -->
        <ControlTemplate x:Key="YourButtonTemplate" TargetType="{x:Type Button}">
            <!-- 控件模板定义 -->
        </ControlTemplate>
        <Style TargetType="{x:Type Button}">
            <!-- 样式定义 -->
        </Style>
    </Window.Resources>
    <Grid>
        <!-- 应用样式到按钮 -->
        <Button Content="Click Me" Style="{DynamicResource {x:Type Button}}"/>
    </Grid>
</Window>

在这个示例中,我们使用的思路是在模板中定义我们需要的样式效果,然后通过样式style标签的定义引用template,最后在控件中引用style的动态资源。我们在 Window.Resources 中定义了控件模板和样式,并通过 DynamicResource 引用了模板和样式。然后,我们在 Button 控件上应用了这个样式。

4、另外一种方式:在模板中直接定义好样式

也就是说,我们不必像上述步骤那么麻烦,直接一点,把要设置的样式先在模板中实现了即可。

xml 复制代码
<ControlTemplate x:Key="ButtonTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Orange" BorderThickness="3" CornerRadius="2" Background="Red">
<ContentPresenter Margin="{TemplateBinding Padding}" HorizontalAlignment="Center" />
</Border>
</ControlTemplate>

ContentPresenter 和templateBinding前面讲样式定义的时候已经讲过了,不熟悉的可以爬楼上去看看。

下面是应用模板的调用:

xml 复制代码
<StackPanel Margin="5" >
<Button Margin="5" Padding="13">Normal Button</Button>
<Button Margin="5" Padding="3" Template="{StaticResource ButtonTemplate}" />
</StackPanel>

通过这种方式,你可以完全控制 WPF 控件的外观和行为,实现高度定制化的用户界面。

文章原出处:https://blog.csdn.net/haigear/article/details/142632573

三、词典

在 WPF 中,样式和控件模板通常定义在资源字典(ResourceDictionary)中,以便于重用和管理。样式词典可以看作是资源字典的一种应用,用于集中管理样式。

如何编写样式词典?样式词典的编写通常涉及以下步骤:

1.创建资源字典文件:

创建一个或多个 XAML 文件(通常是 .xaml 文件),用于存放样式和资源定义。

假设我们有两个资源字典文件,一个用于定义按钮样式,另一个用于定义窗口的背景样式。ButtonStyles.xaml和WindowStyles.xaml;

2.定义样式和模板:

在资源字典中定义样式和控件模板,就像之前示例中展示的那样。

ButtonStyles.xaml

xml 复制代码
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="PrimaryButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Blue"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="FontSize" Value="14"/>
        <!-- 更多设置 -->
    </Style>
</ResourceDictionary>

WindowStyles.xaml

xml 复制代码
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="MainWindowStyle" TargetType="{x:Type Window}">
        <Setter Property="Background" Value="LightGray"/>
        <!-- 更多设置 -->
    </Style>
</ResourceDictionary>

3.引用资源字典:

在需要使用这些样式的 XAML 文件中,通过<ResourceDictionary.MergedDictionaries> 标签引入资源字典文件。

在主窗口中引用样式词典

xml 复制代码
<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="200"
        Style="{DynamicResource MainWindowStyle}">
    <Grid>
        <Button Content="Click Me" Style="{DynamicResource PrimaryButtonStyle}"/>
    </Grid>
</Window>

在主窗口的 XAML 中,我们通过 <Window.Resources> 引入了 WindowStyles.xaml,并使用 DynamicResource 引用了窗口样式。同时,我们也在按钮上应用了 PrimaryButtonStyle 样式。

通过这种方式,你可以将样式和资源集中管理,并在多个地方重用它们,从而提高代码的可维护性和可扩展性。样式词典(资源字典)的使用有助于保持样式的一致性,并简化样式管理。

文章可能随时更新,请关注文章原出处:https://blog.csdn.net/haigear/article/details/142632573

相关推荐
是木子啦11 小时前
wpf passwordbox控件 光标移到最后
c#·wpf
The Sheep 202311 小时前
wpf 命令理解
wpf
布伦鸽11 小时前
C# WPF DataGrid使用Observable<Observable<object>类型作为数据源
开发语言·c#·wpf
打码的猿15 小时前
在Qt中实现SwitchButton(开关按钮)
开发语言·qt·ui
分布式存储与RustFS1 天前
告别复杂配置:用Milvus、RustFS和Vibe Coding,60分钟DIY专属Chatbot
wpf·文件系统·milvus·对象存储·minio·rustfs·vibe
未来之窗软件服务1 天前
UI设计(三)按实际输出内容递增的序号效果——东方仙盟筑基期
ui·thinkphp·仙盟创梦ide·东方仙盟sdk
知识分享小能手1 天前
微信小程序入门学习教程,从入门到精通,自定义组件与第三方 UI 组件库(以 Vant Weapp 为例) (16)
前端·学习·ui·微信小程序·小程序·vue·编程
攻城狮CSU1 天前
WPF 绑定机制实现原理
wpf
攻城狮CSU1 天前
WPF 之数据绑定一(Data Binding)
wpf
wuty0072 天前
记录一下 WPF进程 SendMessage 发送窗口消息进行进程间通信,存在进程权限无法接受消息的问题
wpf·进程间通信·sendmessage·进程权限