WindowsPhone自定义控件详解

(一) - 控件类库分析

为了让你的应用程序更有个性,我们通常会在WP7开发过程中会自定义自己风格的控件,自定义控件中设置各种模板,绑定数据,修改控件行为,但是对于很多朋友来说,这么做是为什么? 不是很清楚,看MSDN的话,又一时找不到头绪,本文就是从控件类库的继承关系里来剖析出自定义控件中的为什么?如果你觉得本文观点独特,对你有很大帮助,请顶之,谢谢。

 

一、 控件分类

让我们先从我们熟悉的控件分类开始,因为这样理解更直观。

WP7里基本控件可以分为以下四类:

1.         文本控件:主要用于显示和输入文本

2.         内容控件:主要用于包含一个子控件元素UIElement(UIElement是所有可视UI控件的基类,我叫它第三代控件祖宗)

3.         项集合控件:从名字可以知道,它是包含有多个子项的控件,如ListBox

4.         容器控件:它可以包含有多个子控件元素(UIElement)

 

如下表所示,常用基本控件。

控件

控件类型

内容属性

TextBlock

文本控件

TextBlock.Text

TextBlock.Inlines

TextBox

文本控件

TextBox.Text

PasswordBox

文本控件

PasswordBox.Password

Button

内容控件

ContentControl.Content

CheckBox

内容控件

ContentControl.Content

RadioButton

内容控件

ContentControl.Content

ListBoxItem

内容控件

ContentControl.Content

ListBox

项集合控件

ItemsControl.Items

ItemsControl.ItemsSource

Canvas

容器控件

Panel.Children

Grid

容器控件

Panel.Children

StackPanel

容器控件

Panel.Children

 原创地址: http://blog.csdn.net/mr_raptor/article/details/7251942

二、主要控件基类

System.Windows.Controls命名空间下,包含以下几个主要的控件基类:

  • Control
  • ContentControl
  • ItemsControl
  • Panel

1. Control:表示用户界面控件的基类

 

 

主要属性:

  • Control.Template 属性:

  通过将ControlTemplate应用给Control.Template 属性来定义其外观。所有继承自Control类的子类,都继承Template属性,也就意味着,你可以通过自定义ControlTemplate来实现Control子控件的自定义。

注意:它有一个子类UserControl,用于快速创建控件,不过,你不能自定义ControlTemplate,并将它应用于UserControl。

 

主要事件:

  • GotFocus,LostFocus 

当控件获得或失去焦点时回调

  •   MouseLeftButtonUp ,MouseMove ,MouseLeftButtonDown

当控件被点击,移动时分别回调

  •   ManipulationStarted ,ManipulationDelta, ManipulationCompleted

当用户对控件开始进行操作时回调ManipulationStarted,当用户对控件操作并更改位置时回调ManipulationDelta,当用户对控件停止操作时,回调ManipulationCompleted。

  •  SizeChanged

当代表控件大小的ActualHeight 或 ActualWidth属性改变时回调

 

2. ContentControl:是其他内容控件的基类,表示包含单项元素的内容控件。像 Button、CheckBox 和 ScrollViewer 这样的控件直接或间接继承自该类。

 

主要属性:

  • ContentControl. Content 属性:

  ContentControl.Content 属性可以是任何类型的对象,例如字符串、UIElement 或 DateTime。当ContentControl.Content 设置为 UIElement 时,ContentControl 中将显示 UIElement。当ContentControl.Content 设置为其他类型的对象时,ContentControl 中将显示该对象的字符串表示形式。

注意:ContentControl 具有有限的默认样式。如果要增强该控件的外观,可以创建新的 DataTemplate类型属性用于自定义。

  •  ContentControl. ContentTemplate属性:

它是一个DataTemplate对象元素,定义该ContentControl的内容显示外观。

 原创地址: http://blog.csdn.net/mr_raptor/article/details/7251942

3. ItemsControl:是项集合类的基类。

 

