1.首先是采取自定义控件还是用户控件开发,由于水印控件跟一般的TextBox区别无非就是增加了水印文本属性,故采取自定义控件,虽然用户控件可能简单一些...
贴一段源码:
View Code
1 public class WatermarkedTextBox : Control 2 { 3 // Fields 4 private bool _hasFocus; 5 private bool _hasText; 6 private TextBox _text; 7 public static readonly DependencyProperty InputScopeProperty = DependencyProperty.Register("InputScope", typeof(InputScope), typeof(WatermarkedTextBox), new PropertyMetadata(null)); 8 9 public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(WatermarkedTextBox), new PropertyMetadata(string.Empty)); 10 public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register("TextWrapping", typeof(TextWrapping), typeof(WatermarkedTextBox), new PropertyMetadata((TextWrapping)1)); 11 public static readonly DependencyProperty WatermarkBrushProperty = DependencyProperty.Register("WatermarkBrush", typeof(Brush), typeof(WatermarkedTextBox), new PropertyMetadata(null)); 12 public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark", typeof(string), typeof(WatermarkedTextBox), new PropertyMetadata(null)); 13 14 // Events 15 public event TextChangedEventHandler TextChanged; 16 17 // Methods 18 public WatermarkedTextBox() 19 { 20 base.DefaultStyleKey = typeof(WatermarkedTextBox); 21 } 22 23 public override void OnApplyTemplate() 24 { 25 if (this._text != null) 26 { 27 this._text.GotFocus -= new RoutedEventHandler(this.OnGotFocus); 28 this._text.LostFocus -= new RoutedEventHandler(this.OnLostFocus); 29 this._text.TextChanged -= new TextChangedEventHandler(this.OnTextChanged); 30 } 31 base.OnApplyTemplate(); 32 this._text = base.GetTemplateChild("_text") as TextBox; 33 if (this._text != null) 34 { 35 this._text.GotFocus += new RoutedEventHandler(this.OnGotFocus); 36 this._text.LostFocus += new RoutedEventHandler(this.OnLostFocus); 37 this._text.TextChanged += new TextChangedEventHandler(this.OnTextChanged); 38 } 39 this.UpdateVisualStates(false); 40 } 41 42 private void OnGotFocus(object sender, RoutedEventArgs e) 43 { 44 this._hasFocus = true; 45 this.UpdateVisualStates(true); 46 } 47 48 private void OnLostFocus(object sender, RoutedEventArgs e) 49 { 50 this._hasFocus = false; 51 this.UpdateVisualStates(true); 52 } 53 54 private void OnTextChanged(object sender, TextChangedEventArgs e) 55 { 56 this._hasText = !string.IsNullOrEmpty(this._text.Text); 57 this.UpdateVisualStates(true); 58 if (this.Text != this._text.Text) 59 { 60 this.Text = this._text.Text; 61 } 62 TextChangedEventHandler textChanged = this.TextChanged; 63 if (textChanged != null) 64 { 65 textChanged.Invoke(this._text, e); 66 } 67 } 68 69 private void UpdateVisualStates(bool useTransitions) 70 { 71 VisualStateManager.GoToState(this, this._hasText ? "Normal" : "Watermarked", useTransitions); 72 VisualStateManager.GoToState(this, this._hasFocus ? "Focused" : "Unfocused", useTransitions); 73 } 74 75 // Properties 76 public InputScope InputScope 77 { 78 get 79 { 80 return (InputScope)base.GetValue(InputScopeProperty); 81 } 82 set 83 { 84 base.SetValue(InputScopeProperty, value); 85 } 86 } 87 88 public string Text 89 { 90 get 91 { 92 return (base.GetValue(TextProperty) as string); 93 } 94 set 95 { 96 base.SetValue(TextProperty, value); 97 } 98 } 99 100 public TextWrapping TextWrapping 101 { 102 get 103 { 104 return (TextWrapping)base.GetValue(TextWrappingProperty); 105 } 106 set 107 { 108 base.SetValue(TextWrappingProperty, value); 109 } 110 } 111 112 public string Watermark 113 { 114 get 115 { 116 return (base.GetValue(WatermarkProperty) as string); 117 } 118 set 119 { 120 base.SetValue(WatermarkProperty, value); 121 } 122 } 123 124 public Brush WatermarkBrush 125 { 126 get 127 { 128 return (Brush)base.GetValue(WatermarkBrushProperty); 129 } 130 set 131 { 132 base.SetValue(WatermarkBrushProperty, value); 133 } 134 } 135 }
该水印文本框控件具有水印文本,水印文本换行,水印画刷,虚拟键盘输入范围,文本框文本几个属性,为了能够在MVVM架构下进行数据绑定,我们把这几个属性定义为依赖属性;
注意这段代码:base.DefaultStyleKey = typeof(WatermarkedTextBox);一般从Control派生出来的控件都要设置其默认样式,即控件外观;一般是在项目文件夹Theme/generic.xaml这个文件进行外观定义,该水印文本框控件外观定义XAML源码如下:
View Code
1 <Style TargetType="Controls:WatermarkedTextBox" > 2 <Setter Property="WatermarkBrush" Value="{StaticResource PhoneTextBoxReadOnlyBrush}"/> 3 <Setter Property="Watermark" Value="enter text" /> 4 <Setter Property="Template"> 5 <Setter.Value> 6 <ControlTemplate TargetType="Controls:WatermarkedTextBox"> 7 <Grid x:Name="Root"> 8 <VisualStateManager.VisualStateGroups> 9 <VisualStateGroup x:Name="WatermarkStates"> 10 <VisualStateGroup.Transitions> 11 <VisualTransition GeneratedDuration="00:00:00.2" /> 12 </VisualStateGroup.Transitions> 13 <VisualState x:Name="Normal"> 14 <Storyboard> 15 <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" 16 Storyboard.TargetName="_watermarkGrid" 17 Storyboard.TargetProperty="(UIElement.Opacity)"> 18 <LinearDoubleKeyFrame KeyTime="00:00:00" Value="0" /> 19 </DoubleAnimationUsingKeyFrames> 20 </Storyboard> 21 </VisualState> 22 <VisualState x:Name="Watermarked"> 23 <Storyboard> 24 <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" 25 Storyboard.TargetName="_watermarkGrid" 26 Storyboard.TargetProperty="(UIElement.Opacity)"> 27 <LinearDoubleKeyFrame KeyTime="00:00:00" Value="0.9" /> 28 </DoubleAnimationUsingKeyFrames> 29 </Storyboard> 30 </VisualState> 31 </VisualStateGroup> 32 <VisualStateGroup x:Name="FocusStates"> 33 <VisualStateGroup.Transitions> 34 <VisualTransition GeneratedDuration="0" /> 35 <VisualTransition To="Focused" GeneratedDuration="0:0:0.1" /> 36 </VisualStateGroup.Transitions> 37 <VisualState x:Name="Unfocused" /> 38 <VisualState x:Name="Focused"> 39 <Storyboard> 40 <DoubleAnimation To="0.0" 41 Storyboard.TargetProperty="(UIElement.Opacity)" 42 Storyboard.TargetName="_watermark"/> 43 </Storyboard> 44 </VisualState> 45 </VisualStateGroup> 46 </VisualStateManager.VisualStateGroups> 47 <TextBox x:Name="_text" 48 Text="{TemplateBinding Text}" 49 TextWrapping="{TemplateBinding TextWrapping}" 50 InputScope="{TemplateBinding InputScope}" 51 /> 52 <Grid x:Name="_watermarkGrid" Margin="6,6,0,0"> 53 <TextBlock 54 Margin="{StaticResource PhoneTouchTargetOverhang}" 55 Style="{StaticResource PhoneTextNormalStyle}" 56 FontFamily="{StaticResource PhoneFontFamilyNormal}" 57 FontSize="{StaticResource PhoneFontSizeMediumLarge}" 58 x:Name="_watermark" 59 IsHitTestVisible="False" 60 TextWrapping="Wrap" 61 Foreground="{TemplateBinding WatermarkBrush}" 62 Text="{TemplateBinding Watermark}" 63 VerticalAlignment="Top" 64 HorizontalAlignment="Left" /> 65 </Grid> 66 </Grid> 67 </ControlTemplate> 68 </Setter.Value> 69 </Setter> 70 </Style>
该控件的控件模板设计思路是:水印文本用TextBlock定义(用只有一个单元格的Grid进行包裹),默认文本框还是TextBox定义,最后把这两个控件用只含有一个单元格的Grid进行包裹;然后就是定义其视觉状态组,在这个控件里我们只关注水印状态视觉组和Focus状态视觉组;
定义好控件外观后,我们需要设置其事件,套用外观模板,我们需要重写Control控件的OnApplyTemplate方法,源码如下:
View Code
1 public override void OnApplyTemplate() 2 { 3 if (this._text != null) 4 { 5 this._text.GotFocus -= new RoutedEventHandler(this.OnGotFocus); 6 this._text.LostFocus -= new RoutedEventHandler(this.OnLostFocus); 7 this._text.TextChanged -= new TextChangedEventHandler(this.OnTextChanged); 8 } 9 base.OnApplyTemplate(); 10 this._text = base.GetTemplateChild("_text") as TextBox; 11 if (this._text != null) 12 { 13 this._text.GotFocus += new RoutedEventHandler(this.OnGotFocus); 14 this._text.LostFocus += new RoutedEventHandler(this.OnLostFocus); 15 this._text.TextChanged += new TextChangedEventHandler(this.OnTextChanged); 16 } 17 this.UpdateVisualStates(false); 18 }
然后贴各种事件和视觉状态更新方法源码如下:
View Code
1 private void OnGotFocus(object sender, RoutedEventArgs e) 2 { 3 this._hasFocus = true; 4 this.UpdateVisualStates(true); 5 } 6 7 private void OnLostFocus(object sender, RoutedEventArgs e) 8 { 9 this._hasFocus = false; 10 this.UpdateVisualStates(true); 11 } 12 13 private void OnTextChanged(object sender, TextChangedEventArgs e) 14 { 15 this._hasText = !string.IsNullOrEmpty(this._text.Text); 16 this.UpdateVisualStates(true); 17 if (this.Text != this._text.Text) 18 { 19 this.Text = this._text.Text; 20 } 21 TextChangedEventHandler textChanged = this.TextChanged; 22 if (textChanged != null) 23 { 24 textChanged.Invoke(this._text, e); 25 } 26 } 27 28 private void UpdateVisualStates(bool useTransitions) 29 { 30 VisualStateManager.GoToState(this, this._hasText ? "Normal" : "Watermarked", useTransitions); 31 VisualStateManager.GoToState(this, this._hasFocus ? "Focused" : "Unfocused", useTransitions); 32 }