Silverlight 2 Customized Control 开发

Customized Control 和 User Control

相信大家比较熟悉使用Silverlight的User Control,在VS2008的Silverlight插件中,可以通过添加新项(Add new Item)创建一个User Control,而且Silverlight在网页中嵌入的本身就是一个User Control。

本文中讲的是如何开发Customized Control,Customized Control是与User Control完全不同的,Customized Control是继承或者间接继承System.Windows.Controls.Control的,而User Control必须继承System.Windows.Controls.Control.UserControl。Customized Control更接近于Silverlight本身提供的Button、CheckBox等Control, User Control则比较类似这些原生Control的组合体。

准备工作

在阅读本文之前至少应该掌握:

  • 创建和使用简单Silverlight程序
  • 使用和编辑Silverlight控件模板
  • 使用Silverlight的Visual State
  • C#基本知识
  • 使用Visual studio的项目和解决方案

推荐链接

环境

(提示:连接给出的默认是英文,大家可以根据习惯选择。开发工具和Service Pack语言必须对应。)

(注意:Visual Studio和Expression Blend不是免费工具,这里给出的是试用版本链接,用于商业开发可能存在风险。)

 

接下来我们可以开始Customized Control之旅了。

(因为不想在基础的地方浪费时间,所以前面部分会非常简略,请参考推荐链接)

 

一、创建解决方案

Customized Control一般会编写成DLL文件,所以创建工程的时候应该选择Silverlight Class Library,同时为了调试,我们还要在解决方案中再建立一个新的Silverlight Application project。如果希望用Web站点来调试Silverlight Application,就还需要创建解决方案中的第三个project,推荐使用动态生成的HTML。然后需要在解决放案属性中定义好项目的依赖关系以及start up project。考虑到这部分大家都比较了解,就不再赘述。

二、创建Control的C#类

创建的Silverlight Class Library中默认会有一个Class1.cs,这是一个普通的C#类,与Silverlight并无关系,可以选择保留它利用VS的重构功能换成喜欢的名字,也可以删掉它再重新建立一个类。总之我们的Class Library中只需要保留一个我们要开发的控件名字的类就可以了,比如我打算开发一个MenuItem。然后我们要继承Control类,当然也可以继承任何其它继承了Control的类。最后代码看起来像是这样:

 
 
     public   class  MenuItem : Control
    {
        
public  MenuItem()
        {
        }
    }

 

OK,这样我们就制作出了一个最基本的Customized Control。

三、调用Control

接下来我们希望在Silverlight Application中调用生成的Customized Control,首先要添加引用,因为现在在同一Solution中,所以可以选择项目引用。

然后我们要把我们类库的clr命名空间加入到XAML的命名空间当中,所以修改Page.xaml的UserControl为:

 

< UserControl  x:Class ="SilverlightMenuTest.Page"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom
="clr-namespace:SilverlightMenu;assembly=SilverlightMenu"
    Width
="400"  Height ="300"   />

 

 

clr-namespace写我们类库中使用的namespace,assembly写类库项目的名称就可以了,XML命名空间可以是我们喜欢的任何名字,这里用了custom。

然后我们可以在XAML中使用我们的新控件了,控件名称和C#类名相同,前面要带上命名空间,代码最后类似这样:

 
 
< UserControl  x:Class ="SilverlightMenuTest.Page"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom
="clr-namespace:SilverlightMenu;assembly=SilverlightMenu"
    Width
="400"  Height ="300" >
    
< Grid  x:Name ="LayoutRoot"  Background ="White" >
        
< custom:MenuItem  />
    
</ Grid >
</ UserControl >

接下来我们可以运行程序了,但是当然结果是一片空白。

 
 

四、添加模板

这个一片空白的Control显然没有任何意义,接下来我们要让它变成可见的。只需要设置好MenuItem的Template属性就可以了,可以看到这里设置了Width和Height,Template中的元素会自动把自己限制在这个范围内。

 
 
