深入浅出WPF-11.Template(模板)01

模板

在WPF中,模板可以分为两大类:

  • 控件模板(ControlTemplate)是算法内容的表现形式,一个控件怎么组织其内部的结构才能让它更符合业务逻辑,让用户操作更舒服,都是由她控制的。它决定了控件长什么样子,并让程序员有机会在控件原有的内部逻辑基础上扩展自己的逻辑。

  • 数据模板(DataTemplate)是数据内容的表现形式,一条数据表现成什么样子,是简单的文本还是直观的图形动画就由他决定。

我们先了解一下数据模板,同样一条数据比如拥有类Student实例,具有如下就一个字段:

public class Student
    {
        /// <summary>
        /// 索引
        /// </summary>
        public int Id { get; set; }
        /// <summary>
        /// 姓名
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 性别
        /// </summary>
        public Sex Sex { get; set; }
        /// <summary>
        /// 出生日期
        /// </summary>
        public DateTime BirthDate { get; set; }
    }

    /// <summary>
    /// 性别
    /// </summary>
    public enum Sex : byte
    {
        /// <summary>
        /// 男
        /// </summary>
        Male = 0,
        /// <summary>
        /// 女
        /// </summary>
        Female = 1,
        /// <summary>
        /// 其他
        /// </summary>
        Other = 2,
    }

这样的内容,在不同的控件中展示,展示的形式会不一样,这种模式称之为 数据-视图 模式。在WPF中,我们可以使用自定义控件UserControl来实现,也可以使用数据模板DataTemplate实现。

DataTemplate常用在下面情况下:

  • ContentControl的ContentTemplate属性,相当与给ContentControl的内容穿衣服
  • ItemsControl 的ItemTemplate属性,相当远给ItemsControl的数据条目穿衣服
  • GridViewColumn的CellTemplate属性,相当于给GridViewColumn的单元格的数据穿衣服

我么你先看一下一个程序的展示效果

这里我们看一下代码实现:

<UserControl
    x:Class="LpbPrj.Client.Views.ResultWideAreaImagingView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:control="clr-namespace:LpbPrj.Client.Controls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:hc="https://handyorg.github.io/handycontrol"
    xmlns:langs="clr-namespace:LpbPrj.Client.Properties.Langs"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:prism="http://prismlibrary.com/">
    <ListBox
        Margin="0,0,0,0"
        control:CustomeSelectionItems.SelectedItems="{Binding ResultSelectedItems}"
        BorderThickness="0"
        ItemsPanel="{StaticResource FluidMoveBehaviorWrapPanelItemsPanelTemplate}"
        ItemsSource="{Binding ResultList}"
        SelectedItem="{Binding Result}"
        SelectionMode="{Binding SelectionMode}"
        Style="{StaticResource WrapPanelHorizontalListBox}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <ContentControl prism:RegionManager.RegionName="ItemWideAreaImaging" />
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</UserControl>

在这个ListBox中,我们对其中的ItemTemplate穿衣服,这个衣服的内容是:

<UserControl
    x:Class="LpbPrj.Client.Views.ItemWideAreaImaging"
    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:hc="https://handyorg.github.io/handycontrol"
    xmlns:langs="clr-namespace:LpbPrj.Client.Properties.Langs"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:prism="http://prismlibrary.com/">
    <hc:Card
        MaxWidth="240"
        Margin="8"
        BorderThickness="0"
        Effect="{StaticResource EffectShadow2}"
        Footer="{Binding}"
        Header="{Binding}">
        <hc:Card.HeaderTemplate>
            <DataTemplate>
                <TextBlock
                    Margin="5"
                    Style="{StaticResource TextBlockDefault}"
                    Text="{Binding ExamType, Converter={StaticResource DtoShowStringConverter}}" />
            </DataTemplate>
        </hc:Card.HeaderTemplate>
        <Border CornerRadius="4,4,0,0" Style="{StaticResource BorderClip}">
            <Grid>
                <Image Source="{Binding ImageThumbPath, Converter={StaticResource Path2BitmapImageConverter}}" Stretch="Uniform" />
            </Grid>
        </Border>
        <hc:Card.FooterTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <StackPanel
                        Grid.Column="0"
                        Margin="5"
                        Orientation="Horizontal">
                        <TextBlock
                            Style="{StaticResource TextBlockDefault}"
                            Text="{Binding ExamFileType, Converter={StaticResource DtoShowStringConverter}}"
                            TextTrimming="CharacterEllipsis"
                            TextWrapping="NoWrap" />
                        <TextBlock
                            Margin="10,0,0,0"
                            Style="{StaticResource TextBlockDefault}"
                            Text="{Binding ExamImageType, Converter={StaticResource ExamImageTypeConverter}}"
                            TextTrimming="CharacterEllipsis"
                            TextWrapping="NoWrap" />
                        <TextBlock
                            Margin="10,0,0,0"
                            Style="{StaticResource TextBlockDefault}"
                            Text="{Binding EyeType, Converter={StaticResource DtoShowStringConverter}}"
                            TextTrimming="CharacterEllipsis"
                            TextWrapping="NoWrap" />
                    </StackPanel>
                    <CheckBox
                        Grid.Column="1"
                        Width="30"
                        Height="30"
                        Margin="10,0,5,0"
                        IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
                </Grid>
            </DataTemplate>
        </hc:Card.FooterTemplate>
    </hc:Card>
</UserControl>

这段代码中的prism,我们暂且不管,但是主要内容还是能够理解的,我们使用了一个Card控件进行描述ListBox的展示内容,每个Card又通过TextBlock和CheckBox等控件进行描述。TextBlock中的对应的数据类型可能不能直接用于展示,比如枚举值,时间类型等。这里我们通过转化Converter来实现想要的展示形式。

这里面存在一个认识的误区,在上面的代码中,我们认为ListBox的Items属性里面存放的是数据而不是控件。我们可以直接将ListBox的选中属性SelectedItem绑定到一个对应的数据实例中。这种模式叫做数据驱动模式。和我们之前使用的事件驱动模式不一样。事件驱动是控件和控件之间沟通或者说是形式和形式之间的沟通,而数据驱动则是数据与控件之间的沟通,是内容和形式之间的沟通。

数据模板理解之后,我们再看一下控件模板ControlTemplate,我们可以使用披着羊皮的狼来理解控件模板,表面上看上去是羊,但是其实是狼。所以控件模板只是改变了控件的外形,不能改变控件的本质。对应控件外形的编辑我们推荐使用Blend。

我们先看一个简单的Lable标签:

<Style x:Key="LabelBaseStyle" TargetType="{x:Type Label}">
        <Style.Triggers>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="Opacity" Value="0.4" />
            </Trigger>
        </Style.Triggers>
        <Setter Property="Foreground" Value="{DynamicResource TextIconBrush}" />
        <Setter Property="Background" Value="{DynamicResource RegionBrush}" />
        <Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}" />
        <Setter Property="CornerRadius" Value="{StaticResource DefaultCornerRadius}" />
        <Setter Property="Padding" Value="{StaticResource DefaultControlPadding}" />
        <Setter Property="HorizontalContentAlignment" Value="Center" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Label}">
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true" CornerRadius="{Binding Path=(controls:BorderElement.CornerRadius), RelativeSource={RelativeSource TemplatedParent}}">
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

我们对Lable标签的ControlTemplate进行修改,里面包含了一个控件Border,Border内部包含了控件的内容ContentPresenter。Border的属性通过绑定设置了BorderBrush="{TemplateBinding BorderBrush}" ,代表Border的BorderBrush属性需要绑定到Label的BorderBrush属性,两者保持一致。

posted @ 2020-07-31 11:13  蜗牛的希望  阅读(965)  评论(0编辑  收藏  举报