如何为控件创建样式 (WPF .NET)

使用 Windows Presentation Foundation (WPF),可以使用自己的可重用样式自定义现有控件的外观。 可以对应用、窗口和页面全局应用样式,也可以将样式直接应用于控件。

重要

面向 .NET 7 和 .NET 6 的桌面指南文档正在撰写中。

创建样式

可以将 Style 视为一种将一组属性值应用到一个或多个元素的便利方法。 可以对从 FrameworkElementFrameworkContentElement(如 WindowButton)派生的任何元素使用样式。

声明样式的最常见方法是在 XAML 文件的 Resources 部分中声明为资源。 由于样式是一种资源,因此它们同样遵从适用于所有资源的范围规则。 简而言之,声明样式的位置会影响样式的应用范围。 例如,如果在应用定义 XAML 文件的根元素中声明样式,则该样式可以在应用中的任何位置使用。

<Application x:Class="IntroToStylingAndTemplating.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:IntroToStylingAndTemplating"
             StartupUri="WindowExplicitStyle.xaml">
    <Application.Resources>
        <ResourceDictionary>
            
            <Style x:Key="Header1" TargetType="TextBlock">
                <Setter Property="FontSize" Value="15" />
                <Setter Property="FontWeight" Value="ExtraBold" />
            </Style>
            
        </ResourceDictionary>
    </Application.Resources>
</Application>

如果在应用的一个 XAML 文件中声明样式,则该样式只能在该 XAML 文件中使用。 有关资源的范围规则的详细信息,请参阅 XAML 资源概述

<Window x:Class="IntroToStylingAndTemplating.WindowSingleResource"
        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:IntroToStylingAndTemplating"
        mc:Ignorable="d"
        Title="WindowSingleResource" Height="450" Width="800">
    <Window.Resources>
        
        <Style x:Key="Header1" TargetType="TextBlock">
            <Setter Property="FontSize" Value="15" />
            <Setter Property="FontWeight" Value="ExtraBold" />
        </Style>
        
    </Window.Resources>
    <Grid />
</Window>

样式由 <Setter> 子元素组成,这些元素在应用了样式的元素上设置属性。 在上面的示例中,请注意,样式已设置为通过 TargetType 属性应用于 TextBlock 类型。 样式会将 FontSize 设置为 15,并将 FontWeight 设置为 ExtraBold。 为样式更改的每个属性添加一个 <Setter>

隐式应用样式

Style 是一种将一组属性值应用到多个元素的便利方法。 例如,请考虑以下 TextBlock 元素及其在窗口中的默认外观。

<StackPanel>
    <TextBlock>My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

Styling sample screenshot before

可以通过直接对每个 TextBlock 元素设置属性(如 FontSizeFontFamily)来更改默认外观。 但是,如果需要让 TextBlock 元素共享某些属性,可以在 XAML 文件的 Resources 部分中创建 Style,如下所示。

<Window.Resources>
    <!--A Style that affects all TextBlocks-->
    <Style TargetType="TextBlock">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Comic Sans MS"/>
        <Setter Property="FontSize" Value="14"/>
    </Style>
</Window.Resources>

将样式的 TargetType 设置为 TextBlock 类型并省略 x:Key 属性时,该样式将应用到样式的所有 TextBlock 元素,通常是 XAML 文件本身。

现在 TextBlock 元素如下所示。

Styling sample screenshot base style

显式应用样式

如果向样式添加具有值的 x:Key 属性,则样式将不再隐式应用于 TargetType 的所有元素。 只有显式引用样式的元素才会应用样式。

下面是上一节中的样式,但使用 x:Key 属性进行了声明。

<Window.Resources>
    <Style x:Key="TitleText" TargetType="TextBlock">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Comic Sans MS"/>
        <Setter Property="FontSize" Value="14"/>
    </Style>
</Window.Resources>

若要应用样式,请使用 StaticResource 标记扩展将元素上的 Style 属性设置为 x:Key 值,如下所示。

<StackPanel>
    <TextBlock Style="{StaticResource TitleText}">My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

请注意,第一个 TextBlock 元素已应用样式,而第二个 TextBlock 元素保持不变。 上一节中的隐式样式已更改为声明了 x:Key 属性的样式,也就是说,该样式影响的唯一元素是直接引用该样式的那个元素。