< UserControl  x:Class ="SilverlightMenuTest.Page"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom
="clr-namespace:SilverlightMenu;assembly=SilverlightMenu"
    Width
="400"  Height ="300" >
    
< Grid  x:Name ="LayoutRoot"  Background ="White"  Width ="100"  Height ="24" >
        
< custom:MenuItem >
            
< custom:MenuItem.Template >
                
< ControlTemplate >
                    
< Rectangle  Fill ="Black"  StrokeThickness ="1"  RadiusX ="2"  RadiusY ="2"  x:Name ="Bg" />
                
</ ControlTemplate >
            
</ custom:MenuItem.Template >
        
</ custom:MenuItem >
    
</ Grid >
</ UserControl >
显示这段代码我们就可以看到一个黑色的圆角矩形了。

五、绑定属性

这样我们有了一个可见的控件,但是这个控件的任何属性(如Background之类的)都没有办法发生作用。现在我们可以想想办法让矩形的颜色是控件的背景色。使用Silverlight的属性文法,可以将矩形的Fill属性绑定到MenuItem控件的Background。

 
 
< UserControl  x:Class ="SilverlightMenuTest.Page"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom
="clr-namespace:SilverlightMenu;assembly=SilverlightMenu"
    Width
="400"  Height ="300" >
    
< Grid  x:Name ="LayoutRoot"  Background ="White"  Width ="100"  Height ="24" >
        
< custom:MenuItem  Background ="Blue"   >
            
< custom:MenuItem.Template >
                
< ControlTemplate >
                    
< Rectangle  Fill =" {TemplateBinding Background} "  StrokeThickness ="1"  RadiusX ="2"  RadiusY ="2"  x:Name ="Bg" />
                
</ ControlTemplate >
            
</ custom:MenuItem.Template >
        
</ custom:MenuItem >
    
</ Grid >
</ UserControl >

感兴趣的话,可以试着把Rectangle的Stroke绑定到MenuItem的BorderBrush。这样,我们就可以通过控件的属性控制控件的显示效果了。

六、添加默认模板

当然你不能要求使用者总是编辑你的Control的模板,所以我们必须要有一个默认的模板。VisualStudio总是会从项目的themes文件夹下查找generic.xaml作为默认模板,注意这个项目是控件的类所在的项目,也就是我们建立的Silverlight Class Library。

image

如果你看过之前的介绍Silverlight自定义控件开发的文章,你会发现,这个地方Silverlight 2 RTW版和Silverlight 2 beta 2或者beta 1是不兼容的,我们必须将generic.xaml放到themes目录下。beta 2版本是放在根目录下,而beta 1版本是放在根目录下且必须修改generic.xaml的属性才能生效。

一个空的generic.xaml的内容如下(但是VS不会自动为你生成这个文件,需要手动添加):

 
 
< ResourceDictionary
    
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" >
</ ResourceDictionary >

我们需要通过style把新控件的模板放进去,但之前应该先把clr命名空间加入。

 
 
< ResourceDictionary
    
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom
="clr-namespace:SilverlightMenu;assembly=SilverlightMenu" >
</ ResourceDictionary >

最后的XAML类似这样(style大家并不陌生,就不多讲了)

 

< ResourceDictionary
    
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom
="clr-namespace:SilverlightMenu;assembly=SilverlightMenu" >
    
< Style  TargetType ="custom:MenuItem" >
        
< Setter  Property ="Template" >
            
< Setter.Value >
                
< ControlTemplate  TargetType ="custom:MenuItem" >
                    
< Rectangle  Fill =" {TemplateBinding Background} "  StrokeThickness ="1"  RadiusX ="2"  RadiusY ="2"  x:Name ="Bg" />
                
</ ControlTemplate >
            
</ Setter.Value >
        
</ Setter >
    
</ Style >
</ ResourceDictionary >

 

但仅仅如此控件是不会有任何变化的,要在MenuItem的构造函数中加入一句:

 
 
