我们经常能在很多设计中看到当文本框为空的时候显示灰色斜体的提示性文字,这样可以省略文本框前的标签说明,同时也会使设计变得美观。在html中我们只需要给input加上placeholder就行了,那么在wpf中怎么做呢?
首先我继承System.Windows.Controls.TextBox实现WatermarkTextBox的类,并添加一个Watermark的string属性,用于设置水印文字。
WatermarkTextBox.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace Tenshi.Common.Wpf.Controls
{
public class WatermarkTextBox : TextBox
{
private string watermark = string.Empty;
public string Watermark
{
get { return watermark; }
set { watermark = value ?? string.Empty; }
}
public static readonly DependencyProperty WatermarkProperty =
DependencyProperty.Register("Watermark", typeof(string), typeof(WatermarkTextBox));
static WatermarkTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(WatermarkTextBox),
new FrameworkPropertyMetadata(typeof(WatermarkTextBox)));
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
if (this.Text == string.Empty)
{
VisualStateManager.GoToState(this, "Empty", true);
}
else
{
VisualStateManager.GoToState(this, "NotEmpty", true);
}
}
}
}
这里为WatermarkTextBox添加了两种VisualState,文本为空时进入Empty状态,非空进入NotEmpty状态。
TextStates.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tenshi.Common.Wpf.Controls
{
public enum TextStates
{
/// <summary>
/// 内容为空
/// </summary>
Empty,
/// <summary>
/// 内容不为空
/// </summary>
NotEmpty
}
}
接下来在xaml中设置WatermarkTextBox的Template
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Tenshi.Common.Wpf.Controls">
<ControlTemplate x:Key="WatermarkTextBoxTemplate" TargetType="local:WatermarkTextBox">
<Grid>
<TextBlock x:Name="WatermarkText" Text="{TemplateBinding Watermark}" FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}"
FontStyle="Italic"
Foreground="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"
Opacity="1.0"
Margin="{TemplateBinding Padding}"/>
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="TextStates">
<VisualState x:Name="NotEmpty">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="WatermarkText"
Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame KeyTime="0" Value="0.0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Empty">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="WatermarkText"
Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame KeyTime="0" Value="1.0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#FF569DE5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style TargetType="local:WatermarkTextBox">
<Setter Property="Template" Value="{StaticResource WatermarkTextBoxTemplate}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="#FF007ACC"/>
</Style>
</ResourceDictionary>
大家可以看到所谓的水印就是我加进去的一个叫WatermarkText的TextBlock,当进入Empty状态时,我让他的Opacity = 1.0,这时候就会显示水印;当进入NotEmpty状态时,他的Opacity = 0.0,这时候水印就会隐藏。
最后配一张效果图: