WPF中文网

Button的模板样式实战

我们以Button按钮为例,演示其模板和样式的用法。首先我们定义两个样式,并在样式中定义了Button的控件模板(ControlTemplate)。

第一个样式

<Style x:Key="ButtonIconStyle" TargetType="Button">
    <Setter Property="Background"  Value="Transparent"/>
    <Setter Property="Foreground" Value="#646464"/>
    <Setter Property="FontSize" Value="16"/>
    <Setter Property="Height" Value="30"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border Background="{TemplateBinding Background}"
                        CornerRadius="5"
                        Margin="0"
                        Height="{TemplateBinding Height}">
                    <Grid HorizontalAlignment="Center"
                          VerticalAlignment="Center">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="auto"/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{TemplateBinding Tag}"
                                   FontSize="{TemplateBinding FontSize}"
                                   Foreground="{TemplateBinding Foreground}"
                                   Margin="5 5 5 5"
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center"/>
                        <TextBlock Grid.Column="1" 
                                   Text="{TemplateBinding Content}"
                                   FontSize="{TemplateBinding FontSize}"
                                   Foreground="{TemplateBinding Foreground}"
                                   Margin="0 5 5 5" 
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center"/>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="LightPink"/>
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="False">
                        <Setter Property="Background" Value="Transparent"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

在上面的样式中,我们在Template属性中实例化了一个ControlTemplate控件模板,为Button的模板(可视化树)定义了一个Border装饰器,为了实现图文按钮效果,所以在里面实例化了一个Grid,以及两个TextBlock,使用TemplateBinding将Button的属性与可视化树中的控件的属性进行模板绑定,巧妙的利用Button的Tag属性作为图标显示。最后,在ControlTemplate中实例化了两个触发器,条件是鼠标移上去或移开,改变Button的背景颜色。

第二个样式

<Style x:Key="ButtonIconBorderStyle" TargetType="Button">
    <Setter Property="Background"  Value="Transparent"/>
    <Setter Property="Foreground" Value="#646464"/>
    <Setter Property="FontSize" Value="16"/>
    <Setter Property="Height" Value="30"/>
    <Setter Property="Margin" Value="5"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        CornerRadius="0"
                        Margin="0"
                        Height="{TemplateBinding Height}">
                    <Grid HorizontalAlignment="Center"
                          VerticalAlignment="Center">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="auto"/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{TemplateBinding Tag}"
                                   FontSize="{TemplateBinding FontSize}"
                                   Foreground="{TemplateBinding Foreground}"
                                   Margin="5 5 5 5"
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center"/>
                        <TextBlock Grid.Column="1" 
                                   Text="{TemplateBinding Content}"
                                   FontSize="{TemplateBinding FontSize}"
                                   Foreground="{TemplateBinding Foreground}"
                                   Margin="0 5 5 5" 
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center"/>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="DarkGreen"/>
                        <Setter Property="BorderBrush" Value="Green"/>
                        <Setter Property="Foreground" Value="White"/>
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="False">
                        <Setter Property="Background" Value="Transparent"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我们在第一种样式的基础之上进行了细节优化,绑定了Button的边框到可视化树中的Border控件上,并优化了触发器的设置,从而形成不同的按钮呈现。最后我们来看一下两个按钮的呈现效果。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:070-《Button模板样式实战》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年10月9日

什么是样式?样式有什么作用?首先,WPF框架提供了许许多多的控件,而这些控件拥有不同的属性,例如一个button控件的长宽、背景颜色、字体字号、内外边距、边框等,我们可以设置这些属性的值,从而让控件呈现出不同的显示效果。如果有多个button,我们该怎么办呢,每个按钮都去设置一遍属性?显示这是不科学的。于是,我们可以将一系列的属性的设置“集中”起来,将它们定义成一个样式,最后将这个样式再设置到控件上,从而达到“一处定义多处引用”的偷懒行为。

样式——就是一种将一组属性值应用到多个元素的便捷方法。

一、样式的定义

public class Style : DispatcherObject, INameScope, IAddChild, ISealable, IHaveResources, IQueryAmbient
{
    public Style();
    public Style(Type targetType);
    public Style(Type targetType, Style basedOn);