this .DefaultStyleKey  =   typeof (MenuItem);
这样我们的Control就更接近原生Control了,可以这样调用:
 
 
< UserControl  x:Class ="SilverlightMenuTest.Page"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom
="clr-namespace:SilverlightMenu;assembly=SilverlightMenu"
    Width
="400"  Height ="300" >
    
< Grid  x:Name ="LayoutRoot"  Background ="White"  Width ="100"  Height ="24" >
        
< custom:MenuItem  Background ="Black" ></ custom:MenuItem >
    
</ Grid >
</ UserControl >

再运行就可以看到黑色矩形了。

七、绑定事件

到此为止,我们的Control还只能是显示出来看看,没有任何交互性,下面我们就来给它添加一定的交互性。因为Control本身提供了一些事件,所以我们很容易捕获这些事件并作出响应。对于Control来说,我们只需要添加事件处理函数并且绑定到相应事件就可以了。

 
 
namespace  System.Windows.Controls
{
    
public   class  MenuItem : Control
    {
        
public  MenuItem()
        {
            
this .DefaultStyleKey  =   typeof (MenuItem);
            
this .MouseEnter  +=   new  MouseEventHandler(OnMouseEnter);
        }
        
public   void  OnMouseEnter( object  sender, MouseEventArgs args)
        {
        }
    }
}
可以在OnMouseEnter中设置断点来观察事件响应情况。

八、操作模板成员

现在我们知道了如何通过事件处理函数响应事件,我们可以在其中改变控件自身的状态,比如,当鼠标划过时,改变控件的背景。前面说到背景显示是通过一个Rectangle实现的,那么要改变背景就需要取到这个Rectangle,我们要使用一个函数GetTemplateChild来取这个元素。注意因为GetTemplateChild并不一定总能取到元素,而且元素类型并不一定是我们期望的(了解Template的朋友会知道,Template是一个可以被EndUser修改的属性,所以不能以任何形式断言控件Template的结构和其中的元素名),所以一定要处理异常。下面的代码是让鼠标在控件上时背景变成红色:

 
 
public   class  MenuItem : Control
ExpandedBlockStart.gifContractedBlock.gif
{
    
public MenuItem()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
this.DefaultStyleKey = typeof(MenuItem);
        
this.MouseEnter += new MouseEventHandler(OnMouseEnter);
        
this.MouseLeave += new MouseEventHandler(OnMouseLeave);
    }

    
public void OnMouseEnter(object sender, MouseEventArgs args)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
try
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            Rectangle BgRect 
= GetTemplateChild("Bg"as Rectangle;
            BgRect.Fill 
= new SolidColorBrush(Color.FromArgb(25525500));
        }

        
catch
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
        }

    }

    
public void OnMouseLeave(object sender, MouseEventArgs args)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
try
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            Rectangle BgRect 
= GetTemplateChild("Bg"as Rectangle;
            BgRect.Fill 
= new SolidColorBrush(Color.FromArgb(255000));
        }

        
catch
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
        }

    }

}
有些时候,我们希望在模板生效的时候就对某些模板成员进行操作,如绑定事件,调整属性等,就需要一个事件OnApplyTemplate,我们只能通过override父类的OnApplyTemplate来响应模板生效事件:

 

九、添加可视状态(VisualState)

上面的代码实现了我们想要的功能,然而却有很多问题:

  1. 颜色是硬编码
  2. 无法保留设置的背景色(只能是黑色)
  3. 显示样式用C#代码书写,不符合XAML+C#界面和逻辑分开的精神

那么如何解决呢?很显然,跟使用Silverlight开发Web应用一样,答案就是使用VisualState。首先我们要为我们的控件模板定义VisualState,因为VisualState必须定义在root element上,所以这里我稍微做了一点改动,给模板加了一个容器。关于如何定义和使用VisualState,早有不少高手写的很清楚,这里就不再重复了。下面的Visual定义了2个互斥的状态MouseOver和MouseOut,MouseOver状态将背景Rectangle颜色变红,MouseOut则恢复原状。

 
 