主要属性:

  • Items属性:

如果要显示固定列表,则应当用一个或多个 FrameworkElement 子对象填充ItemsControl .Items属性,并且不要指定ItemsSource。

  • ItemsSource属性:

如果要显示对象数据源中的数据,则应将ItemsSource指定为对某个对象的引用。 通常,这可通过 Binding 声明来实现,

  • ItemsPanel属性:

设置该属性来自定义项集合控件的布局方式,是一个ItemsPanelTemplate类型元素。

  • ItemTemplate属性:

设置该属性来指定项集合控件中,每一个Item项的数据显示外观,是一个DataTemplate类型元素。

 

4. Panel: 是容器控件的基类,比如:Canvas, Grid, StackPanel。

 

 

自定义布局行为可以通过以下方式来实现:从 Panel 继承并使用 MeasureOverride 和 ArrangeOverride 方法重写默认度量和排列行为。

原创地址: http://blog.csdn.net/mr_raptor/article/details/7251942

上面解释的基类中的主要属性和方法是我们在自定义控件时去修改的东西,通过中间的继承关系图可以看得出,Panel类与Control类没有任何继承关系,这也就从祖宗这辈上决定了其孩子的特性不一样,所以在对两个大分支类进行自定义时不一样,Panel类主要是包含其它控件的容器控件,对容器控件的主要自定义就是设置不同的被包含控件之间的布局,因此重载其两个测量方法。而Contorl类是用户交互类,有外观,有交互操作,对其子类主要是自定义其外观和操作,也就是说,通过设置其Template属性和重载具体的事件来达到目的。

  • ContentControl类是Control类的子类,它有Control的特点,不过,它是内容控件,主要用来包含一个UIElement。
  • 既然包含一个UIElement,就要设置这个UIElement的内容,即:Content属性。
  • 通过ContentTempalte来定义这个被包含的UIElement的外观,因此,如果要自定义外观那就是重新创建新的DataTempalte,应用到ContentTempalte属性喽。
  • ItemsControl类是Control类的子类,同样继承了该继承的东西,它是个项集合类,可以包含多个子项Item。
  • 它有Items属性和ItemsSource让用户来指定包含哪些子项。
  • 各子项之间要怎样布局,就有了ItemtPanel属性用来让用户指定。
  • 每一个子项是一个内容控件,它具有ContentControl的特点,因此还要通过ItemTempalte属性指定每一个项的外观。
(二) - 模板类库分析

基类自定义时都要用到模板,在框架中所有的模板都是FrameworkTemplate的子类,包括:

  1.  ControlTemplate
  2. ItemsPanelTemplate
  3. DataTemplate

通过上节对控件的继承关系的分析,你已经可以理解为什么有上面的三种模板了吧。

无非就是对三种控件分类的,三种模板。即:

 > Control类模板对应ControlTemplate

 > ItemsControl类模板对应ItemsPanelTemplate

 > ContentControl、ItemTemplate类模板对应DataTemplate

 

下面分别来解释三种模板。

 

一、 模板类详解

 

继承关系:

 

 

由上图可知,控件对象模板,项集合模板和数据对象模板都是继承自FrameworkTemplate类,

1. ControlTemplate主要用于自定义控件的操作行为视图结构的外观显示效果。如:当按钮按下时如何显示等,按钮上要不要同时显示图片和文本。

  • 通过设置控件的Template属性(继承自Control)来应用自定义的ControlTemplate

2.  ItemsPanelTemplate主要用于自定义带有列表项的控件中各子控件的布局外观显示效果,如:ListBox中的列表项怎样对布局。

  • 通过设置控件的ItemsPanel属性来应用自定义的ItemsPanelTemplate