    public bool IsSealed { get; }
    public Type TargetType { get; set; }
    public Style BasedOn { get; set; }
    public TriggerCollection Triggers { get; }
    public SetterBaseCollection Setters { get; }
    public ResourceDictionary Resources { get; set; }

    public override int GetHashCode();
    public void RegisterName(string name, object scopedElement);
    public void Seal();
    public void UnregisterName(string name);

}

样式的类型叫Style,它继承于DispatcherObject,它最重要的几个属性如下:

TargetType属性:这是一个类类型,也就是一个反射,这个属性指明了当前样式要作用于哪种类型的控件上。因为WPF中有许多的控件,我们定义一个样式时,必须要指明这个样式的“适用范围”。

BasedOn属性:样式也有继承的概念,所以,BasedOn指明了当前样式继承于哪个样式

Triggers属性:这是一个集合属性,表示触发器的定义,当满足某些条件时,触发哪些行为,以使控件达到一定的“节目效果”。比如当鼠标移上去时,控件的背景颜色变成红色。这些的效果就可以通过定义控件的触发器来设置。

Setters属性:这也是一个集合属性,样式是控件属性的值的“提前设置”,所以,我们对控件属性的值的设置都是以Setter条目的形式,一条一条的放到这个Setters集合中。

Resources属性:这个属性叫资源字典。在正式讲解样式之前,我们要先介绍一下资源字典的概念及其用途。它表示一些资源,以字典的形式进行维护,方便程序引用。下一节,我们先介绍ResourceDictionary。

二、Style样式如何使用

当我们定义好一个Style之后,如此去使用它?首先,我们在《FrameworkElement基类》那一节中也曾了解过Style的概念,并且我们会发现FrameworkElement基类就有一个Style属性。而所有的控件都继承于FrameworkElement基类,所以,我们只需要将定义好的样式赋值到控件的Style属性即可。

2.1 在Application.Resources中定义样式

<Application x:Class="HelloWorld.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:HelloWorld"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <Style x:Key="ButtonStyle" TargetType="Button">
            <Setter Property="Width" Value="100"/>
            <Setter Property="Height" Value="30"/>
            <Setter Property="Background" Value="Red"/>
            <Setter Property="Foreground" Value="White"/>
        </Style>
    </Application.Resources>
</Application>

2.2 在XAML中引用样式

<Grid>
    <Button Content="文字块" Style="{StaticResource ButtonStyle}"/>
</Grid>

注意:在引用样式时,我们有两种方式,分别是DynamicResource和StaticResource,后面再写上样式的名称。DynamicResource表示动态资源,StaticResource表示静态资源。这两者的区别是:静态资源在第一次编译后即确定其对象或值,之后不能对其进行修改。动态资源则是在运行时决定,当运行过程中真正需要时,才到资源目标中查找其值。因此,我们可以动态地修改它。由于动态资源的运行时才能确定其值,因此效率比静态资源要低。

另外,我们在定义样式的条目时,实际上是实例化了一个Setter对象。它有两个关键是属性,分别是Property和Value。Property表示要设置的属性名,Value表示要设置的属性值。有点像键值对的感觉。

public class Setter : SetterBase, ISupportInitialize
{
    public Setter();
    public Setter(DependencyProperty property, object value);
    public Setter(DependencyProperty property, object value, string targetName);

    public DependencyProperty Property { get; set; }
    public object Value { get; set; }
    public string TargetName { get; set; }

    public static void ReceiveMarkupExtension(object targetObject, XamlSetMarkupExtensionEventArgs eventArgs);
    public static void ReceiveTypeConverter(object targetObject, XamlSetTypeConverterEventArgs eventArgs);

}

另外,Setter从定义上看,它还有一个属性叫TargetName,顾名思义,目标名称,也就是这一对属性名和值是哪个控件的属性名和值。通常它在定义触发器时使用,我们将在后面的场景中去学习它。

这一节,我们只是简单的介绍了样式的来龙去脉,待我们了解了资源字典之后,会进一步的学习样式。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:047-《WPF样式概述》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年9月11日

copyright @重庆教主 WPF中文网 联系站长:(QQ)23611316 (微信)movieclip (QQ群).NET小白课堂:864486030 | 本文由WPF中文网原创发布,谢绝转载 渝ICP备2023009518号-1