Styling sample screenshot textblock

在显式或隐式应用样式后,它将变为密封状态,不能更改。 如果要更改已应用的样式,请创建新的样式来替换现有样式。 有关更多信息,请参见 IsSealed 属性。

可以创建一个根据自定义逻辑选择要应用的样式的对象。 有关示例,请参阅为 StyleSelector 类提供的示例。

以编程方式应用样式

若要以编程方式将已命名的样式分配到元素,请从资源集合中获取样式,并将其分配到元素的 Style 属性。 资源集合中的项属于 Object 类型。 因此,在将检索到的样式分配给 Style 属性之前,必须将它强制转换为 System.Windows.Style。 例如,下面的代码将名为 textblock1TextBlock 的样式设置为定义的样式 TitleText

textblock1.Style = (Style)Resources["TitleText"];
textblock1.Style = CType(Resources("TitleText"), Windows.Style)

扩展样式

也许你希望两个 TextBlock 元素共享某些属性值,如 FontFamily 和居中的 HorizontalAlignment。 你可能还希望文本“My Pictures” 具有一些其他属性。 可以通过创建基于第一个样式的新样式来实现此目的,如下所示。

<Window.Resources>
    <!-- .... other resources .... -->

    <!--A Style that affects all TextBlocks-->
    <Style TargetType="TextBlock">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Comic Sans MS"/>
        <Setter Property="FontSize" Value="14"/>
    </Style>
    
    <!--A Style that extends the previous TextBlock Style with an x:Key of TitleText-->
    <Style BasedOn="{StaticResource {x:Type TextBlock}}"
           TargetType="TextBlock"
           x:Key="TitleText">
        <Setter Property="FontSize" Value="26"/>
        <Setter Property="Foreground">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Offset="0.0" Color="#90DDDD" />
                        <GradientStop Offset="1.0" Color="#5BFFFF" />
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<StackPanel>
    <TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

TextBlock 样式现在居中,使用大小为 26Comic Sans MS 字体,并将前景色设置为如示例中显示的 LinearGradientBrush。 请注意,它会重写基本样式的 FontSize 值。 如果有多个 Setter 指向 Style 中的同一属性,则最后声明的 Setter 优先。

TextBlock 元素现在如下所示:

Styled TextBlocks

TitleText 样式扩展为 TextBlock 类型创建的样式,该样式由 BasedOn="{StaticResource {x:Type TextBlock}}" 引用。 还可以使用样式的 x:Key 扩展具有 x:Key 的样式。 例如,如果有一个名为 Header1 的样式,并且你需要扩展该样式,可以使用 BasedOn="{StaticResource Header1}"

TargetType 属性与 x:Key 属性之间的关系

如前所述,将 TargetType 属性设置为 TextBlock 时,如果不为样式分配 x:Key,会导致将样式应用于所有 TextBlock 元素。 在此情况下,x:Key 隐式设置为 {x:Type TextBlock}。 这意味着,如果将 x:Key 值显式设置为除 {x:Type TextBlock} 以外的任何值,则 Style 不会自动应用于所有 TextBlock 元素。 相反,必须将该样式(通过使用 x:Key 值)显式应用到 TextBlock 元素。 如果你的样式位于资源部分中,并且你未对样式设置 TargetType 属性,则必须设置 x:Key 属性。

除了为 x:Key 提供默认值以外,TargetType 属性还指定 setter 属性应用到的类型。 如果不指定 TargetType,则必须使用语法 Property="ClassName.Property",通过类名称限定 Setter 对象中的属性。 例如,必须将 Property 设置为 "TextBlock.FontSize""Control.FontSize",而不是设置 Property="FontSize"

另请注意,许多 WPF 控件由其他 WPF 控件的组合构成。 如果创建应用于某个类型的所有控件的样式,可能会产生意外结果。 例如,如果创建一个样式,该样式以 Window中的 TextBlock 类型为目标,那么,即使 TextBlock 是另一个控件(如 ListBox)的一部分,该样式也将应用于窗口中的所有 TextBlock 控件。

另请参阅