Silverlight 2 Customized Control 开发
Customized Control 和 User Control
相信大家比較熟悉使用Silverlight的User Control,在VS2008的Silverlight插件中,可以通過(guò)添加新項(xiàng)(Add new Item)創(chuàng)建一個(gè)User Control,而且Silverlight在網(wǎng)頁(yè)中嵌入的本身就是一個(gè)User Control。
本文中講的是如何開(kāi)發(fā)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則比較類(lèi)似這些原生Control的組合體。
準(zhǔn)備工作
在閱讀本文之前至少應(yīng)該掌握:
- 創(chuàng)建和使用簡(jiǎn)單Silverlight程序
- 使用和編輯Silverlight控件模板
- 使用Silverlight的Visual State
- C#基本知識(shí)
- 使用Visual studio的項(xiàng)目和解決方案
推薦鏈接
- http://msdn.microsoft.com/zh-cn/magazine/cc721611.aspx?
一篇for SL2 beta 2的自定義控件開(kāi)發(fā)介紹,有些地方需要變動(dòng)才能正常工作 - http://silverlight.net/GetStarted/?
官方的Silverlight入門(mén), 其中有SL組成員blog的鏈接 - http://msdn.microsoft.com/en-us/library/cc278068(VS.95).aspx?
MSDN的Control Customization介紹 - http://msdn.microsoft.com/en-us/library/cc838194(VS.95).aspx?
MSDN的Silverlight類(lèi)庫(kù)參考
環(huán)境
- Visual Studio 2008 with Service Pack 1
- Silverlight Tools for Visual Studio 2008 SP1
- [optional]Expression Blend 2 with Service Pack 1
(提示:連接給出的默認(rèn)是英文,大家可以根據(jù)習(xí)慣選擇。開(kāi)發(fā)工具和Service Pack語(yǔ)言必須對(duì)應(yīng)。)
(注意:Visual Studio和Expression Blend不是免費(fèi)工具,這里給出的是試用版本鏈接,用于商業(yè)開(kāi)發(fā)可能存在風(fēng)險(xiǎn)。)
?
接下來(lái)我們可以開(kāi)始Customized Control之旅了。
(因?yàn)椴幌朐诨A(chǔ)的地方浪費(fèi)時(shí)間,所以前面部分會(huì)非常簡(jiǎn)略,請(qǐng)參考推薦鏈接)
?
一、創(chuàng)建解決方案
Customized Control一般會(huì)編寫(xiě)成DLL文件,所以創(chuàng)建工程的時(shí)候應(yīng)該選擇Silverlight Class Library,同時(shí)為了調(diào)試,我們還要在解決方案中再建立一個(gè)新的Silverlight Application project。如果希望用Web站點(diǎn)來(lái)調(diào)試Silverlight Application,就還需要?jiǎng)?chuàng)建解決方案中的第三個(gè)project,推薦使用動(dòng)態(tài)生成的HTML。然后需要在解決放案屬性中定義好項(xiàng)目的依賴(lài)關(guān)系以及start up project。考慮到這部分大家都比較了解,就不再贅述。
二、創(chuàng)建Control的C#類(lèi)
創(chuàng)建的Silverlight Class Library中默認(rèn)會(huì)有一個(gè)Class1.cs,這是一個(gè)普通的C#類(lèi),與Silverlight并無(wú)關(guān)系,可以選擇保留它利用VS的重構(gòu)功能換成喜歡的名字,也可以刪掉它再重新建立一個(gè)類(lèi)??傊覀兊腃lass Library中只需要保留一個(gè)我們要開(kāi)發(fā)的控件名字的類(lèi)就可以了,比如我打算開(kāi)發(fā)一個(gè)MenuItem。然后我們要繼承Control類(lèi),當(dāng)然也可以繼承任何其它繼承了Control的類(lèi)。最后代碼看起來(lái)像是這樣:
????public?class?MenuItem?:?Control????{
????????public?MenuItem()
????????{
????????}
????}
?
OK,這樣我們就制作出了一個(gè)最基本的Customized Control。
三、調(diào)用Control
接下來(lái)我們希望在Silverlight Application中調(diào)用生成的Customized Control,首先要添加引用,因?yàn)楝F(xiàn)在在同一Solution中,所以可以選擇項(xiàng)目引用。
然后我們要把我們類(lèi)庫(kù)的clr命名空間加入到XAML的命名空間當(dāng)中,所以修改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寫(xiě)我們類(lèi)庫(kù)中使用的namespace,assembly寫(xiě)類(lèi)庫(kù)項(xiàng)目的名稱(chēng)就可以了,XML命名空間可以是我們喜歡的任何名字,這里用了custom。
然后我們可以在XAML中使用我們的新控件了,控件名稱(chēng)和C#類(lèi)名相同,前面要帶上命名空間,代碼最后類(lèi)似這樣:
<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>
接下來(lái)我們可以運(yùn)行程序了,但是當(dāng)然結(jié)果是一片空白。
四、添加模板
這個(gè)一片空白的Control顯然沒(méi)有任何意義,接下來(lái)我們要讓它變成可見(jiàn)的。只需要設(shè)置好MenuItem的Template屬性就可以了,可以看到這里設(shè)置了Width和Height,Template中的元素會(huì)自動(dòng)把自己限制在這個(gè)范圍內(nèi)。
<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> 顯示這段代碼我們就可以看到一個(gè)黑色的圓角矩形了。
五、綁定屬性
這樣我們有了一個(gè)可見(jiàn)的控件,但是這個(gè)控件的任何屬性(如Background之類(lèi)的)都沒(méi)有辦法發(fā)生作用。現(xiàn)在我們可以想想辦法讓矩形的顏色是控件的背景色。使用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。這樣,我們就可以通過(guò)控件的屬性控制控件的顯示效果了。
六、添加默認(rèn)模板
當(dāng)然你不能要求使用者總是編輯你的Control的模板,所以我們必須要有一個(gè)默認(rèn)的模板。VisualStudio總是會(huì)從項(xiàng)目的themes文件夾下查找generic.xaml作為默認(rèn)模板,注意這個(gè)項(xiàng)目是控件的類(lèi)所在的項(xiàng)目,也就是我們建立的Silverlight Class Library。
如果你看過(guò)之前的介紹Silverlight自定義控件開(kāi)發(fā)的文章,你會(huì)發(fā)現(xiàn),這個(gè)地方Silverlight 2 RTW版和Silverlight 2 beta 2或者beta 1是不兼容的,我們必須將generic.xaml放到themes目錄下。beta 2版本是放在根目錄下,而beta 1版本是放在根目錄下且必須修改generic.xaml的屬性才能生效。
一個(gè)空的generic.xaml的內(nèi)容如下(但是VS不會(huì)自動(dòng)為你生成這個(gè)文件,需要手動(dòng)添加):
<ResourceDictionary????xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
????xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</ResourceDictionary>
我們需要通過(guò)style把新控件的模板放進(jìn)去,但之前應(yīng)該先把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類(lèi)似這樣(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>
?
但僅僅如此控件是不會(huì)有任何變化的,要在MenuItem的構(gòu)造函數(shù)中加入一句:
this.DefaultStyleKey?=?typeof(MenuItem); 這樣我們的Control就更接近原生Control了,可以這樣調(diào)用: <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>
再運(yùn)行就可以看到黑色矩形了。
七、綁定事件
到此為止,我們的Control還只能是顯示出來(lái)看看,沒(méi)有任何交互性,下面我們就來(lái)給它添加一定的交互性。因?yàn)镃ontrol本身提供了一些事件,所以我們很容易捕獲這些事件并作出響應(yīng)。對(duì)于Control來(lái)說(shuō),我們只需要添加事件處理函數(shù)并且綁定到相應(yīng)事件就可以了。
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中設(shè)置斷點(diǎn)來(lái)觀察事件響應(yīng)情況。
八、操作模板成員
現(xiàn)在我們知道了如何通過(guò)事件處理函數(shù)響應(yīng)事件,我們可以在其中改變控件自身的狀態(tài),比如,當(dāng)鼠標(biāo)劃過(guò)時(shí),改變控件的背景。前面說(shuō)到背景顯示是通過(guò)一個(gè)Rectangle實(shí)現(xiàn)的,那么要改變背景就需要取到這個(gè)Rectangle,我們要使用一個(gè)函數(shù)GetTemplateChild來(lái)取這個(gè)元素。注意因?yàn)镚etTemplateChild并不一定總能取到元素,而且元素類(lèi)型并不一定是我們期望的(了解Template的朋友會(huì)知道,Template是一個(gè)可以被EndUser修改的屬性,所以不能以任何形式斷言控件Template的結(jié)構(gòu)和其中的元素名),所以一定要處理異常。下面的代碼是讓鼠標(biāo)在控件上時(shí)背景變成紅色:
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)
????{
????????try
????????{
????????????Rectangle?BgRect?=?GetTemplateChild("Bg")?as?Rectangle;
????????????BgRect.Fill?=?new?SolidColorBrush(Color.FromArgb(255,?255,?0,?0));
????????}
????????catch
????????{
????????}
????}
????public?void?OnMouseLeave(object?sender,?MouseEventArgs?args)
????{
????????try
????????{
????????????Rectangle?BgRect?=?GetTemplateChild("Bg")?as?Rectangle;
????????????BgRect.Fill?=?new?SolidColorBrush(Color.FromArgb(255,?0,?0,?0));
????????}
????????catch
????????{
????????}
????}
} 有些時(shí)候,我們希望在模板生效的時(shí)候就對(duì)某些模板成員進(jìn)行操作,如綁定事件,調(diào)整屬性等,就需要一個(gè)事件OnApplyTemplate,我們只能通過(guò)override父類(lèi)的OnApplyTemplate來(lái)響應(yīng)模板生效事件:
?
九、添加可視狀態(tài)(VisualState)
上面的代碼實(shí)現(xiàn)了我們想要的功能,然而卻有很多問(wèn)題:
那么如何解決呢?很顯然,跟使用Silverlight開(kāi)發(fā)Web應(yīng)用一樣,答案就是使用VisualState。首先我們要為我們的控件模板定義VisualState,因?yàn)閂isualState必須定義在root element上,所以這里我稍微做了一點(diǎn)改動(dòng),給模板加了一個(gè)容器。關(guān)于如何定義和使用VisualState,早有不少高手寫(xiě)的很清楚,這里就不再重復(fù)了。下面的Visual定義了2個(gè)互斥的狀態(tài)MouseOver和MouseOut,MouseOver狀態(tài)將背景Rectangle顏色變紅,MouseOut則恢復(fù)原狀。
<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,我們的事件就可以寫(xiě)的很簡(jiǎn)單了:
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);
????}
} 這樣運(yùn)行下試試看,是不是很cool?
?
十、添加自定義屬性
接下來(lái)到了最后的部分了,也是最少資料涉及的部分,Silverlight Customized Control自定義屬性。下面我們來(lái)以一個(gè)Text屬性為例演示如何創(chuàng)建一個(gè)自定義屬性,這個(gè)例子將會(huì)用到一些前面的內(nèi)容。首先我們要為DependencyObject的屬性聲明一個(gè)公有的DependencyProperty,這個(gè)屬性是靜態(tài)的,可以用于數(shù)據(jù)綁定。(還記得數(shù)據(jù)綁定中使用的DependencyProperty吧,就是這樣的哦^^)
?
????public?static?readonly?DependencyProperty?TextProperty;?
靜態(tài)屬性在靜態(tài)構(gòu)造函數(shù)中初始化,這里有一點(diǎn)復(fù)雜,首先看完整代碼:
????static?MenuItem()????{
????????TextProperty?=?DependencyProperty.Register("Text",?typeof(string),?typeof(MenuItem),?new?PropertyMetadata(false,?new?PropertyChangedCallback(MenuItem.OnTextPropertyChanged)));
????} DependencyProperty應(yīng)該由DependencyProperty.Register創(chuàng)建,在MSDN中,這個(gè)函數(shù)的原型如下: public?static?DependencyProperty?Register(
????string?name,
????Type?propertyType,
????Type?ownerType,
????PropertyMetadata?typeMetadata
) ?
其中name是屬性在XAML中使用的名字,propertyType則是屬性的類(lèi)型,注意這個(gè)屬性理論上可以是任何類(lèi)型,但是一般只使用整數(shù)、布爾、字符串、還有UI元素這些類(lèi)型,其它類(lèi)型需要定義復(fù)雜的字符串到對(duì)象的屬性文法作為轉(zhuǎn)換規(guī)則(之后可能會(huì)單寫(xiě)一篇blog講converter)。ownerType就是我們自己定義的類(lèi)了。typeMetadata有點(diǎn)復(fù)雜,PropertyMetadata構(gòu)造函數(shù)有3個(gè)重載,提供選擇指定defaultValue和propertyChangedCallback中任意一個(gè)或者同時(shí)指定2個(gè)。原型如下:
public?PropertyMetadata(????Object?defaultValue,
????PropertyChangedCallback?propertyChangedCallback
)
?
defaultValue是屬性的默認(rèn)值沒(méi)什么可說(shuō),propertyChangedCallback也很簡(jiǎn)單,就是當(dāng)屬性改變時(shí)的處理函數(shù),這個(gè)是把屬性同我們的類(lèi)聯(lián)系起來(lái)的關(guān)鍵了,PropertyChangedCallback 是個(gè)委托類(lèi)型,它的簽名也可以在MSDN查到:
public?delegate?void?PropertyChangedCallback(????DependencyObject?d,
????DependencyPropertyChangedEventArgs?e
)
為了理解這個(gè)函數(shù),我們可以先看一下我對(duì)Text屬性的實(shí)現(xiàn):
????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
????????{
????????}
????} 其實(shí)可以在static的OnTextPropertyChanged里面直接操作也是可以的,這個(gè)實(shí)現(xiàn)結(jié)構(gòu)比較合理??梢钥吹?#xff0c;這個(gè)PropertyChangedCallback參數(shù)中,d就是屬性所在的對(duì)象,而e則是屬性改變時(shí)的信息,包括NewValue,OldValue等等,通常我們關(guān)心的是NewValue。
之后,我們要給我們的對(duì)象添加一個(gè)相應(yīng)的C#屬性,這個(gè)屬性一定要和XAML里的屬性名字完全一致,因?yàn)镈ependencyObject會(huì)用反射來(lái)訪問(wèn)這個(gè)屬性。這個(gè)屬性的getter和setter中不應(yīng)該有多余的內(nèi)容,應(yīng)該只是簡(jiǎn)單的轉(zhuǎn)發(fā)操作到DependencyObject,所以看起來(lái)就像下面這樣:???????
????public?string?Text????{
????????get
????????{
????????????return?(string)base.GetValue(TextProperty);
????????}
????????set
????????{
????????????base.SetValue(TextProperty,?value);
????????}
????} 然后我們還需要設(shè)置模板內(nèi)容,把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>?
這樣看起來(lái)就基本完成了,然而,在測(cè)試代碼里加入這個(gè)屬性之后,卻得不到我們想要的結(jié)果。
<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>
為什么呢,注意到,我們?cè)趯傩宰兓臅r(shí)候直接GetTemplateChild,并沒(méi)有考慮到這時(shí)TextBlock是否已經(jīng)創(chuàng)建,在SL中,Text屬性設(shè)置其實(shí)是發(fā)生在Template生效之前的,所以設(shè)置Text的時(shí)候取不到TB元素。那么,我們就需要考慮在加載模板完成的時(shí)候,取出Text屬性并且設(shè)置到TB上。
如上文所述,我們override OnApplyTemplate來(lái)獲取想要的結(jié)果,最終版的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
{
????public?class?MenuItem?:?Control
????{
????????public?static?readonly?DependencyProperty?TextProperty;
????????static?MenuItem()
????????{
????????????TextProperty?=?DependencyProperty.Register("Text",?typeof(string),?typeof(MenuItem),?new?PropertyMetadata("",?new?PropertyChangedCallback(MenuItem.OnTextPropertyChanged)));
????????}
????????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
????????????{?}
????????}
????????public?string?Text
????????{
????????????get
????????????{
????????????????return?(string)base.GetValue(TextProperty);
????????????}
????????????set
????????????{
????????????????base.SetValue(TextProperty,?value);
????????????}
????????}
????????public?MenuItem()
????????{
????????????this.DefaultStyleKey?=?typeof(MenuItem);
????????????this.MouseEnter?+=?new?MouseEventHandler(OnMouseEnter);
????????????this.MouseLeave?+=?new?MouseEventHandler(OnMouseLeave);
????????}
????????public?override?void?OnApplyTemplate()
????????{
????????????base.OnApplyTemplate();
????????????try
????????????{
????????????????var?tb?=?GetTemplateChild("TB")?as?TextBlock;
????????????????(GetTemplateChild("TB")?as?TextBlock).Text?=?this.Text;
????????????}
????????????catch
????????????{?}
????????}
????????public?void?OnMouseEnter(object?sender,?MouseEventArgs?args)
????????{
????????????VisualStateManager.GoToState(this,?"MouseOver",?false);
????????}
????????public?void?OnMouseLeave(object?sender,?MouseEventArgs?args)
????????{
????????????VisualStateManager.GoToState(this,?"MouseOut",?false);
????????}
????}
}
寫(xiě)到這里,我們的自定義Control開(kāi)發(fā)大體流程以及關(guān)鍵技術(shù)問(wèn)題就已經(jīng)比較清楚了,雖然這個(gè)MenuItem距離實(shí)用還有很遠(yuǎn)的距離,各位應(yīng)該可以自己完成它。感謝讀完本文的你,Enjoy Silverlight Customized Control.
總結(jié)
以上是生活随笔為你收集整理的Silverlight 2 Customized Control 开发的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Windows NT体系结构
- 下一篇: vlan间ACL和VACL的区别