05 样式和模板

发布时间 2023-12-01 10:20:07作者: 讨厌敲代码的老郭

05 样式和模板

Windows Presentation Foundation (WPF) 样式和模板 - WPF .NET | Microsoft Learn是指一套功能,这套功能使开发者和设计者能够为其产品创建极具视觉表现力的效果和一致的外观。 自定义应用的外观时,需要一个强大的样式设置和模板化模型,以便维护和共享应用内部和应用之间的外观。 WPF 就提供了这样的模型。

样式

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

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

<Window.Resources>
  <!--一个将会被自动应用于所有Button的样式-->
  <Style TargetType="Button">
    <Setter Property="Background" Value="DarkGreen"/>
    <Setter Property="Foreground" Value="Red"/>
  </Style>
  <!--一个必须手动使用的Style-->
  <!--
  TargetType:指定该样式用于哪个控件
  x:Key:指定该资源的名字
  -->
  <Style TargetType="Button" x:Key="MyBtn">
      <Setter Property="FontSize" Value="20"/>
      <Setter Property="FontFamily" Value="楷体"/>
  </Style>
</Window.Resources>

<StackPanel>
    <Button Content="按钮1" Click="Button_Click" Style="{StaticResource MyBtn}"/>
    <Button Content="按钮2" Click="Button_Click"/>
</StackPanel>

上面这种写法会导致具名的样式只有自己的样式,没有通用的样式,我们可以使用BaseOn​属性解决这个问题,BaseOn​类似类的继承,修改代码如下

<!--{StaticResource {x:Type Button}} 表示继承静态资源中Type为Button的样式-->
<Style TargetType="Button" x:Key="MyBtn" BasedOn="{StaticResource {x:Type Button}}">
    <Setter Property="FontSize" Value="20"/>
    <Setter Property="FontFamily" Value="楷体"/>
</Style>
<!--{StaticResource MyBtn}表示继承静态资源中名字为MyBtn的样式-->
<Style TargetType="Button" x:Key="TestBtn" BasedOn="{StaticResource MyBtn}">
    <Setter Property="FontStyle" Value="Italic"/>
</Style>

模板

控件的 ControlTemplate 用于定义控件的外观。 可以通过定义新的 ControlTemplate 并将其分配给控件来更改控件的结构和外观。 在许多情况下,模板提供了足够的灵活性,从而无需自行编写自定义控件。

每个控件都有一个分配给 Control.Template 属性的默认模板。 该模板将控件的视觉呈现与控件的功能关联起来。 因为在 XAML 中定义了模板,所以无需编写任何代码即可更改控件的外观。 每个模板都是为特定控件(例如 Button)设计的。

我们可以创建自己的控件模板,使用自己的自定义控件

<Window.Resources>
    <!--创建一个模板,名为MyBtn,TargetType指定该模板用于哪个控件-->
    <ControlTemplate TargetType="Button" x:Key="MyBtn">
        <!--模板中间的内容将会当作控件展示的内容-->
        <Grid>
            <!-- 使用 TemplateBinding 语法绑定属性值为使用该模板的控件属性,该示例将圆形的Fill绑定到按钮的Background属性上 -->
            <Ellipse Fill="{TemplateBinding Background}"/>
            <!--使用ContentPresenter控件获取并展示使用该模板时设置的属性,ContentSource="属性名"-->
            <ContentPresenter ContentSource="Content" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </Grid>
    </ControlTemplate>
    <!-- 另一个模板,用于GroupBox -->
    <ControlTemplate TargetType="GroupBox" x:Key="MyGroupBox">
        <Border BorderBrush="Green" BorderThickness="2" CornerRadius="5">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="30"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <Border BorderBrush="Green" BorderThickness="0,0,0,3">
                    <StackPanel Grid.Row="0">
                        <ContentPresenter ContentSource="Header"/>
                    </StackPanel>
                </Border>
                <StackPanel Grid.Row="1">
                    <ContentPresenter ContentSource="Content"/>
                </StackPanel>
            </Grid>
        </Border>
    </ControlTemplate>
</Window.Resources>
<StackPanel>
    <!--使用Template={StaticResource 模板名称}来使用一个模板-->
    <Button Template="{StaticResource MyBtn}" Content="我是按钮" Width="60" Height="60"/>
    <GroupBox Template="{StaticResource MyGroupBox}" Header="哈哈哈">
        <StackPanel>
            <TextBlock>123123123</TextBlock>
            <TextBlock>123123123</TextBlock>
            <TextBlock>123123123</TextBlock>
        </StackPanel>
    </GroupBox>
    <GroupBox Template="{StaticResource MyGroupBox}" Header="66666" Margin="0,20,0,0">
        <Image Source="wallhaven-1pr8k9.jpg"/>
    </GroupBox>
    <GroupBox Template="{StaticResource MyBtn}"></GroupBox>
</StackPanel>

添加交互

上例中自定义了按钮的模板,但是现在鼠标悬浮时按钮没有任何的反馈,我们可以通过添加触发器以在不同的时刻设置控件的不同属性值

通过ControlTemplate.Triggers​​添加触发器,其中Property​​设置何时触发它,Setter​​标识哪个控件的什么属性设置为什么值

更多可选项详见更多IsXXX

<ControlTemplate TargetType="Button" x:Key="myBtn">
    <Grid>
        <Ellipse Fill="{TemplateBinding Background}" Width="{TemplateBinding Height}" Height="{TemplateBinding Height}" Name="e" Stroke="Blue" StrokeThickness="4"/>
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="true">
            <Setter TargetName="e" Property="Fill" Value="green"/>
            <Setter TargetName="e" Property="Stroke" Value="Gold"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>