7.模板Template

7.模板Template Template 模板


WPF的模板基类叫FrameworkTemplate,它是一个抽象类,它有三个子类,分别是ControlTemplate(控件模板)、ItemsPanelTemplate(元素面板模板)和DataTemplate(数据模板)

ControlTemplate控件模板用于定义控件的外观,也就是Control基类的Template属性,而绝大多数控件都继承于Control基类,意味着我们都可以去重新定义它们的视觉外观。

DataTemplate数据模板即数据的外衣。用于从一个对象中提取数据,并在内容控件或列表控件的各个项中显示数据。比如ContentControl基类中的ContentTemplate属性,或者集合控件ItemsControl基类ItemTemplate属性,它们都是DataTemplate数据模板,用来定义数据的外观(数据的呈现形式)。

ItemsPanelTemplate元素面板模板也是用于ItemsControl控件或ItemsControl的子类控件中,因为在集合控件中要考虑每个元素之间的布局方式,所以可以采用ItemsPanelTemplate去定义。ItemsControl基类有一个ItemsPanel属性,它就是一个ItemsPanelTemplate模板。

ControlTemplate控件模板

默认情况下,Button按钮的内容只能显示文字,我们可以设置它的Content属性即可。也可以设置它的Width和Height,改变它的尺寸,但是,它始终是一个矩形的按钮。假如我们希望得到一个圆形的按钮、或者带图标的按钮,这个时候就需要去改变按钮的内部结构外观——ControlTemplate控件模板

FrameworkElement基类有一个Template属性就是指控件的ControlTemplate模板,这就意味着,几乎所有的WPF控件都是可以修改它的结构和外观(可视化树)的。注意,不能仅替换控件的可视化树的一部分;若要更改控件的可视化树,必须将该控件的 Template 属性设置为新的完整 ControlTemplate

查看控件的默认模板:点击button-->鼠标单击右键-->编辑模板-->编辑副本,会弹出一个对话框,点击确定。

 <Window.Resources>
        <Style x:Key="FocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
        <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
        <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
        <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
        <SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
        <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
        <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
        <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
        <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
        <Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
            <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
            <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
            <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Padding" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                            <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsDefaulted" Value="true">
                                <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
                                <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

我们关注一下Setter项目设置Template属性的写法,因为Template属性是ContorlTemplate类型,所以在上面的代码中实例化了一个ControlTemplate对象,并且,TargetType="{x:Type Button}",表示这个ContorlTemplate实例是给Button定义的模板。

而在ContorlTemplate对象中,定义了一棵可视化树。

这里定义了一个Border装修器,里面有一个ContentPresenter对象。什么是ContentPresenter对象?

ContentPresenter继承于FrameworkElement,说明它也是一个控件。从命名上看,它叫内容主持者,本质上它只是一个占座的,为谁占座?为ContentControl内容控件占座。因为Button继承于ContentControl,所以Button也有Content属性,在ContentTemplate中的ContentPresenter可视为等于Content属性

控件模板的几种设置方式:

1.将ControlTemplate定义在在控件中

<Button Content="将ControlTemplate定义在在控件中" 
        Width="280" Height="40" Margin="10" Foreground="#747787">
    <Button.Template>
        <ControlTemplate TargetType="Button">
            <Border Background="Transparent" CornerRadius="5" BorderThickness="1" BorderBrush="#C9CCD5">
                <ContentPresenter  HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Border>
        </ControlTemplate>
    </Button.Template>
</Button>

2.将ControlTemplate定义在资源中

<Window.Resources>
    <ControlTemplate x:Key="ButtonTemplate" TargetType="Button">
        <Border Background="#C6D2FC" CornerRadius="5" BorderThickness="1" BorderBrush="#545BAD">
            <ContentPresenter  HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </Border>
    </ControlTemplate>
</Window.Resources>
 
<Button Content="将ControlTemplate定义在资源中" 
        Template="{StaticResource ButtonTemplate}" 
        Width="280" Height="40" Margin="10" Foreground="#707CA5"/>

3.将ControlTemplate定义在Style样式中

<Button Content="将ControlTemplate定义在Style样式中" 
        Width="280" Height="40" Margin="10" Foreground="White">
    <Button.Style>
        <Style TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="#7AAB7D" CornerRadius="5">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="auto"/>
                                    <ColumnDefinition/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Grid.Column="0" Text="" 
                                           VerticalAlignment="Center" 
                                           Margin="3" FontSize="18"/>
                                <ContentPresenter Grid.Column="1" 
                                                  HorizontalAlignment="Center" 
                                                  VerticalAlignment="Center"/>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Button.Style>
