WPF使用ControlTemplate自定义样式的TextBox

2,666 阅读2分钟

为建立中文知识库加块砖        ——中科大胡不归

问题描述

WPF 可以轻松通过 ControlTemplate 实现自定义样式的 TextBox ,比如非输入框外观、鼠标悬浮着色等效果。

背景知识

WPF 包含数据模板和控件模板,其中控件模板又包括 ControlTemplate 和 ItemsPanelTemplate ,这里讨论一下 ControlTemplate 。

其实 WPF 的每一个控件都有一个默认的模板,该模板描述了控件的外观以及外观对外界刺激所做出的反应。我们可以自定义一个模板来替换掉控件的默认模板以便打造个性化的控件。

与 Style 不同,Style 只能改变控件的已有属性值(比如颜色字体)来定制控件,但控件模板可以改变控件的内部结构( VisualTree ,视觉树)来完成更为复杂的定制,比如我们可以定制这样的按钮:在它的左办部分显示一个小图标而它的右半部分显示文本。

要替换控件的模板,我们只需要声明一个 ControlTemplate 对象,并对该 ControlTemplate 对象做相应的配置,然后将该ControlTemplate 对象赋值给控件的 Template 属性就可以了。

ControlTemplate 包含两个重要的属性:

  1. VisualTree 该模板的视觉树,其实我们就是使用这个属性来描述控件的外观的

  2. Triggers 触发器列表,里面包含一些触发器 Trigger ,我们可以定制这个触发器列表来使控件对外界的刺激发生反应,比如鼠标经过时文本变成粗体等。

代码示例

  1. 在工程中添加一个 Styles.xaml 文件:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <ControlTemplate x:Key="SimpleTextBoxTemplate" TargetType="{x:Type TextBox}">
        <Grid SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
              HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
              VerticalAlignment="{TemplateBinding VerticalAlignment}"
              Margin="0">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualStateGroup.Transitions>
                        <VisualTransition GeneratedDuration="0:0:0.1"/>
                    </VisualStateGroup.Transitions>
                    <VisualState x:Name="Normal"/>
                    <VisualState x:Name="MouseOver">
                        <Storyboard>
                            <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(TextBox.Foreground).(SolidColorBrush.Color)">
                                <EasingColorKeyFrame KeyTime="0" Value="{DynamicResource MouseOverColor}" />
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="Disabled"/>
                    <VisualState x:Name="ReadOnly"/>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <Border x:Name="NormalVisual" Opacity="1"
                           BorderThickness="{TemplateBinding BorderThickness}"
                           BorderBrush="{TemplateBinding BorderBrush}"
                           Background="{TemplateBinding Background}"/>
            <ScrollViewer x:Name="PART_ContentHost"
                                  BorderThickness="0"
                                  IsTabStop="False"
                                  Margin="{TemplateBinding Padding}"
                                  VerticalAlignment="Stretch"
                                  VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                  Background="{x:Null}"
                                  HorizontalAlignment="Stretch"
                                  HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
        </Grid>
    </ControlTemplate>

    <Style x:Key="SimpleTextBox" TargetType="{x:Type TextBox}">
        <Setter Property="SnapsToDevicePixels" Value="True"/>
        <Setter Property="OverridesDefaultStyle" Value="True"/>
        <Setter Property="Width" Value="120"/>
        <Setter Property="Margin" Value="2"/>
        <Setter Property="BorderBrush" Value="Black"/>
        <Setter Property="BorderThickness" Value="0,0,0,1"/>
        <Setter Property="HorizontalContentAlignment" Value="Left" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="Foreground">
            <Setter.Value>
                <SolidColorBrush Color="{DynamicResource NormalColor}"/>
            </Setter.Value>
        </Setter>
        <Setter Property="Template" Value="{StaticResource SimpleTextBoxTemplate}"/>
    </Style>

</ResourceDictionary>

其中定义了 ControlTemplate, 目标是 TextBox。

  1. 在工程配置文件 App.xaml 中添加如下语句以使 如上的Styles.xaml中定义的资源生效:
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Colors.xaml"/>
                <ResourceDictionary Source="Styles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
  1. 在代码中引用
<Window x:Class="Kavand.WpfTextBoxStyle.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
        <TextBlock Margin="5,0,0, 5">风雨无阻</TextBlock>
        <TextBox Style="{DynamicResource SimpleTextBox}" Text="My text..."/>
    </StackPanel>
</Window>
  1. 效果如下

参考文章

  1. source code
  2. 请教WPF高手,如何使ControlTemplate内的元素自动适应整个控件的大小?
  3. WPF中的模板(三)- ControlTemplate和DataTemplate的应用
  4. WPF中的ControlTemplate( 控件模板 )