欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

WindowsPhone自定义控件详解(三) - 实战:自定义带水印的PasswordBox控件,Watermar

程序员文章站 2022-03-08 16:04:42
声明:这个控件是在WatermarkedTextBox的基础上改的。 从WP7-WatermarkedTextBox-custom-control">http://www.win...

声明:这个控件是在WatermarkedTextBox的基础上改的。

WP7-WatermarkedTextBox-custom-control">http://www.windowsphonegeek.com/articles/WP7-WatermarkedTextBox-custom-control上下载WatermarkedTextBox控件


原理分析:

在PasswordBox后面加水印和在TextBox后面加水印差不多,有以下要求:

PasswordBox里没内容时,显示水印
PasswordBox里有内容时,不显示水印,而显示内容
最简单的想法就是,在PasswordBox控件后面加上一个类似TextBlock的控件,然后重写焦点回调方法,当有焦点时,水印不显示,无焦点时,根据是否有内容而决定是否显示水印。

思路如上,下面开始分析WatermarkedTextBox的代码,看看它的作者是不是和我们想法一样。

 

 

一、 分析WatermarkedTextBox代码

 

themes/generic.xaml
自定义控件的样式文件必须要以generic.xaml命名,放到themes目录中。

[html] <ControlTemplate x:Key="PhoneDisabledTextBoxTemplate" TargetType="TextBox"> 
    <ContentControl x:Name="ContentElement" BorderThickness="0" HorizontalContentAlignment="Stretch" Margin="{StaticResource PhoneTextBoxInnerMargin}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="Stretch"/> 
</ControlTemplate> 
        <Style  TargetType="local:WatermarkedTextBox"> 
   <Setter Property="Template"> 
    <Setter.Value> 
        <ControlTemplate TargetType="local:WatermarkedTextBox"> 
            <Grid Background="Transparent"> 
                <Border x:Name="EnabledBorder"> 
                    <Grid> 
                        <ContentControl x:Name="watermarkContent" Style="{TemplateBinding WatermarkStyle}" Content="{TemplateBinding Watermark}" Background="Transparent" Opacity="0.5"/> 
                        <ContentControl x:Name="ContentElement" BorderThickness="0"  VerticalContentAlignment="Stretch"/> 
                    </Grid> 
                </Border> 
                <Border x:Name="DisabledOrReadonlyBorder" Background="Transparent"  Visibility="Collapsed"> 
                    <TextBox x:Name="DisabledOrReadonlyContent" Background="Transparent" IsReadOnly="True" Text="{TemplateBinding Text}"  Template="{StaticResource PhoneDisabledTextBoxTemplate}"/> 
                    </Border> 
                </Grid> 
            </ControlTemplate> 
        </Setter.Value> 
    </Setter> 
</Style> 
 <ControlTemplate x:Key="PhoneDisabledTextBoxTemplate" TargetType="TextBox">
  <ContentControl x:Name="ContentElement" BorderThickness="0" HorizontalContentAlignment="Stretch" Margin="{StaticResource PhoneTextBoxInnerMargin}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="Stretch"/>
 </ControlTemplate>
         <Style  TargetType="local:WatermarkedTextBox">
    <Setter Property="Template">
  <Setter.Value>
   <ControlTemplate TargetType="local:WatermarkedTextBox">
    <Grid Background="Transparent">
     <Border x:Name="EnabledBorder">
      <Grid>
       <ContentControl x:Name="watermarkContent" Style="{TemplateBinding WatermarkStyle}" Content="{TemplateBinding Watermark}" Background="Transparent" Opacity="0.5"/>
       <ContentControl x:Name="ContentElement" BorderThickness="0"  VerticalContentAlignment="Stretch"/>
      </Grid>
     </Border>
     <Border x:Name="DisabledOrReadonlyBorder" Background="Transparent"  Visibility="Collapsed">
      <TextBox x:Name="DisabledOrReadonlyContent" Background="Transparent" IsReadOnly="True" Text="{TemplateBinding Text}"  Template="{StaticResource PhoneDisabledTextBoxTemplate}"/>
      </Border>
     </Grid>
    </ControlTemplate>
   </Setter.Value>
  </Setter>
 </Style>
注:上面省略了一部分无关紧要代码。
http://blog.csdn.net/mr_raptor/article/details/7251992