< ResourceDictionary
    
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vsm
="clr-namespace:System.Windows;assembly=System.Windows"
    xmlns:custom
="clr-namespace:System.Windows.Controls;assembly=SilverlightMenu" >
    
< Style  TargetType ="custom:MenuItem" >
        
< Setter  Property ="Template" >
            
< Setter.Value >
                
< ControlTemplate  TargetType ="custom:MenuItem" >
                    
< Grid >
                        
< vsm:VisualStateManager.VisualStateGroups >
                            
< vsm:VisualStateGroup  x:Name ="MouseOverStates" >
                                
< vsm:VisualState  x:Name ="MouseOver" >
                                    
< Storyboard >
                                        
< ObjectAnimationUsingKeyFrames  Storyboard.TargetName ="Bg"  Storyboard.TargetProperty ="Fill"  Duration ="0:0:0" >
                                            
< ObjectAnimationUsingKeyFrames.KeyFrames >
                                                
< DiscreteObjectKeyFrame  KeyTime ="0:0:0" >
                                                    
< DiscreteObjectKeyFrame.Value >
                                                        
< SolidColorBrush  Color ="#FFFF0000" ></ SolidColorBrush >
                                                    
</ DiscreteObjectKeyFrame.Value >
                                                
</ DiscreteObjectKeyFrame >
                                            
</ ObjectAnimationUsingKeyFrames.KeyFrames >
                                        
</ ObjectAnimationUsingKeyFrames >
                                    
</ Storyboard >
                                
</ vsm:VisualState >
                                
< vsm:VisualState  x:Name ="MouseOut" >
                                    
< Storyboard >
                                    
</ Storyboard >
                                
</ vsm:VisualState >
                            
</ vsm:VisualStateGroup >
                        
</ vsm:VisualStateManager.VisualStateGroups >
                        
< Rectangle  Fill =" {TemplateBinding Background} "  StrokeThickness ="1"  RadiusX ="2"  RadiusY ="2"  x:Name ="Bg" />
                    
</ Grid >
                
</ ControlTemplate >
            
</ Setter.Value >
        
</ Setter >
    
</ Style >
</ ResourceDictionary >

定义好了Visual State,我们的事件就可以写的很简单了:

 
 
public   class  MenuItem : Control
{
    
public  MenuItem()
    {
        
this .DefaultStyleKey  =   typeof (MenuItem);
        
this .MouseEnter  +=   new  MouseEventHandler(OnMouseEnter);
        
this .MouseLeave  +=   new  MouseEventHandler(OnMouseLeave);
    }
    
public   void  OnMouseEnter( object  sender, MouseEventArgs args)
    {
        VisualStateManager.GoToState(
this " MouseOver " false );
    }
    
public   void  OnMouseLeave( object  sender, MouseEventArgs args)
    {
        VisualStateManager.GoToState(
this " MouseOut " false );
    }
}
这样运行下试试看,是不是很cool?

 

十、添加自定义属性

接下来到了最后的部分了,也是最少资料涉及的部分,Silverlight Customized Control自定义属性。下面我们来以一个Text属性为例演示如何创建一个自定义属性,这个例子将会用到一些前面的内容。首先我们要为DependencyObject的属性声明一个公有的DependencyProperty,这个属性是静态的,可以用于数据绑定。(还记得数据绑定中使用的DependencyProperty吧,就是这样的哦^^)

 

     public   static   readonly  DependencyProperty TextProperty;

 

静态属性在静态构造函数中初始化,这里有一点复杂,首先看完整代码:

 
 
     static  MenuItem()
    {
        TextProperty 
=  DependencyProperty.Register( " Text " typeof ( string ),  typeof (MenuItem),  new  PropertyMetadata( false new  PropertyChangedCallback(MenuItem.OnTextPropertyChanged)));
    }
DependencyProperty应该由DependencyProperty.Register创建,在MSDN中,这个函数的原型如下:
 
 
public   static  DependencyProperty Register(
    
string  name,
    Type propertyType,
    Type ownerType,
    PropertyMetadata typeMetadata
)
 

