WPF模板与样式

WPF中的模板是非常强大的机制,可以灵活设置控件的外观和数据呈现方式,在复杂GUI场景下会发挥巨大作用。

模板种类

  • 控件模板(ControlTemplate):定义控件的外观。
  • 数据模板(DataTemplate):定义在内容控件或列表控件中显示数据的方式。
  • 列表项面板模板(ItemsPanelTemplate):控制列表控件中各项的布局。

控件模板

控件模板也是控件的一个属性,模板绑定机制是定义控件模板最重要的概念之一,简单来说通过模板绑定可以拿到控件的属性。

其次,为一种控件定义控件模板时,需要了解该控件的可视化树,这是控件渲染的骨架。

可以通过IDE导出控件的现有模板,然后在此基础上做自定义调整。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!--定义控件模板-->
<ControlTemplate x:Key="ButtonTemplate1" TargetType="{x:Type Button}">
<Border Name="Border" BorderThickness="2" CornerRadius="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}">
<Grid>
<Rectangle Name="FocusCue"
Visibility="Hidden"
Stroke="Black"
StrokeThickness="2"
StrokeDashArray="1 2"
SnapsToDevicePixels="True"/>
<ContentPresenter RecognizesAccessKey="True"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="{TemplateBinding Padding}"/>
</Grid>
</Border>

<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="FocusCue" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

<!--使用控件模板-->
<Button Content="Hello2" Template="{StaticResource ButtonTemplate1}"/>

如果为控件模板指定了x:Key属性,那么就必须通过资源引用的方式使用控件模板;如果没有,就按照控件类型应用到所有控件。

数据模板

数据模板统一使用DataTemplate定义,通常在三个位置可以使用数据模板,DataGridCellTemplate属性、列表控件的ItemTemplate属性和内容控件的ContentTemplate属性。

ContentTemplate

在WPF中,有很多控件继承自ContentControl,这类控件都有Content属性,一般为该属性指定字符串类型的值即可,例如为按钮指定显示文本。

但是,我们也可以为该属性绑定复杂类型的数据,通过ContentTemplate指定这些数据的呈现方式。

如下所示,为Content属性绑定一个类实例,将类实例通过模板显示到按钮上。

1
2
3
4
[ObservableProperty]
private Teacher _teacher;

Teacher = new() { Name = "陈老师", Age = 45 };
1
2
3
4
5
6
7
8
9
<DataTemplate x:Key="ButtonContentTemplate1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Age}" Margin="10,0,0,0" />
</StackPanel>
</DataTemplate>

<Button Content="{Binding Teacher}"
ContentTemplate="{StaticResource ButtonContentTemplate1}"/>

ItemTemplate

在WPF中,有很多控件继承自ItemsControl,例如ComboBoxListBoxTabControl等,这里控件都有ItemsPanelItemTemplate属性,分别用来定义列表项布局模板和列表项数据模板。

项属性绑定是定义数据模板最重要的概念之一,绑定过程就是布局数据显示的过程。

如果为控件模板指定了x:Key属性,那么就必须通过资源引用的方式使用数据模板;如果没有,就按照数据类型应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!--定义数据模板-->
<DataTemplate x:Key="ListBoxItemTemplate1" DataType="{x:Type s:String}">
<Border Margin="5"
BorderThickness="1"
CornerRadius="5"
BorderBrush="SteelBlue">
<StackPanel Orientation="Vertical">
<TextBlock Text="项目:"/>
<TextBlock FontWeight="Bold"
Foreground="Red"
Text="{Binding}"/>
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="lihao">
<Setter Property="ListBoxItem.Foreground" Value="Black"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>

<!--定义列表项布局模板-->
<ItemsPanelTemplate x:Key="ListBoxItemsPanel1">
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>

<!--使用数据模板-->
<ListBox ItemTemplate="{DynamicResource ListBoxItemTemplate1}"
ItemsSource="{Binding Names}"
ItemsPanel="{DynamicResource ListBoxItemsPanel1}">
</ListBox>

以上示例同时展示了列表项布局模板的用法,ItemTemplate用于设置一个项的数据显示外观;ItemsPanelTemplate用于设置项与项之间的排列布置。

CellTemplate

通过单元格模板,可以将类的多个数据组合显示到一个单元格中,并且可以添加其它和数据无关的控件元素,例如按钮等。

1
2
3
4
5
6
7
8
9
10
11
12
public class Teacher {
public string Name { get; set; }
public int Age { get; set; }
}

[ObservableProperty]
private List<Teacher> _teachers;

Teachers = new() {
new(){Name="T1",Age=35 },
new(){Name="T2",Age=35 },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<DataGrid AutoGenerateColumns="False" 
Margin="10"
CanUserAddRows="False"
ItemsSource="{Binding Teachers}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="姓名-年龄">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
<TextBlock Text="-"/>
<TextBlock Text="{Binding Age}" FontWeight="SemiBold"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

<DataGridTemplateColumn Header="操作">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="修改"/>
<Button Content="删除"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>

样式

WPF中的样式用于封装对一种控件的多种属性设置,功能类似CSS。Style有以下属性,其中BasedOnSettersTargetTypeTriggers最为常用。

以下代码展示了前三个属性的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!--定义嵌套样式-->
<Style x:Key="BaseButton" TargetType="{x:Type Button}">
<Setter Property="BorderBrush">
<Setter.Value>
<SolidColorBrush Color="OrangeRed"/>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="1"/>
</Style>

<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}"
BasedOn="{StaticResource BaseButton}">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush>
<GradientStop Color="BlueViolet" Offset="0"/>
<GradientStop Color="CadetBlue" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>

<Style x:Key="ButtonStyle2" TargetType="{x:Type Button}"
BasedOn="{StaticResource BaseButton}">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="GreenYellow"/>
</Setter.Value>
</Setter>
</Style>

<!--使用样式-->
<Button Content="GradientColor"
Margin="10"
Style="{StaticResource ButtonStyle1}"/>
<Button Content="SolidColor"
Margin="10"
Style="{StaticResource ButtonStyle2}"/>

触发器

样式触发器是非常实用的功能,可以监听依赖属性或被绑定数据的变化,动态调整样式中的属性。

  • Trigger:监听依赖属性的变化。
1
2
3
4
5
6
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
  • MultiTrigger:监听多个依赖属性的变化,同时满足时才会触发。
  • DataTrigger:监听被绑定数据的变化。以下实例中,绑定了一个后台数据Flag,该触发器会监听这个后台数据的变化,从而触发样式修改。
1
2
3
4
5
6
<Style.Triggers>	
<DataTrigger Binding="{Binding Flag}" Value="lihao">
<Setter Property="FontStyle" Value="Italic"/>
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
  • MultiDataTrigger:监听多个被绑定数据的变化,同时满足才会触发。
  • EventTrigger:当事件发生时,会触发动画,用于制作炫酷的界面效果。

也可以在控件模板和数据模板上使用这些触发器,用来修改模板中某些元素的样式(可以为Setter指定TargetName属性,指明修改哪个控件的属性):

1
2
3
4
5
6
7
8
9
10
11
12
13
<ControlTemplate TargetType="Button">
<Border x:Name="border" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="LightBlue"/>
<Setter TargetName="border" Property="BorderBrush" Value="DarkBlue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

(转载本站文章请注明作者和出处lihaohello.top,请勿用于任何商业用途)

评论