上面的XAML文件,定义了WatermarkedTextBox的样式:

文件开始,定义了一个ControlTemplate元素,由前两节知识可知,它是要对一个控件设置模板,从后面的TargetType="TextBox"可知,是针对TextBox类型控件,它里面自定义了一个ContentControl类名字为ContentElement。
通过TargetType="local:WatermarkedTextBox" 可知,它是针对WatermarkedTextBox的控件样式。
在EnabledBorder里定义了两个内容控件元素:watermarkContent和ContentElement,watermarkContent里,绑定了依赖属性Watermark(在WatermarkTextBox.cs里声明)。
在下面的DisabledOrReadonlyBorder里面是一个TextBox 控件,它绑定了WatermarkedTextBox里的Text属性,同时这个TextBox 控件,的模板设置为:PhoneDisabledTextBoxTemplate,它这么做的目的是,可以设置WatermarkedTextBox的属性为Disabled,这时,水印就消失了,而显示原先的TextBox控件。
       2. WatermarkTextBox.cs

[csharp] namespace WatermarkedTextBoxControl 

    public class WatermarkedTextBox : TextBox 
    { 
        ContentControl WatermarkContent; 
        public static readonly DependencyProperty WatermarkProperty = 
      DependencyProperty.Register("Watermark", typeof(object), typeof(WatermarkedTextBox), new PropertyMetadata(OnWatermarkPropertyChanged)); 
  
        public static readonly DependencyProperty WatermarkStyleProperty = 
      DependencyProperty.Register("WatermarkStyle", typeof(Style), typeof(WatermarkedTextBox), null); 
  
        public Style WatermarkStyle 
        { 
            get { return base.GetValue(WatermarkStyleProperty) as Style; } 
            set { base.SetValue(WatermarkStyleProperty, value); } 
        } 
  
        public object Watermark 
        { 
            get { return base.GetValue(WatermarkProperty) as object; } 
            set { base.SetValue(WatermarkProperty, value); } 
        } 
  
        public WatermarkedTextBox() 
        { 
            DefaultStyleKey = typeof(WatermarkedTextBox); 
        } 
  
        public override void OnApplyTemplate() 
        { 
            base.OnApplyTemplate(); 
            this.WatermarkContent = this.GetTemplateChild("watermarkContent") as ContentControl; 
            if(WatermarkContent != null) 
            { 
              DetermineWatermarkContentVisibility(); 
            } 
        } 
  
        protected override void OnGotFocus(RoutedEventArgs e) 
        { 
            if (WatermarkContent != null && string.IsNullOrEmpty(this.Text)) 
            { 
                this.WatermarkContent.Visibility = Visibility.Collapsed; 
            } 
            base.OnGotFocus(e); 
        } 
  
        protected override void OnLostFocus(RoutedEventArgs e) 
        { 
            if (WatermarkContent != null && string.IsNullOrEmpty(this.Text)) 
            { 
                this.WatermarkContent.Visibility = Visibility.Visible; 
            } 
            base.OnLostFocus(e); 
        } 
  
        private static void OnWatermarkPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 
        { 
            WatermarkedTextBox watermarkTextBox = sender as WatermarkedTextBox; 
            if(watermarkTextBox != null && watermarkTextBox.WatermarkContent !=null) 
            { 
              watermarkTextBox.DetermineWatermarkContentVisibility(); 
            } 
        } 
  
        private void DetermineWatermarkContentVisibility() 
        { 
            if (string.IsNullOrEmpty(this.Text)) 
            { 
                this.WatermarkContent.Visibility = Visibility.Visible; 
            } 
            else 
            { 
                this.WatermarkContent.Visibility = Visibility.Collapsed; 
            } 
        } 
    } 
    } 

namespace WatermarkedTextBoxControl
{
 public class WatermarkedTextBox : TextBox
 {
  ContentControl WatermarkContent;
  public static readonly DependencyProperty WatermarkProperty =
   DependencyProperty.Register("Watermark", typeof(object), typeof(WatermarkedTextBox), new PropertyMetadata(OnWatermarkPropertyChanged));
 
  public static readonly DependencyProperty WatermarkStyleProperty =
   DependencyProperty.Register("WatermarkStyle", typeof(Style), typeof(WatermarkedTextBox), null);
 