3.  DataTemplate主要用于自定义内容控件中的数据视图效果,如:ListBox中每一项显示什么数据。

  •  通过设置控件的ItemTemplate /ContentTemplate属性来应用自定义的DataTemplate,注意:一个控件上可能应用多个自定义模板,如:ListBox设置ListBox的列表项Items为横向排列,设置每个列表项里布局和数据,这样就要设置ListBox的ItemsPanelTemplate和DataTemplate。

 原创地址:http://blog.csdn.net/mr_raptor/article/details/7251948

ControlTemplate类

  定义控件的视图显示模板,从而可以对控件进行自定义。在模板内可以构建自己的控件对象树。

注意:

  • 如果您正在定义一个控件模板来取代一个现有控件类的模板,则您用于定义控件模板内容的 XAML 应与现有的控件匹配。否则,该控件可能无法在用户界面中正常发挥作用。
  • 不能将 ControlTemplate 应用于 UserControl(上一节有说明为什么)。

例如:为 Button 创建一个简单的 ControlTemplate。控件模板包含一个Grid 并指定以下行为:

·         当用户将鼠标悬停在Button 上方时,Grid 在半秒之后从绿色变为红色。

·         当用户将鼠标移离按钮时,Grid 立即变回到绿色。

 

[html]  view plain copy
  1. <ControlTemplate TargetType="Button">  
  2.   <Grid >  
  3.     <VisualStateManager.VisualStateGroups>  
  4.       <VisualStateGroup x:Name="CommonStates">  
  5.         <VisualStateGroup.Transitions>  
  6.           <!--Take one half second to trasition to the MouseOver state.-->  
  7.           <VisualTransition To="MouseOver" GeneratedDuration="0:0:0.5"/>  
  8.         </VisualStateGroup.Transitions>  
  9.         <VisualState x:Name="Normal" />  
  10.         <!--Change the SolidColorBrush, ButtonBrush, to red when the  
  11.             mouse is over the button.-->  
  12.         <VisualState x:Name="MouseOver">  
  13.           <Storyboard>  
  14.             <ColorAnimation Storyboard.TargetName="ButtonBrush"   
  15.         Storyboard.TargetProperty="Color" To="Red" />  
  16.           </Storyboard>  
  17.         </VisualState>  
  18.       </VisualStateGroup>  
  19.     </VisualStateManager.VisualStateGroups>  
  20.     <Grid.Background>  
  21.       <SolidColorBrush x:Name="ButtonBrush" Color="Green"/>  
  22.     </Grid.Background>  
  23.   </Grid>  
  24. </ControlTemplate>  


 

ItemsPanelTemplate 类

ItemsPanelTemplate定义ItemsControl中的Item项布局的模板。ItemsControl 的默认值是一个指定StackPanel的ItemsPanelTemplate。例如:ListBox是一个ItemsControl子控件,它的Item项布局模板ItemsPanelTemplate为默认的StackPanel,而StackPanel默认布局是垂直布局,因此,默认的ListBox的Item项垂直布局的,当我们向ListBox里添加Item时,都是垂直列表形式,如果你想要自定义你的ListBox风格为水平显示,那么将要自定义ItemsPanelTemplate里StackPanel为水平方向。

如下例,将ListBox的风格改为水平子项显示方式。

 

模板XAML:

[html]  view plain copy
  1. <Grid>  
  2.   <Grid.Resources>  
  3.     <Style x:Key="horizontalListBoxStyle" TargetType="ListBox">  
  4.       <Setter Property="ItemsPanel">  
  5.         <Setter.Value>  
  6.           <ItemsPanelTemplate>  
  7.             <StackPanel Orientation="Horizontal"  
  8.               VerticalAlignment="Center"  
  9.               HorizontalAlignment="Center"/>  
  10.           </ItemsPanelTemplate>  
  11.         </Setter.Value>  
  12.       </Setter>  
  13.     </Style>  
  14. <src:Items x:Key="items"/>  
  15.   </Grid.Resources>  
  16.   <ListBox ItemsSource="{StaticResource items}"   
  17.            Style="{StaticResource horizontalListBoxStyle}"/>  
  18. </Grid>  

 