其中name是属性在XAML中使用的名字,propertyType则是属性的类型,注意这个属性理论上可以是任何类型,但是一般只使用整数、布尔、字符串、还有UI元素这些类型,其它类型需要定义复杂的字符串到对象的属性文法作为转换规则(之后可能会单写一篇blog讲converter)。ownerType就是我们自己定义的类了。typeMetadata有点复杂,PropertyMetadata构造函数有3个重载,提供选择指定defaultValue和propertyChangedCallback中任意一个或者同时指定2个。原型如下:

 
 
public  PropertyMetadata(
    Object defaultValue,
    PropertyChangedCallback propertyChangedCallback
)

 

defaultValue是属性的默认值没什么可说,propertyChangedCallback也很简单,就是当属性改变时的处理函数,这个是把属性同我们的类联系起来的关键了,PropertyChangedCallback 是个委托类型,它的签名也可以在MSDN查到:

 
 
public   delegate   void  PropertyChangedCallback(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e
)

为了理解这个函数,我们可以先看一下我对Text属性的实现:

 
 
     private   static   void  OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d 
as  MenuItem).OnTextPropertyChanged(e);
    }
    
void  OnTextPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
        
try
        {
            (GetTemplateChild(
" TB " as  TextBlock).Text  =  e.NewValue  as   string ;
        }
        
catch
        {
        }
    }
其实可以在static的OnTextPropertyChanged里面直接操作也是可以的,这个实现结构比较合理。可以看到,这个PropertyChangedCallback参数中,d就是属性所在的对象,而e则是属性改变时的信息,包括NewValue,OldValue等等,通常我们关心的是NewValue。

之后,我们要给我们的对象添加一个相应的C#属性,这个属性一定要和XAML里的属性名字完全一致,因为DependencyObject会用反射来访问这个属性。这个属性的getter和setter中不应该有多余的内容,应该只是简单的转发操作到DependencyObject,所以看起来就像下面这样:       

     public   string  Text
    {
        
get
        {
            
return  ( string ) base .GetValue(TextProperty);
        }
        
set
        {
            
base .SetValue(TextProperty, value);
        }
    }
然后我们还需要设置模板内容,把TextBlock加入。generic.xaml最终版如下:

 

 
 
< ResourceDictionary    
    
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"     
    xmlns:vsm
="clr-namespace:System.Windows;assembly=System.Windows"     
    xmlns:custom
="clr-namespace:System.Windows.Controls;assembly=SilverlightMenu" >
    
< Style  TargetType ="custom:MenuItem" >
        
< Setter  Property ="Template" >
            
< Setter.Value >
                
< ControlTemplate  TargetType ="custom:MenuItem" >
                    
< Grid >
                        
< vsm:VisualStateManager.VisualStateGroups >
                            
< vsm:VisualStateGroup  x:Name ="MouseOverStates" >
                                
< vsm:VisualState  x:Name ="MouseOver" >
                                    
< Storyboard >
                                        
< ObjectAnimationUsingKeyFrames  Storyboard.TargetName ="Bg"  Storyboard.TargetProperty ="Fill"  Duration ="0:0:0" >
                                            
< ObjectAnimationUsingKeyFrames.KeyFrames >
                                                
< DiscreteObjectKeyFrame  KeyTime ="0:0:0" >
                                                    
< DiscreteObjectKeyFrame.Value >
                                                        
< SolidColorBrush  Color ="#FFFF0000" ></ SolidColorBrush >
                                                    
</ DiscreteObjectKeyFrame.Value >
                                                
</ DiscreteObjectKeyFrame >
                                            
</ ObjectAnimationUsingKeyFrames.KeyFrames >
                                        
</ ObjectAnimationUsingKeyFrames >
                                    
</ Storyboard >
                                
</ vsm:VisualState >
                                
< vsm:VisualState  x:Name ="MouseOut" >
                                    
