如何为控件创建样式

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

创建样式

可以将一种 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>

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

隐式应用样式

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

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

样式化前的示例屏幕截图

可以通过直接在每个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>

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

现在,元素 TextBlock 如下所示。

样式示例屏幕截图基本样式

明确应用样式

如果将具有值的属性添加到 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 标记扩展将元素上的属性设置为Stylex:Key值,如下所示。

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

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

设置示例屏幕截图文本块的样式

一旦应用样式,无论是显式还是隐式应用,它便会锁定且无法更改。 如果要更改已应用的样式,请创建新样式以替换现有样式。 有关详细信息,请参阅 IsSealed 属性。

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

以编程方式应用样式

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

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

扩展样式

也许你希望两个 TextBlock 元素共享一些属性值,例如居中的 FontFamilyHorizontalAlignment。 但你还希望 文本“我的图片” 具有一些其他属性。 为此,可以创建基于第一个样式的新样式,如下所示。

<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>

然后,将样式应用于 TextBlock

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

TextBlock 样式现在居中,使用 Comic Sans MS 大小为 26和前景色设置为 LinearGradientBrush 示例中所示的字体。 请注意,它覆盖了基本样式的 FontSize 值。 如果在Style中有多个Setter指向同一属性,则最后声明的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 元素。 如果样式位于 resources 节中,并且未在样式上设置 TargetType 属性,则必须设置 x:Key 该属性。

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

另请注意,许多 WPF 控件由其他 WPF 控件的组合组成。 如果创建适用于类型的所有控件的样式,可能会获得意外的结果。 例如,如果在中创建一个以类型为目标的样式,该样式将应用于窗口中的所有控件,即使该是另一控件的一部分,例如

另请参阅