C#代码:

[csharp]  view plain copy
  1. public class Items :   
  2.     System.Collections.ObjectModel.ObservableCollection<string>  
  3. {  
  4.     public Items()  
  5.     {  
  6.         Add("Item 1");  
  7.         Add("Item 2");  
  8.         Add("Item 3");  
  9.         Add("Item 4");  
  10.         Add("Item 5");  
  11.     }  
  12. }  

显示效果如下:

 


总结:

ItemsPanelTemplate主要用于带有Item项的控件风格布局模板设置,常见的控件就是ListBox,

 原创地址:http://blog.csdn.net/mr_raptor/article/details/7251948

DataTemplate 类

  用于定义内容控件内数据对象的可视结构模板。虽然内容控件只能包含一个UIElement,但是,它可以包含一个容器控件,从而可以间接的包含多个子控件,而DataContent就是为这些容器控件里的子控件进行布局的模板类。

下面的例子,自定了ListBox中每一项中的UI如何表现。每一个Item中包含四个水平布局的TextBlock控件,每个TextBlock控件都绑定了Customers的属性。

XAML:

[html]  view plain copy
  1. <Grid>  
  2.     <Grid.Resources>  
  3.         <src:Customers x:Key="customers"/>  
  4.     </Grid.Resources>  
  5.   
  6.     <ListBox ItemsSource="{StaticResource customers}" Width="350" Margin="0,5,0,10">  
  7.         <ListBox.ItemTemplate>  
  8.             <DataTemplate>  
  9.                 <StackPanel Orientation="Horizontal">  
  10.                     <TextBlock Padding="5,0,5,0"  
  11.                        Text="{Binding FirstName}" />  
  12.                     <TextBlock Text="{Binding LastName}" />  
  13.                     <TextBlock Text=", " />  
  14.                     <TextBlock Text="{Binding Address}" />  
  15.                 </StackPanel>  
  16.             </DataTemplate>  
  17.         </ListBox.ItemTemplate>  
  18.     </ListBox>  
  19. </Grid>  


C#:

[csharp]  view plain copy
  1. public class Customer  
  2. {  
  3.     public String FirstName { getset; }  
  4.     public String LastName { getset; }  
  5.     public String Address { getset; }  
  6.   
  7.     public Customer(String firstName, String lastName, String address)  
  8.     {  
  9.         this.FirstName = firstName;  
  10.         this.LastName = lastName;  
  11.         this.Address = address;  
  12.     }  
  13. }  
  14.   
  15. public class Customers : ObservableCollection<Customer>  
  16. {  
  17.     public Customers()  
  18.     {  
  19.         Add(new Customer("Michael""Anderberg",  
  20.                 "12 North Third Street, Apartment 45"));  
  21.         Add(new Customer("Chris""Ashton",  
  22.                 "34 West Fifth Street, Apartment 67"));  
  23.         Add(new Customer("Cassie""Hicks",  
  24.                 "56 East Seventh Street, Apartment 89"));  
  25.         Add(new Customer("Guido""Pica",  
  26.                 "78 South Ninth Street, Apartment 10"));  
  27.     }  
  28. }  


 

二、其它

原创地址:http://blog.csdn.net/mr_raptor/article/details/7251948

DataContext类

 

 

  DataContext是FrameworkElement的属性,是Object类型,用于获取或设置 FrameworkElement 参与数据绑定时的数据上下文。也就是说它是被数据绑定的对象。

DataContext也就是第四代控件祖宗的属性(说实话,控件从第三代祖宗UIElement开始才有了外观,有了点人样),

如果你给它绑定了数据源,CLR就会从数据源里拿出对应数据用于显示,DataContext有传递性,如果外部包含控件设置了DataContext,被包含控件没有设置该属性,则被包含控件也可以使用外部包含控件的DataContext。

比如:

 XAML:

[html]  view plain copy
  1. <phone:PhoneApplicationPage.Resources>  
  2.             <local:WeiBoData x:Key="MyWeiBoData"/>  
  3. </phone:PhoneApplicationPage.Resources>  
  4.   
  5. <Grid x:Name="LayoutRoot" Background="Transparent"  DataContext="{StaticResource MyWeiBoData}">  
  6.         <StackPanel Grid.Row="0" Margin="12,17,0,28">  
  7.             <TextBlock x:Name="DateTextBlock" Text="{Binding WeiBoDate}" >  
  8.             <TextBlock x:Name="TitleTextBlock" Text="{Binding WeiBoTitle}" />  
  9.         </StackPanel>  
  10. </Grid>  


WeiBoData类中包含有WeiBoDate属性和WeiBoTitle属性,虽然没有指定两个TextBlock的绑定对象,但是它有Grid控件的DataContext。

 在后续两节,我们分别以这两节的知识,分享两个例子:

自定义水印密码输入控件下拉刷新控件

注:上述两个控件经常使用,并且方便快捷。


 (三) - 实战:自定义带水印的PasswordBox控件,WatermarkedPasswordBox


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

原创地址:http://blog.csdn.net/mr_raptor/article/details/7251992

原理分析:

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

  1. PasswordBox里没内容时,显示水印
  2. PasswordBox里有内容时,不显示水印,而显示内容

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

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

 

 

一、 分析WatermarkedTextBox代码

 

  1. themes/generic.xaml

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

[html]  view plain copy
  1. <ControlTemplate x:Key="PhoneDisabledTextBoxTemplate" TargetType="TextBox">  
  2.     <ContentControl x:Name="ContentElement" BorderThickness="0" HorizontalContentAlignment="Stretch" Margin="{StaticResource PhoneTextBoxInnerMargin}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="Stretch"/>  
  3. </ControlTemplate>  
  4.         <Style  TargetType="local:WatermarkedTextBox">  
  5.    <Setter Property="Template">  
  6.     <Setter.Value>  
  7.         <ControlTemplate TargetType="local:WatermarkedTextBox">  
  8.             <Grid Background="Transparent">  
  9.                 <Border x:Name="EnabledBorder">  
  10.                     <Grid>  
  11.                         <ContentControl x:Name="watermarkContent" Style="{TemplateBinding WatermarkStyle}" Content="{TemplateBinding Watermark}" Background="Transparent" Opacity="0.5"/>  
  12.                         <ContentControl x:Name="ContentElement" BorderThickness="0"  VerticalContentAlignment="Stretch"/>  
  13.                     </Grid>  
  14.                 </Border>  
  15.                 <Border x:Name="DisabledOrReadonlyBorder" Background="Transparent"  Visibility="Collapsed">  
  16.                     <TextBox x:Name="DisabledOrReadonlyContent" Background="Transparent" IsReadOnly="True" Text="{TemplateBinding Text}"  Template="{StaticResource PhoneDisabledTextBoxTemplate}"/>  
  17.                     </Border>  
  18.                 </Grid>  
  19.             </ControlTemplate>  
  20.         </Setter.Value>  
  21.     </Setter>  
  22. </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]  view plain copy
  1. namespace WatermarkedTextBoxControl  
  2. {  
  3.     public class WatermarkedTextBox : TextBox  
  4.     {  
  5.         ContentControl WatermarkContent;  
  6.         public static readonly DependencyProperty WatermarkProperty =  
  7.       DependencyProperty.Register("Watermark"typeof(object), typeof(WatermarkedTextBox), new PropertyMetadata(OnWatermarkPropertyChanged));  
  8.    
  9.         public static readonly DependencyProperty WatermarkStyleProperty =  
  10.       DependencyProperty.Register("WatermarkStyle"typeof(Style), typeof(WatermarkedTextBox), null);  
  11.    
  12.         public Style WatermarkStyle  
  13.         {  
  14.             get { return base.GetValue(WatermarkStyleProperty) as Style; }  
  15.             set { base.SetValue(WatermarkStyleProperty, value); }  
  16.         }  
  17.    
  18.         public object Watermark  
  19.         {  
  20.             get { return base.GetValue(WatermarkProperty) as object; }  
  21.             set { base.SetValue(WatermarkProperty, value); }  
  22.         }  
  23.    
  24.         public WatermarkedTextBox()  
  25.         {  
  26.             DefaultStyleKey = typeof(WatermarkedTextBox);  
  27.         }  
  28.    
  29.         public override void OnApplyTemplate()  
  30.         {  
  31.             base.OnApplyTemplate();  
  32.             this.WatermarkContent = this.GetTemplateChild("watermarkContent"as ContentControl;  
  33.             if(WatermarkContent != null)  
  34.             {  
  35.               DetermineWatermarkContentVisibility();  
  36.             }  
  37.         }  
  38.    
  39.         protected override void OnGotFocus(RoutedEventArgs e)  
  40.         {  
  41.             if (WatermarkContent != null && string.IsNullOrEmpty(this.Text))  
  42.             {  
  43.                 this.WatermarkContent.Visibility = Visibility.Collapsed;  
  44.             }  
  45.             base.OnGotFocus(e);  
  46.         }  
  47.    
  48.         protected override void OnLostFocus(RoutedEventArgs e)  
  49.         {  
  50.             if (WatermarkContent != null && string.IsNullOrEmpty(this.Text))  
  51.             {  
  52.                 this.WatermarkContent.Visibility = Visibility.Visible;  
  53.             }  
  54.             base.OnLostFocus(e);  
  55.         }  
  56.    
  57.         private static void OnWatermarkPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)  
  58.         {  
  59.             WatermarkedTextBox watermarkTextBox = sender as WatermarkedTextBox;  
  60.             if(watermarkTextBox != null && watermarkTextBox.WatermarkContent !=null)  
  61.             {  
  62.               watermarkTextBox.DetermineWatermarkContentVisibility();  
  63.             }  
  64.         }  
  65.    
  66.         private void DetermineWatermarkContentVisibility()  
  67.         {  
  68.             if (string.IsNullOrEmpty(this.Text))  
  69.             {  
  70.                 this.WatermarkContent.Visibility = Visibility.Visible;  
  71.             }  
  72.             else  
  73.             {  
  74.                 this.WatermarkContent.Visibility = Visibility.Collapsed;  
  75.             }  
  76.         }  
  77.     }  
  78.     }  
  79. }  