< Storyboard ></ Storyboard >
                                
</ vsm:VisualState >
                            
</ vsm:VisualStateGroup >
                        
</ vsm:VisualStateManager.VisualStateGroups >
                        
< Rectangle  Fill =" {TemplateBinding Background} "  StrokeThickness ="1"  RadiusX ="2"  RadiusY ="2"  x:Name ="Bg" />
                        
< TextBlock  x:Name ="TB" ></ TextBlock >
                    
</ Grid >
                
</ ControlTemplate >
            
</ Setter.Value >
        
</ Setter >
    
</ Style >
</ ResourceDictionary >  

这样看起来就基本完成了,然而,在测试代码里加入这个属性之后,却得不到我们想要的结果。

 
 
< UserControl  x:Class ="SilverlightMenuTest.Page"     
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"     
    xmlns:custom
="clr-namespace:System.Windows.Controls;assembly=SilverlightMenu"     
    Width
="400"  Height ="300" >
    
< Grid  x:Name ="LayoutRoot"  Background ="White"  Width ="400"  Height ="300" >
        
< custom:MenuItem  Text ="Hello"  Background ="Black"  Width ="100"  Height ="24" ></ custom:MenuItem >
    
</ Grid >
</ UserControl >

为什么呢,注意到,我们在属性变化的时候直接GetTemplateChild,并没有考虑到这时TextBlock是否已经创建,在SL中,Text属性设置其实是发生在Template生效之前的,所以设置Text的时候取不到TB元素。那么,我们就需要考虑在加载模板完成的时候,取出Text属性并且设置到TB上。

如上文所述,我们override OnApplyTemplate来获取想要的结果,最终版的MenuItem.cs代码:

 

using  System;
using  System.Net;
using  System.Windows;
using  System.Windows.Controls;
using  System.Windows.Documents;
using  System.Windows.Ink;
using  System.Windows.Input;
using  System.Windows.Media;
using  System.Windows.Media.Animation;
using  System.Windows.Shapes;
using  System.ComponentModel;
namespace  System.Windows.Controls
ExpandedBlockStart.gifContractedBlock.gif
{
    
public class MenuItem : Control
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
public static readonly DependencyProperty TextProperty;
        
static MenuItem()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            TextProperty 
= DependencyProperty.Register("Text"typeof(string), typeof(MenuItem), new PropertyMetadata(""new PropertyChangedCallback(MenuItem.OnTextPropertyChanged)));
        }

        
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            (d 
as MenuItem).OnTextPropertyChanged(e);
        }

        
void OnTextPropertyChanged(DependencyPropertyChangedEventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
try
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                (GetTemplateChild(
"TB"as TextBlock).Text = e.NewValue as string;
            }

            
catch
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{ }
        }

        
public string Text
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
return (string)base.GetValue(TextProperty);
            }

            
set
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
base.SetValue(TextProperty, value);
            }

        }

        
public MenuItem()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
this.DefaultStyleKey = typeof(MenuItem);
            
this.MouseEnter += new MouseEventHandler(OnMouseEnter);
            
this.MouseLeave += new MouseEventHandler(OnMouseLeave);
        }

        
public override void OnApplyTemplate()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
base.OnApplyTemplate();
            
try
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                var tb 
= GetTemplateChild("TB"as TextBlock;
                (GetTemplateChild(
"TB"as TextBlock).Text = this.Text;
            }

            
catch
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{ }
        }

        
public void OnMouseEnter(object sender, MouseEventArgs args)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            VisualStateManager.GoToState(
this"MouseOver"false);
        }

        
public void OnMouseLeave(object sender, MouseEventArgs args)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            VisualStateManager.GoToState(
this"MouseOut"false);
        }

    }

}


写到这里,我们的自定义Control开发大体流程以及关键技术问题就已经比较清楚了,虽然这个MenuItem距离实用还有很远的距离,各位应该可以自己完成它。感谢读完本文的你,Enjoy Silverlight Customized Control.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值