  public Style WatermarkStyle
  {
   get { return base.GetValue(WatermarkStyleProperty) as Style; }
   set { base.SetValue(WatermarkStyleProperty, value); }
  }
 
  public object Watermark
  {
   get { return base.GetValue(WatermarkProperty) as object; }
   set { base.SetValue(WatermarkProperty, value); }
  }
 
  public WatermarkedTextBox()
  {
   DefaultStyleKey = typeof(WatermarkedTextBox);
  }
 
  public override void OnApplyTemplate()
  {
   base.OnApplyTemplate();
   this.WatermarkContent = this.GetTemplateChild("watermarkContent") as ContentControl;
   if(WatermarkContent != null)
   {
     DetermineWatermarkContentVisibility();
   }
  }
 
  protected override void OnGotFocus(RoutedEventArgs e)
  {
   if (WatermarkContent != null && string.IsNullOrEmpty(this.Text))
   {
    this.WatermarkContent.Visibility = Visibility.Collapsed;
   }
   base.OnGotFocus(e);
  }
 
  protected override void OnLostFocus(RoutedEventArgs e)
  {
   if (WatermarkContent != null && string.IsNullOrEmpty(this.Text))
   {
    this.WatermarkContent.Visibility = Visibility.Visible;
   }
   base.OnLostFocus(e);
  }
 
  private static void OnWatermarkPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
  {
   WatermarkedTextBox watermarkTextBox = sender as WatermarkedTextBox;
   if(watermarkTextBox != null && watermarkTextBox.WatermarkContent !=null)
   {
     watermarkTextBox.DetermineWatermarkContentVisibility();
   }
  }
 
  private void DetermineWatermarkContentVisibility()
  {
   if (string.IsNullOrEmpty(this.Text))
   {
    this.WatermarkContent.Visibility = Visibility.Visible;
   }
   else
   {
    this.WatermarkContent.Visibility = Visibility.Collapsed;
   }
  }
 }
    }
}
 
原创地址:http://blog.csdn.net/mr_raptor/article/details/7251992
由这个WatermarkedTextBox类可知:

它继承了TextBox类
增加了Watermark和WatermarkStyle两个依赖属性,用于用户设置它的水印内容和样式,在Watermark属性里添加了属性改变事件:OnWatermarkPropertyChanged
重载了OnApplyTemplate方法来取得generic.xaml文件里声明的元素引用:watermarkContent,并且根据generic.xaml里的TextBox :DisabledOrReadonlyContent,取得它里面是否有内容,如果有内容,则WatermarkContent不可见,否则WatermarkContent可见。
重载了OnGotFocus,OnLostFocus,当该自定义控件得到焦点时,设置WatermarkContent不可见,否则WatermarkContent可见。
由上面的分析可知,当用户设置了自定义控件的Watermark属性时,回调注册的OnWatermarkPropertyChanged方法,在该方法里,判断是否WatermarkContent里有内容,如果有,WatermarkContent不可见,否则WatermarkContent可见。两样,重载了OnGotFocus,OnLostFocus,在得到和失去焦点时也要判断是否将WatermarkContent设置为可见与否。

 

二、 自定义WatermarkedPasswordBox

  根据前面的分析,我们可以试着做以下修改:

新建类WatermarkedPasswordBox
将WatermarkTextBox.cs拷贝到类WatermarkedPasswordBox里,改下类名,让WatermarkedPasswordBox继承了Password类
在themes/generic.xaml里,拷贝 <Style  TargetType="local:WatermarkedPasswordBox">里的全部代码,改为WatermarkedPasswordBox的代码,中间细节自己改就行了,我们不打算支持Disabled属性,所以DisabledOrReadonlyBorder去掉就行了,将EnabledBorder里的ContentElement去掉,换成PasswordBox,名字还是ContentElement
 

编译时,错误出现了:WatermarkedPasswordBox里this.Text出错,这是因为Password没有Text属性,它有个Password属性,所以要做下面的修改:

让WatermarkedPasswordBox类继承TextBox,但是添加一个属性:PasswordBox类型的PasswordContent
在OnApplyTemplate方法里,获得自己加的PasswordBox控件的引用ContentElement,为PasswordBox控件添加PasswordChanged事件,当密码框里内容改变时,将TextBox的Text属性的值为PasswordBox.Password的值
同样,在XAML样式文件里,在PasswordBox控件里加上 Password="{TemplateBinding Text}"
修改后的代码如下:

XAML:

[html] <Style  TargetType="local:WatermarkedPasswordBox"> 
       <Setter Property="Template"> 
           <Setter.Value> 
               <ControlTemplate TargetType="local:WatermarkedPasswordBox"> 
                   <Grid Background="Transparent"> 
                       <Border x:Name="EnabledBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="{StaticResource PhoneTouchTargetOverhang}"> 
                           <Grid> 
                               <ContentControl x:Name="watermarkContent" Style="{TemplateBinding WatermarkStyle}" Content="{TemplateBinding Watermark}" Background="Transparent" Opacity="0.5"/> 
                               <PasswordBox x:Name="ContentElement" Background="Transparent" 
                Password="{TemplateBinding Text}"  
                                    BorderThickness="0" HorizontalContentAlignment="Stretch" 
                                    Margin="-12,-12,-12,-12"  
                                    VerticalContentAlignment="Stretch"/> 
                           </Grid> 
                       </Border> 
                   </Grid> 
               </ControlTemplate> 
           </Setter.Value> 
       </Setter> 
   </Style> 
 <Style  TargetType="local:WatermarkedPasswordBox">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:WatermarkedPasswordBox">
                    <Grid Background="Transparent">
                        <Border x:Name="EnabledBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="{StaticResource PhoneTouchTargetOverhang}">
                            <Grid>
                                <ContentControl x:Name="watermarkContent" Style="{TemplateBinding WatermarkStyle}" Content="{TemplateBinding Watermark}" Background="Transparent" Opacity="0.5"/>
                                <PasswordBox x:Name="ContentElement" Background="Transparent"
     Password="{TemplateBinding Text}"
                                     BorderThickness="0" HorizontalContentAlignment="Stretch"
                                     Margin="-12,-12,-12,-12"
                                     VerticalContentAlignment="Stretch"/>
                            </Grid>
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
原创地址:http://blog.csdn.net/mr_raptor/article/details/7251992
C#:

[html] public class WatermarkedPasswordBox : TextBox 
   { 
       ContentControl WatermarkContent; 
       private PasswordBox PasswordContent; 
 
       public object Watermark 
       { 
           get { return base.GetValue(WatermarkProperty) as object; } 
           set { base.SetValue(WatermarkProperty, value); } 
       } 
       public static readonly DependencyProperty WatermarkProperty = 
       DependencyProperty.Register("Watermark", typeof(object), typeof(WatermarkedPasswordBox), new PropertyMetadata(OnWatermarkPropertyChanged)); 
 
       private static void OnWatermarkPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 
       { 
           WatermarkedPasswordBox watermarkTextBox = sender as WatermarkedPasswordBox; 
           if (watermarkTextBox != null && watermarkTextBox.WatermarkContent != null) 
           { 
               watermarkTextBox.DetermineWatermarkContentVisibility(); 
           } 
 
           Debug.WriteLine("OnWatermarkPropertyChanged"); 
       } 
 
       public Style WatermarkStyle 
       { 
           get { return base.GetValue(WatermarkStyleProperty) as Style; } 
           set { base.SetValue(WatermarkStyleProperty, value); } 
       } 
       public static readonly DependencyProperty WatermarkStyleProperty = 
       DependencyProperty.Register("WatermarkStyle", typeof(Style), typeof(WatermarkedPasswordBox), null); 
 
       public WatermarkedPasswordBox() 
       { 
           DefaultStyleKey = typeof(WatermarkedPasswordBox); 
       } 
 
       public override void OnApplyTemplate() 
       { 
           base.OnApplyTemplate(); 
           thisthis.WatermarkContent = this.GetTemplateChild("watermarkContent") as ContentControl; 
           thisthis.PasswordContent = this.GetTemplateChild("ContentElement") as PasswordBox; 
           if (WatermarkContent != null && WatermarkContent != null) 
           { 
               PasswordContent.PasswordChanged += new RoutedEventHandler(PasswordContent_PasswordChanged); 
               DetermineWatermarkContentVisibility(); 
           } 
       } 
 
       void PasswordContent_PasswordChanged(object sender, RoutedEventArgs e) 
       { 
           PasswordBox passwdBx = sender as PasswordBox; 
           this.Text = passwdBx.Password; 
       } 
 
       protected override void OnGotFocus(RoutedEventArgs e) 
       { 
           if (WatermarkContent != null && WatermarkContent != null && string.IsNullOrEmpty(this.PasswordContent.Password)) 
           { 
               this.WatermarkContent.Visibility = Visibility.Collapsed; 
           } 
           base.OnGotFocus(e); 
       } 
 
       protected override void OnLostFocus(RoutedEventArgs e) 
       { 
           if (WatermarkContent != null && WatermarkContent != null && string.IsNullOrEmpty(this.PasswordContent.Password)) 
           { 
               this.WatermarkContent.Visibility = Visibility.Visible; 
           } 
           base.OnLostFocus(e); 
       } 
 
       private void DetermineWatermarkContentVisibility() 
       { 
           if (string.IsNullOrEmpty(this.PasswordContent.Password)) 
           { 
               this.WatermarkContent.Visibility = Visibility.Visible; 
           } 
           else 
           { 
               this.WatermarkContent.Visibility = Visibility.Collapsed; 
           } 
       } 
   } 
 public class WatermarkedPasswordBox : TextBox
    {
        ContentControl WatermarkContent;
        private PasswordBox PasswordContent;
 
        public object Watermark
        {
            get { return base.GetValue(WatermarkProperty) as object; }
            set { base.SetValue(WatermarkProperty, value); }
        }
        public static readonly DependencyProperty WatermarkProperty =
        DependencyProperty.Register("Watermark", typeof(object), typeof(WatermarkedPasswordBox), new PropertyMetadata(OnWatermarkPropertyChanged));
 
        private static void OnWatermarkPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            WatermarkedPasswordBox watermarkTextBox = sender as WatermarkedPasswordBox;
            if (watermarkTextBox != null && watermarkTextBox.WatermarkContent != null)
            {
                watermarkTextBox.DetermineWatermarkContentVisibility();
            }
 
            Debug.WriteLine("OnWatermarkPropertyChanged");
        }
 
        public Style WatermarkStyle
        {
            get { return base.GetValue(WatermarkStyleProperty) as Style; }
            set { base.SetValue(WatermarkStyleProperty, value); }
        }
        public static readonly DependencyProperty WatermarkStyleProperty =
        DependencyProperty.Register("WatermarkStyle", typeof(Style), typeof(WatermarkedPasswordBox), null);
 
        public WatermarkedPasswordBox()
        {
            DefaultStyleKey = typeof(WatermarkedPasswordBox);
        }
 
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.WatermarkContent = this.GetTemplateChild("watermarkContent") as ContentControl;
            this.PasswordContent = this.GetTemplateChild("ContentElement") as PasswordBox;
            if (WatermarkContent != null && WatermarkContent != null)
            {
                PasswordContent.PasswordChanged += new RoutedEventHandler(PasswordContent_PasswordChanged);
                DetermineWatermarkContentVisibility();
            }
        }
 
        void PasswordContent_PasswordChanged(object sender, RoutedEventArgs e)
        {
            PasswordBox passwdBx = sender as PasswordBox;
            this.Text = passwdBx.Password;
        }
 
        protected override void OnGotFocus(RoutedEventArgs e)
        {
            if (WatermarkContent != null && WatermarkContent != null && string.IsNullOrEmpty(this.PasswordContent.Password))
            {
                this.WatermarkContent.Visibility = Visibility.Collapsed;
            }
            base.OnGotFocus(e);
        }
 
        protected override void OnLostFocus(RoutedEventArgs e)
        {
            if (WatermarkContent != null && WatermarkContent != null && string.IsNullOrEmpty(this.PasswordContent.Password))
            {
                this.WatermarkContent.Visibility = Visibility.Visible;
            }
            base.OnLostFocus(e);
        }
 
        private void DetermineWatermarkContentVisibility()
        {
            if (string.IsNullOrEmpty(this.PasswordContent.Password))
            {
                this.WatermarkContent.Visibility = Visibility.Visible;
            }
            else
            {
                this.WatermarkContent.Visibility = Visibility.Collapsed;
            }
        }
    }

编译通过,将生成的库引入到Demo程序里,然后将控件加上,成功,效果如下。

左图,未输入内容,显示水印,右图,输入内容时显示效果。

 WindowsPhone自定义控件详解(三) - 实战:自定义带水印的PasswordBox控件,Watermar

摘自 mr_raptor的专栏