</Button>

 

DataTemplate数据模板

DataTemplate 继承于FrameworkTemplate基类,它有3个属性,分别是DataType 、Triggers 和DataTemplateKey 。DataType表示当前数据模板所针对的数据类型,Triggers 是触发器集合。

在ItemsControl集合控件中就有一个ItemTemplate属性,它的类型就是DataTemplate 。说明所有继承于ItemsControl的集合子控件都可以设置数据模板。

案列:

<Grid>
    <ScrollViewer>
        <ItemsControl ItemsSource="{Binding Persons}"><!--数据控件-->
            <ItemsControl.ItemTemplate><!--ItemTemplate属性-->
                <DataTemplate><!--数据模板-->
                    <Border x:Name="border"
                        Width="280"
                        Height="200"
                        Margin="5"
                        BorderThickness="1" 
                        BorderBrush="Gray">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <StackPanel Grid.Row="0" Margin="20">
                                <TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="20"/>
                                <Rectangle Height="5"/>
                                <TextBlock Text="{Binding Occupation}" FontSize="16"/>
                            </StackPanel>
                            <StackPanel Grid.Row="1" Orientation="Horizontal">
                                <TextBlock Grid.Column="0" Text=""  
                                           VerticalAlignment="Center"  Margin="20" 
                                           FontSize="50" Foreground="#E26441"/>
                                <StackPanel Margin="30 0 0 0" Width="150">
                                    <TextBlock Text="COMPANY NAME"/>
                                    <TextBlock Text="Age:">
                                        <Run Text="{Binding Age}"/>
                                    </TextBlock>
                                    <TextBlock Text="Money:">
                                        <Run Text="{Binding Money, StringFormat={}{0:C}}"/>
                                    </TextBlock>
                                    <TextBlock Text="Address:" TextWrapping="Wrap">
                                        <Run Text="{Binding Address}"/>
                                    </TextBlock>
                                </StackPanel>
                            </StackPanel>
                        </Grid>
                    </Border>
                    <DataTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="#7AAB7D" TargetName="border" />
                        </Trigger>
                    </DataTemplate.Triggers>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>    
</Grid>

 

ItemsPanelTemplate布局模板

ItemsPanelTemplate用于指定集合控件中元素与元素之间的布局的方式,所以,ItemsPanelTemplate其实就是一个布局面板,而我们在前面的章节中已经学习了WPF的面板控件,它们都继承于Panel基类,分别是Grid、UniformGrid、StackPanel、WrapPanel、DockPanel、Canvas等。而在使用ItemsPanelTemplate模板去设置某一个集合控件的元素布局面板时,默认使用StackPanel布局,或者WrapPanel。

例如在上的ItemsControl控件中,我们有4个元素,它们都是垂直排列的。我们可以修改ItemsPanel属性,用以设置元素以瀑布流的方式排列显示。

<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <WrapPanel/>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

此时ItemsControl中的元素便随着Window窗体大小的改变,而自适应水平排列其中的元素

<ListBox ItemsSource="{Binding Persons}" >
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border x:Name="border"
                    Width="280"
                    Height="200"
                    Margin="5"
                    BorderThickness="1" 
                    BorderBrush="Gray">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <StackPanel Grid.Row="0" Margin="20">
                        <TextBlock Text="{Binding Name}" FontWeight="Bold" FontSize="20"/>
                        <Rectangle Height="5"/>
                        <TextBlock Text="{Binding Occupation}" FontSize="16"/>
                    </StackPanel>
                    <StackPanel Grid.Row="1" Orientation="Horizontal">
                        <TextBlock Grid.Column="0" Text=""  
                                       VerticalAlignment="Center"  Margin="20" 
                                       FontSize="50" Foreground="#E26441"/>
                        <StackPanel Margin="30 0 0 0" Width="150">
                            <TextBlock Text="COMPANY NAME"/>
                            <TextBlock Text="Age:">
                                    <Run Text="{Binding Age}"/>
                            </TextBlock>
                            <TextBlock Text="Money:">
                                    <Run Text="{Binding Money, StringFormat={}{0:C}}"/>
                            </TextBlock>
                            <TextBlock Text="Address:" TextWrapping="Wrap">
                                    <Run Text="{Binding Address}"/>
                            </TextBlock>
                        </StackPanel>
                    </StackPanel>
                </Grid>
            </Border>
            <DataTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#7AAB7D" TargetName="border" />
                </Trigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ListBox>

 

评论