原创地址: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]  view plain copy
  1. <Style  TargetType="local:WatermarkedPasswordBox">  
  2.        <Setter Property="Template">  
  3.            <Setter.Value>  
  4.                <ControlTemplate TargetType="local:WatermarkedPasswordBox">  
  5.                    <Grid Background="Transparent">  
  6.                        <Border x:Name="EnabledBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Margin="{StaticResource PhoneTouchTargetOverhang}">  
  7.                            <Grid>  
  8.                                <ContentControl x:Name="watermarkContent" Style="{TemplateBinding WatermarkStyle}" Content="{TemplateBinding Watermark}" Background="Transparent" Opacity="0.5"/>  
  9.                                <PasswordBox x:Name="ContentElement" Background="Transparent"  
  10.                 Password="{TemplateBinding Text}"   
  11.                                     BorderThickness="0" HorizontalContentAlignment="Stretch"  
  12.                                     Margin="-12,-12,-12,-12"   
  13.                                     VerticalContentAlignment="Stretch"/>  
  14.                            </Grid>  
  15.                        </Border>  
  16.                    </Grid>  
  17.                </ControlTemplate>  
  18.            </Setter.Value>  
  19.        </Setter>  
  20.    </Style>  

原创地址:http://blog.csdn.net/mr_raptor/article/details/7251992
C#:

[html]  view plain copy
  1. public class WatermarkedPasswordBox : TextBox  
  2.    {  
  3.        ContentControl WatermarkContent;  
  4.        private PasswordBox PasswordContent;  
  5.   
  6.        public object Watermark  
  7.        {  
  8.            get { return base.GetValue(WatermarkProperty) as object; }  
  9.            set { base.SetValue(WatermarkProperty, value); }  
  10.        }  
  11.        public static readonly DependencyProperty WatermarkProperty =  
  12.        DependencyProperty.Register("Watermark", typeof(object), typeof(WatermarkedPasswordBox), new PropertyMetadata(OnWatermarkPropertyChanged));  
  13.   
  14.        private static void OnWatermarkPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)  
  15.        {  
  16.            WatermarkedPasswordBox watermarkTextBox = sender as WatermarkedPasswordBox;  
  17.            if (watermarkTextBox != null && watermarkTextBox.WatermarkContent != null)  
  18.            {  
  19.                watermarkTextBox.DetermineWatermarkContentVisibility();  
  20.            }  
  21.   
  22.            Debug.WriteLine("OnWatermarkPropertyChanged");  
  23.        }  
  24.   
  25.        public Style WatermarkStyle  
  26.        {  
  27.            get { return base.GetValue(WatermarkStyleProperty) as Style; }  
  28.            set { base.SetValue(WatermarkStyleProperty, value); }  
  29.        }  
  30.        public static readonly DependencyProperty WatermarkStyleProperty =  
  31.        DependencyProperty.Register("WatermarkStyle", typeof(Style), typeof(WatermarkedPasswordBox), null);  
  32.   
  33.        public WatermarkedPasswordBox()  
  34.        {  
  35.            DefaultStyleKey = typeof(WatermarkedPasswordBox);  
  36.        }  
  37.   
  38.        public override void OnApplyTemplate()  
  39.        {  
  40.            base.OnApplyTemplate();  
  41.            thisthis.WatermarkContent = this.GetTemplateChild("watermarkContent") as ContentControl;  
  42.            thisthis.PasswordContent = this.GetTemplateChild("ContentElement") as PasswordBox;  
  43.            if (WatermarkContent != null && WatermarkContent != null)  
  44.            {  
  45.                PasswordContent.PasswordChanged += new RoutedEventHandler(PasswordContent_PasswordChanged);  
  46.                DetermineWatermarkContentVisibility();  
  47.            }  
  48.        }  
  49.   
  50.        void PasswordContent_PasswordChanged(object sender, RoutedEventArgs e)  
  51.        {  
  52.            PasswordBox passwdBx = sender as PasswordBox;  
  53.            this.Text = passwdBx.Password;  
  54.        }  
  55.   
  56.        protected override void OnGotFocus(RoutedEventArgs e)  
  57.        {  
  58.            if (WatermarkContent != null && WatermarkContent != null && string.IsNullOrEmpty(this.PasswordContent.Password))  
  59.            {  
  60.                this.WatermarkContent.Visibility = Visibility.Collapsed;  
  61.            }  
  62.            base.OnGotFocus(e);  
  63.        }  
  64.   
  65.        protected override void OnLostFocus(RoutedEventArgs e)  
  66.        {  
  67.            if (WatermarkContent != null && WatermarkContent != null && string.IsNullOrEmpty(this.PasswordContent.Password))  
  68.            {  
  69.                this.WatermarkContent.Visibility = Visibility.Visible;  
  70.            }  
  71.            base.OnLostFocus(e);  
  72.        }  
  73.   
  74.        private void DetermineWatermarkContentVisibility()  
  75.        {  
  76.            if (string.IsNullOrEmpty(this.PasswordContent.Password))  
  77.            {  
  78.                this.WatermarkContent.Visibility = Visibility.Visible;  
  79.            }  
  80.            else  
  81.            {  
  82.                this.WatermarkContent.Visibility = Visibility.Collapsed;  
  83.            }  
  84.        }  
  85.    }  


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

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

 

 



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值