silverlight-带水印的自定义TextBox控件(版本2)
之前那个版本《silverlight-带水印的TextBox》不得不说是相当失败的,起码我是这样理解的。其实我一心想把这个给实现了,但是不得不承认自身技术上的缺陷。经过一番尝试和折腾,有了下面这个版本。
两个版本的区别:
其实是有本质的区别的。
之前那个版本只能设置“文本水印”,相当有局限性。现在的版本是可以自定义水印内容的。譬如,可以为button,rectangle,ellipsed...
1.先新建一个“silverlight 模板化控件”,取名随意,此处为SuperText
2.修改继承的类Contol为TextBox
3.修改TextBox模板
控件SuperTextBox的模板,样式都是保存在一个叫“Generic.xaml”文件里面的。在第一次新建“silverlight 模板化控件”的时候,这个文件会自动产生。这里将TextBox的模板添加进入,并修改。
完整的
完整的Generic.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:TextBoxWaterMark"> <Style TargetType="local:SuperTextBox"> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Background" Value="#FFFFFFFF"/> <Setter Property="Foreground" Value="#FF000000"/> <Setter Property="Padding" Value="2"/> <Setter Property="BorderBrush"> <Setter.Value> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FFA3AEB9" Offset="0"/> <GradientStop Color="#FF8399A9" Offset="0.375"/> <GradientStop Color="#FF718597" Offset="0.375"/> <GradientStop Color="#FF617584" Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:SuperTextBox"> <Grid x:Name="RootElement"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"/> <VisualState x:Name="MouseOver"> <Storyboard> <ColorAnimation Storyboard.TargetName="MouseOverBorder" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" To="#FF99C1E2" Duration="0"/> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <DoubleAnimation Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/> </Storyboard> </VisualState> <VisualState x:Name="ReadOnly"> <Storyboard> <DoubleAnimation Storyboard.TargetName="ReadOnlyVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0" /> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="FocusStates"> <VisualState x:Name="Focused"> <Storyboard> <DoubleAnimation Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/> </Storyboard> </VisualState> <VisualState x:Name="Unfocused"> <Storyboard> <DoubleAnimation Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="ValidationStates"> <VisualState x:Name="Valid"/> <VisualState x:Name="InvalidUnfocused"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="InvalidFocused"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsOpen"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <sys:Boolean>True</sys:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="WatermarkDisplayed"> <VisualState x:Name="Hidden"> <Storyboard> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="watermarkContent" Storyboard.TargetProperty="(UIElement.Opacity)"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Shown"> <Storyboard> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="watermarkContent" Storyboard.TargetProperty="(UIElement.Opacity)"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="1" Opacity="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"> <Grid> <UserControl x:Name="watermarkContent" Content="{TemplateBinding Watermark}"/> <Border x:Name="ReadOnlyVisualElement" Opacity="0" Background="#5EC9C9C9"/> <Border x:Name="MouseOverBorder" BorderThickness="1" BorderBrush="Transparent"> <ScrollViewer x:Name="ContentElement" Padding="{TemplateBinding Padding}" BorderThickness="0" IsTabStop="False"/> </Border> </Grid> </Border> <Border x:Name="DisabledVisualElement" Background="#A5F7F7F7" BorderBrush="#A5F7F7F7" BorderThickness="{TemplateBinding BorderThickness}" Opacity="0" IsHitTestVisible="False"/> <Border x:Name="FocusVisualElement" BorderBrush="#FF6DBDD1" BorderThickness="{TemplateBinding BorderThickness}" Margin="1" Opacity="0" IsHitTestVisible="False"/> <Border x:Name="ValidationErrorElement" BorderThickness="1" CornerRadius="1" BorderBrush="#FFDB000C" Visibility="Collapsed"> <Grid Width="12" Height="12" HorizontalAlignment="Right" Margin="1,-4,-4,0" VerticalAlignment="Top" Background="Transparent"> <Path Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C"/> <Path Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff"/> </Grid> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
在上面的"Generic.xaml"文件的SuperTextBox模板中修改新增了一下内容
- 新增一个UserControl,即水印内容。
取名随意,这里为“watermarkContent”,并且绑定Content属性为“WaterMark”
<UserControl x:Name="watermarkContent" Content="{TemplateBinding Watermark}"/>
- 增加两个状态
状态为了控制水印是否出现。其实本质是调节水印的透明度。
<VisualStateGroup x:Name="WatermarkDisplayed"> <VisualState x:Name="Hidden"> <Storyboard> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="watermarkContent" Storyboard.TargetProperty="(UIElement.Opacity)"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Shown"> <Storyboard> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="watermarkContent" Storyboard.TargetProperty="(UIElement.Opacity)"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup>
4.在托管代码中
- 因为模板中绑定了一个新的属性WaterMark,所以需要在托管代码进行注册。并且用属性Watermark保存。
public object Watermark { get { return base.GetValue(WatermarkProperty) as object; } set { base.SetValue(WatermarkProperty, value); } } //向空间增加property public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark", typeof(object), typeof(SuperTextBox), new PropertyMetadata(null));
- 为了控制水印状态需要增加一个TextChanged事件。
public SuperTextBox() { this.DefaultStyleKey = typeof(SuperTextBox); TextChanged += new TextChangedEventHandler(SuperTextBox_TextChanged); } void SuperTextBox_TextChanged(object sender, TextChangedEventArgs e) { TextBox tb = (TextBox)sender; if (tb.Text.Length > 0) { System.Windows.VisualStateManager.GoToState(tb, "Hidden", false); } else { System.Windows.VisualStateManager.GoToState(tb, "Shown", false); } }
- 完整托管代码
完整的SuperTextBox托管代码
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Collections.ObjectModel; namespace TextBoxWaterMark { public class SuperTextBox : TextBox { public object Watermark { get { return base.GetValue(WatermarkProperty) as object; } set { base.SetValue(WatermarkProperty, value); } } //向空间增加property 瓶颈所在 public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark", typeof(object), typeof(SuperTextBox), new PropertyMetadata(null)); public SuperTextBox() { this.DefaultStyleKey = typeof(SuperTextBox); TextChanged += new TextChangedEventHandler(SuperTextBox_TextChanged); } void SuperTextBox_TextChanged(object sender, TextChangedEventArgs e) { TextBox tb = (TextBox)sender; if (tb.Text.Length > 0) { System.Windows.VisualStateManager.GoToState(tb, "Hidden", false); } else { System.Windows.VisualStateManager.GoToState(tb, "Shown", false); } } public override void OnApplyTemplate() { base.OnApplyTemplate(); } } }
5.调用
<local:SuperTextBox Height="20"> <local:SuperTextBox.Watermark> <StackPanel Orientation="Horizontal" Opacity="0.4"> <Rectangle Fill="Red" Width="10" Height="10"/> <TextBlock Text="我是一个水印"/> </StackPanel> </local:SuperTextBox.Watermark> </local:SuperTextBox>
需加上命名空间local,local指向的是SuperTextBox所在的命名空间。
6.效果
参考资料:《[控件问题] TextBox控件的Watermark问题变通解决方法》
《silverlight-带水印的TextBox》 人生苦短,虚心求教
作者:Ron Ngai
出处:http://rondsny.github.io
关于作者:断码码农一枚。
欢迎转载,但未经作者同意须在文章页面明显位置给出原文连接
如有问题,可以通过rondsny#gmail.com 联系我,非常感谢。