创建自定义控件
一个基本的Microsoft Silverlight project 假设当前project中的Extensible Application Markup Language (XAML)文件定义了一个特定属于应用程序的页. 然而, 一个control project 使用 XAML 文件来为所有调用它的对象提供user interface (UI)的定义. 这篇中将介绍怎么样往一个基本的control project中添加文件,来测试这个control.
运行 查看 要求 (可参阅Silverlight download site):
Microsoft Silverlight 1.1 Alpha.
Microsoft Visual Studio Code Name "Orcas" Beta 1.
Microsoft Silverlight Tools Alpha for Visual Studio Code Name "Orcas" Beta 1.
控件UI 和 object Model
创建一个control 包含两个任务: 定义UI和定义object model. UI 主要是在 XAML 文件中定义. 而object model 则在后台代码文件中定义. 这会经常存在前台的标记文件和后台的代码的交互, 因为也许你会在标记文件中初始为一个 Loaded handler 而然后在后台代码文件中定义它的handler. 又或者, 你可能在XAML中构造或给一个已经存在的elements命名, 然后你在后台代码文件中来得到它们的引用. 这样,代码文件和XAML文件都将编译到一个library中, 这个 library然后就可以被其它的applications 或者 projects使用了.
初见Silverlight Class Library Project
一个基本的 Silverlight class library project模板是没有 XAML 文件的, 而只有一个代码文件.你可以添加一个作为embedded资源的XAML文件到其中.
对Silverlight class library project进行修改
打开 Visual Studio. 在 File 菜单中, 点击 New Project.在 New Project 对话框中, 选择 Visual C#, 然后选择 Silverlight. 在 Templates 列表中, 选择 Silverlight Class Library.
在Solution Explorer, 右击 class1.cs 然后选择 Delete.
在Solution Explorer, 右击 project,选择 Add, New Item.
在 Add New Item 对话框中, 选择 Silverlight User Control. 命名为 MyLabel.xaml, 然后点击OK. 这将添加两个文件: 一个定义UI的 XAML 文件, 和一个编写代码的 .cs 文件.
注意
相比直接把XAML页加进来进行编译,把它作为嵌入资源却更合适.原因在接下来我们会进行介绍.
定义 UI
Canvas 是一个典型控件开始点. Canvas的 root element的内容将影响任何引用它的应用程序UI.
你定义了各种控制UI的作为XAML root的子elements. 在示例中, 你将只添加一个 element,但这将足够来观看 control 的运行. 一些复杂的control samples可能需要添加数百行代码到 XAML 中来, 这包括预定义的用于收集UI交互的基于 event-driven的animations , 很深层次的element 嵌套, 图象等.
在XAML中定义 UI
打开 MyLabel.xaml 进行编辑.将下面的XAML内容复制到 Canvas 标记根中.
CS
<TextBlock x:Name="tb"></TextBlock>
<TextBlock x:Name="tb"></TextBlock>
保存文件.
在QuickStart这个示例中,你所做的就是这些了. 注意 x:Name 属性. 你应该一般都会要放入x:Name到任何一个重要的element中, 因为在后台代码文件中使用name references 是你牢牢控制UI控件的一个重要手段.
获取 Object References
在你开始添加自定义属性前,你需要获得一些 XAML中对象的引用,这样你才可以在代码文件中使用. 当你打开后台代码文件时, 你可以看到类已经定义了一个默认的构造函数. 这块就说明了为什么要把XAML文件作为一个嵌入资源来对待了: 代码文件将从assembly资源中以流的方式来访问 XAML文件. 这个stream将作为Control 类的方法 InitializeFromXaml的重要参数输入字符串,这个方法就说明了XAML和其后台代码文件是怎么样在最开始就进行挂勾的. 其实, 模板生成的后台构造函数还做了其它的一些工作: InitializeFromXaml 实际上是有一个返回值的, 这个值对你以后的对XAML中各种对象的引用将是有用的,你可以在接下来的过程中访问object tree.
在你的control中获得对象引用
打开 MyLabel.xaml.cs 或 MyLabel.xaml.vb 进行编辑.
添加一个命名为implementationRoot的FrameworkElement类型的变量. 然后改变InitializeFromXaml 调用,把它的返回值赋给implementationRoot. 当你做完这些后,你的代码应该和下面的例子一样了.
接下来, 把 implementationRoot 作为object tree的开始点,这样你就可以得到TextBlock element的引用了. 如果你不调用 FindName 而使用 "this" 或者 "Me" ( Control base), 则不对,因为这么还不存在object tree. object tree 只在载入完XAML文件后生成的. 在这儿, 为TextBlock添加一个变量.
CS
TextBlock tb;
VB
Private m_tb As TextBlock
加入第三行代码,来调用 FindName的构造函数,这样得到了TextBlock的引用.
CS
tb = implementationRoot.FindName("tb") as TextBlock;
VB
m_tb = m_implementationRoot.FindName("tb")
为控件定制属性和事件
一般情况下, 控件是没有暴露任何属性或事件的.你可以定义公有的属性(使用public setters),这样你无论是在标记文件或是代码文件中都可以对控件的实例进行设置.同样的道理,你也可以定义共用的事件. 在本篇 QuickStart中, 你将跟着示例对FrameworkElement的两个基本的外观尺寸属性进行设置 , Height 和 Width.你将添加两个自定义的属性到MyLabel,这将应用到TextBlock 的属性值.
添加属性
在 MyLabel.xaml.cs 或 MyLabel.xaml.vb, 添加MyLabel的Height属性,以此来设置外面的implementationRootCanvas 宽度. 你必须隐藏这个属性,因为这个属性已经在Control中存在了, 但是 Control.Height 却没有和你的控件产生任何联系.
CS
public virtual new double Height{get{return implementationRoot.Height;}set{implementationRoot.Height = value;UpdateLayout();}}
VB
Public Overridable Shadows Property Height() As DoubleGetReturn m_implementationRoot.HeightEnd GetSet(ByVal value As Double)m_implementationRoot.Height = valueUpdateLayout()End SetEnd Property
添加一个带NEW关键字的属性Width 给 MyLabel.
CS
public virtual new double Width{get{return implementationRoot.Width;}set{implementationRoot.Width = value;UpdateLayout();}}
VB
Public Overridable Shadows Property Width() As DoubleGetReturn m_implementationRoot.WidthEnd GetSet(ByVal value As Double)m_implementationRoot.Width = valueUpdateLayout()End SetEnd Property
你需要再提供一个重要的属性给MyLabel, 这就是它的显示文字. 定义一个 Text 属性给 MyLabel, 这样你就可以为你的控件TextBlock设置Text属性了(该控件是和tb引用关系的).
CS
public String Text{get{return tb.Text;}set{tb.Text = value;UpdateLayout();}}
VB
Public Property Text() As StringGetReturn m_tb.TextEnd GetSet(ByVal value As String)m_tb.Text = valueUpdateLayout()End SetEnd Property
你也可以定义更多的属性,比如设置文字颜色的属性. , 在这种情况下,你需要将 Brush 类型强行转换为 SolidColorBrush来避免设置属性的局限性, 当然,设置的属性值应该符合element 语法.
CS
public SolidColorBrush LabelColor{get{return (SolidColorBrush)tb.Foreground;}set{tb.Foreground = (SolidColorBrush)value;}}
VB
Public Property LabelColor() As SolidColorBrushGetReturn m_tb.ForegroundEnd GetSet(ByVal value As SolidColorBrush)m_tb.Foreground = DirectCast(value, SolidColorBrush)End SetEnd Property
保存 MyLabel.Xaml.cs (或 MyLabel.xaml.vb) 然后build 这个 project.
目前,你还不能对你的project进行debug 或运行, 因为你的control还只是一个library,它并没有被其它的Silverlight页使用.下面,我们将添加一些test文件到你的project. 别外一个可选的方法是,创建一个包含多个project 的solutions,然后再将control project的assembly加进来,然后再使用.
测试你的 Control
对control进行测试
新开一个Visual Studio. 创建一个新的基本的 Silverlight project (查看 怎么来创建一个Silverlight Project). 你怎么样 命名没有关系, 因为接下来你将会对project中的文件进行整理,最后加入到主project中来.
打开 Explorer. 到 你刚创建的project 文件夹. 复制这些 文件: Default.html, CreateSilverlight.js, Silverlight.js, 和 Page.xaml. 在本篇 QuickStart中, 你不需要 Page.xaml.cs.
导航 Explorer 到 SilverlightCustomControl project 文件夹. 粘贴这四个文件.
在创建SilverlightCustomControl project的 Visual Studio 程序中 , 打开 Solution Explorer. 右击 project, 然后选择Add, Existing Item.
在Add Existing Item 对话框中,选择四个你才粘贴的 文件.点击 OK.
在 Solution Explorer, 右击 project, 然后选择Properties.
在Properties 窗口上, 点击Debug 栏. 改变 Start Action 为 Specific Page, 设置值为 Default.html.
打开 Page.xaml 进行编辑. 删除x:Class 属性 和 root element中的Loaded handler . (You would have needed the code file and a separate build action to support this code, but you do not need any code to merely instantiate your control in XAML.)
现在你需要添加一行xmlns 来引用你的自定义 control assembly. 添加以下属性到你的根tag下:
xmlns:my="clr-namespace:SilverlightSampleControl;assembly=ClientBin/SilverlightSampleControl.dll".现在添加一个特定的 element将放置一个MyLabel 到你的页面中来. 但是在element名字前你应该加一个前缀my ,这样编译器才知道怎么去找到它的定义. 你也可以设置你为MyLabel定义的属性值.
CS
<my:MyLabel Height="30" Width="200" Text="Hello...." LabelColor="Blue"/>
VB
<my:MyLabel Height="30" Width="200" Text="Hello...." LabelColor="Blue"/>
编译并对application进行debug .你可以看到自定义的 MyLabel 显示了. 你也可以添加断点,比如在 MyLabel 的构造函数处, 或者在handlers 中添加,来调试用户交互过程.
隐藏继承属性
上面的示例定义了两个属性 (Height 和 Width) 它们通过使用关键字new有意的隐藏了基类的相应属性,这种方法在Silverlight SDK中的其它Sample中也可以看到. 隐藏属性并不是一个最佳的办法,但是却是目前唯一可行的办法. Control 从FrameworkElement继承 Height 和 Width , 但是 FrameworkElement 却不清楚 Control 基类的实现定义. XAML 属性试图来设置基类的属性.除了隐藏这些从基类来的属性外,你在被访问前应该同步这些属性.在这个示例中, UpdateLayout 方法就在相应的属性申明中被调用了,而类的构造函数也设置了基类被隐藏属性的,以使其同步.