复合控件的创建
创建定制控件的第三种方法是组合二个或二个以上的现有的控件。在下面的例子中,读者将以合同编程人员的身份出现,而我则是客户,我希望读者能够开发一个稍微复杂一些的控件,使我能够用来记录收到的对我的书的询价。
作为客户,我将要求读者开发一个控件,使我能够输入一本或多本书籍,每当点击一本书时,控件就会记录下对该书的点击次数,如下图所示:
这一程序的.aspx文件如下所示,除@ Page命令外,该程序的C#和VB程序是相同的:
在上面的代码中需要注意的是,BookInquiryList组件中包含许多BookCounter元素,其中有一个BookCounter元素是对应着我希望记录的书籍。这个控件非常灵活,我可以对任意数量的书进行记录。每个BookCounter元素有一个用来显示被记录书籍名字的BookName属性。
从图9中我们可以看到,每本书都由一个CountedButton定制控件进行记录,但.aspx文件中没有CountedButton控件的定义,它被完整地封装在了BookCounter定制控件中。
整个体系结构如下所示:
使用BookInquiry对象的目的有二个:它是BookCounter对象的容器;它负责绘制它本身并确保它包含的BookCounter对象能够按需求绘制自己。
我们需要对CountedButton控件进行一些很小的修改,下面分别是C#和VB.NET版的CountedButton控件。
修改后的CountedButton.cs文件
using System; using System.Web.UI; using System.Web.UI.WebControls; using System.ComponentModel; namespace CustomControls { // 由System.Web.UI.WebControls.Button派生出的定制控件 public class CountedButton : System.Web.UI.WebControls.Button { private string displayString; // 缺省的构造器 public CountedButton( ) { displayString = "clicks"; InitValues( ); } // 重载,显示字符串 public CountedButton(string displayString) { this.displayString = displayString; InitValues( ); } // 由构造器调用的函数 private void InitValues( ) { if (ViewState["Count"] == null) ViewState["Count"] = 0; this.Text = "Click me"; } // Count是ViewState中的一个特性 public int Count { get { // 在构造器中初始化,不能是NULL return (int) ViewState["Count"]; } set { ViewState["Count"] = value; } } // 覆盖OnClick事件处理程序,增大Count变量的值,并在更新按钮上的文本后调用基础类中的方法 protected override void OnClick(EventArgs e) { ViewState["Count"] = ((int)ViewState["Count"]) + 1; this.Text = ViewState["Count"] + " " + displayString; base.OnClick(e); } } } |
修改后的CountedButton.vb文件
Imports System.ComponentModel Imports System.Web.UI Imports System.Web.UI.WebControls ' 从System.Web.UI.WebControls.Button中派生的定制控件 Public Class CountedButton Inherits System.Web.UI.WebControls.Button Private displayString As String ' 构造器对ViewState进行初始化 Public Sub New( ) displayString = "clicks" Init( ) End Sub ' 重载,显示字符串 Public Sub New(ByVal displayString As String) Me.displayString = displayString Init( ) End Sub ' 由构造器调用的方法 Private Shadows Sub Init( ) If ViewState("Count") = Is Nothing Then ViewState("Count") = 0 Me.Text = "Click me" End If End Sub ' Count是ViewState中的一个特性 Public Property Count( ) As Integer Get Return CInt(ViewState("Count")) End Get Set(ByVal Value As Integer) ViewState("Count") = Value End Set End Property ' 覆盖OnClick事件处理程序,增大Count变量的值,并在更新按钮上的文本后调用基础类中的方法 Protected Overrides Sub OnClick(ByVal e As EventArgs) ViewState("Count") = CInt(ViewState("Count")) + 1 Me.Text = CStr(ViewState("Count") & " " & displayString MyBase.OnClick(e) End Sub End Class?) |
由于我们希望按钮上显示“5 Inquiries”而不是“5 clicks”字符串,因此必须修改OnClick方法中修改按钮字符串文本的代码:
this.Text = ViewState["Count"] + " " + displayString; |
相应的VB.NET代码是:
Me.Text = ViewState("Count") & " " & displayString |
我们还使用了一个private性质的成员变量displayString来存储传递给构造器中的数值:
private string displayString; |
在VB.NET中的代码为:
Private displayString As String |
我们必须在构造器中设置这一字符串。为了保护已经使用了缺省的构造器的代码,我们必须重载构造器,新增加一个带有字符串参数的构造器:
public CountedButton(string displayString) { this.displayString = displayString; Init( ); } |
在VB.NET中的代码是:
Public Sub New(ByVal displayString As String) Me.displayString = displayString Initialize( ) End Sub |
我们可以对缺省的构造器进行修改,将displayString成员变量有值设置为一个合理的缺省值。其C#代码如下:
public CountedButton( ) { displayString = "clicks"; InitValues( ); } |
相应的VB.NET代码是:
Public Sub New( ) displayString = "clicks" Init( ) End Sub
|
二个构造器的代码都没有考虑private性质的辅助方法Init,它能够保证Count特性被初始化为0,并设置最初时按钮显示的字符串:
private void Init( ) { if (ViewState["Count"] == null) ViewState["Count"] = 0; this.Text = "Click me"; } |
在VB.NET中,相应的代码为:
Private Shadows Sub Init( ) If ViewState("Count") = Nothing Then ViewState("Count") = 0 Me.Text = "Click me" End If End Sub |
作了上述的修改后,我们就可以在第一个复合控件━━BookCounter中使用CountedButton了。
BookCounter复合控件用于记录和显示对某一本书查询的次数,下面分别是C#和VB.NET版的BookCounter复合控件的源代码:
C#版本的BookCounter控件源文件:BookCounter.cs
using System; using System.Web.UI; using System.Web.UI.WebControls; using System.ComponentModel; namespace CustomControls { public class BookCounter : System.Web.UI.WebControls.WebControl, INamingContainer { // 初始化按钮成员 CountedButton btn = new CountedButton("inquiries"); public string BookName { get { return (string) ViewState["BookName"]; } set { ViewState["BookName"] = value; } } public int Count { get { return btn.Count; } set { btn.Count = value; } } public void Reset( ) { btn.Count = 0; } protected override void CreateChildControls( ) { Controls.Add(btn); } } } |
VB.NET版的BookCounter控件的源代码: BookCounter.vb
Imports System Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.ComponentModel Public Class BookCounter Inherits System.Web.UI.WebControls.WebControl Implements INamingContainer ' 初始化按钮成员 Public btn As CountedButton = New CountedButton("inquiries") Public Property BookName( ) As String Get Return CStr(ViewState("BookName")) End Get Set(ByVal Value As String) ViewState("BookName") = Value End Set End Property Public Property Count( ) As Integer Get Return btn.Count End Get Set(ByVal Value As Integer) btn.Count = Value End Set End Property Public Sub Reset( ) btn.Count = 0 End Sub Protected Overrides Sub CreateChildControls( ) Controls.Add(btn) End Sub End Class |
BookCounter类中首先需要注意的是它实现了INamingContainer界面,这是一个没有方法的“记分器”界面。这一界面的目的是识别创建新的ID名字空间的容器控件,保证所有的子控件都有对于应用程序是唯一的ID。
BookCounter类包含有CountedButton的一个实例:
CountedButton btn = new CountedButton("inquiries"); |
或:
Public btn As CountedButton = New CountedButton("inquiries") |
btn成员是在由System.Control继承的CreateChildControls方法中被初始化的:
protected override void CreateChildControls( ) { Controls.Add(btn); } |
在VB.NET中,相应的代码为:
Protected Overrides Sub CreateChildControls( ) Controls.Add(btn) End Sub |
CreateChildControls是在绘制的准备工作时被调用的,它使BookCounter类能够添加btn对象作为被包含的控件。
BookCounter无需覆盖Render方法,它唯一需要绘制的是CountedButton。Render缺省的操作是绘制所有的子控件,因此无需对它作任何修改就能完成其功能。
BookCounter还有二个特性:BookName和Count。BookName是在控件中显示的一个字符串,而且通过ViewState进行管理,其C#代码如下所示:
public string BookName { get { return (string) ViewState["BookName"]; } set { ViewState["BookName"] = value; } } |
相应的VB.NET源代码为:
Public Property BookName( ) As String Get Return CStr(ViewState("BookName")) End Get Set(ByVal Value As String) ViewState("BookName") = Value End Set End Property |
Count是对一本特定的书查询的数量,记录这一数量的任务由CountedButton完成。其C#代码如下所示:
public int Count { get { return btn.Count; } set { btn.Count = value; } } |
相应的VB.NET源代码为:
Public Property Count( ) As Integer Get Return btn.Count End Get Set(ByVal Value As Integer) btn.Count = Value End Set End Property |
无需将该值放在ViewState中,因为按钮本身就可以对其数据进行管理。
每个BookCounter对象都被包含在BookInquiryList控件集合中,该控件没有特性,只有一个Render方法,其C#和VB.NET代码如下所示:
[ControlBuilderAttribute(typeof(BookCounterBuilder)),ParseChildren(false)] public class BookInquiryList : System.Web.UI.WebControls.WebControl, INamingContainer { protected override void Render(HtmlTextWriter output) { int totalInquiries = 0; BookCounter current; // 输出头部 output.Write(" "cellspacing='1' align = 'center' >"); output.Write(" |
");
}
}
Imports System.ComponentModel Imports System.Web.UI _ Public Class BookInquiryList Inherits System.Web.UI.WebControls.WebControl Implements INamingContainer Protected Overrides Sub Render(ByVal output As HtmlTextWriter) Dim totalInquiries As Integer = 0 ' 输出头部 output.Write(" "cellspacing='1' align = 'center' >") output.Write(" |
")
End Sub
End Class
Friend Class BookCounterBuilder
Inherits ControlBuilder
Public Overrides Function GetChildControlType( _
ByVal tagName As String, ByVal attributes As IDictionary) As Type
If tagName = "BookCounter" Then
Dim x As BookCounter
Return x.GetType
Else
Return Nothing
End If
End Function
Public Overrides Sub AppendLiteralString(ByVal s As String)
End Sub
End Class
BookCounter类必须与BookInquiryClass联合使用,ASP.NET才能将.aspx网页中的元素转换为适当的代码。完成这一工作需要用到ControlBuilder属性:
[ControlBuilderAttribute(typeof(BookCounterBuilder)),ParseChildren(false)]
ControlBuilderAttribute的参数是一个通过传递BookCounterBuilder类获取的Type对象。下面是使用C#和VB.NET编写的BookCounterBuilder源代码:
internal class BookCounterBuilder : ControlBuilder { public override Type GetChildControlType( string tagName, IDictionary attributes) { if (tagName == "BookCounter") return typeof(BookCounter); else return null; } public override void AppendLiteralString(string s) { } } VB.NET版的BookCounterBuilder Friend Class BookCounterBuilder Inherits ControlBuilder Public Overrides Function GetChildControlType(_ ByVal tagName As String, ByVal attributes As Idictionary) As Type If tagName = "BookCounter" Then Dim x As BookCounter Return x.GetType Else Return Nothing End If End Function Public Overrides Sub AppendLiteralString(ByVal s As String) End Sub End Class |
ASP.NET将使用由ControlBuilder中派生出的BookCounterBuilder来判断由BookCounter标记批指示的对象的类型。通过这种结合,每个BookCounter对象才能被实例化,并添加到BookInquiryClass的控件集合中。
第二个参数ParseChildren必须被设置成false,让ASP.NET知道我们已经对子属性进行了处理,无需再对它进行进一步地解析了。false值表明子属性不是外部对象的特性,而仅仅是子控件的特性。
BookInquiryClass中的唯一方法是覆盖Render的方法,Render的作用是使用由每个BookCounter子控件管理的数据绘制图9中的表格。
BookInquiryClass提供了一个总的查询数字,如下图所示:
Figure 14-16. Total inquiries displayed
通过将totalInquiries整型变量初始化为0,然后依次遍历每个控件,将其Count特性与totalInquiries相加,就可以得到查询的总数了。除了C#中语句结束时的分号为,实现这一功能的C#和VB.NET语句相同:
totalInquiries += current.Count;
通过遍历每个控件,下面的代码能够绘制出每个子控件:
for (int i = 0; i < Controls.Count; i++) { current = (BookCounter) Controls[i]; totalInquiries += current.Count; output.Write(" |
相应的VB.NET代码为:
For Each current in Controls totalInquiries += current.Count output.Write(" |
局部的BookCounter对象型current变量的值被赋为控件集合中的每个对象:
for (int i = 0; i < Controls.Count; i++) { current = (BookCounter) Controls[i]; |
然后就可以利用下面的代码计算totalInquiries:
totalInquiries += current.Count; |
然后我们就可以继续绘制对象了。通过利用current的BookName特性,HtmlTextWriter可以用来创建一个行并显示书的名字:
" + current.BookName + " |
接下来,我们绘制一个TD标记,在该标记内我们让BookCounter对象绘制自己。最后,使用RenderEndTag来绘制一个结束性的TD标记,并使用HTMLTextWriter的Write方法绘制行结束标记。
output.RenderBeginTag("TD"); current.RenderControl(output); output.RenderEndTag( ); output.Write(" |
下面的代码使被包含的控件自己绘制自己:
current.RenderControl(output); |
上面的代码会调用BookCounter的Render方法。由于我们没有覆盖该方法,调用的仍然是基础类中的Render 类。唯一被包含的对象是CountedButton,由于我们没有覆盖CountedButton中的Render方法,在绘制该按钮时调用的仍然是基础类Button中的Render方法。
所有的子控件绘制完毕后,BookInquiryList将创建一个新的行,显示总的查询次数:
output.Write(" |
至此,作为合同编程人员的你,就完成了任务。
在上面的文章中,我们讨论了开发定制控件的三种途径,尤其是通过一个例子,详细论述了建立复合控件的过程。在具体的工作中,灵活地运用这三种方式创建定制控件,使我们的开发工作事半功倍。
相关阅读 Mac访问Windows共享文件夹Windows 7正版系统验证方法windows 8.1系统版本号查看方法Windows 8.1系统电话激活时无法输入微软返回代码解决方法Windows 8如何调整屏幕分辨率windows8.1磁盘占用100%解决方法Mac双系统如何删除Boot Camp安装的Windows分区Apple教你如何在Mac 上运行 Windows
热门文章 360随身Wifi 4G版什么怎样提高origin下载速百度收购PPS已敲定!3
最新文章
伊森卡特的消失通关流千牛怎么设置自动回复
增加新功能,S版Moto G喜获Android 4.4.4更鸡肋?谷歌Play Music发布更新版本千牛怎么设置自动回复千牛云盘怎么用
人气排行 xp系统停止服务怎么办?xp系统升级win7系统方office2013安装教程图解:手把手教你安装与同步推是什么?同步推使用方法介绍QQ2012什么时候出 最新版下载VeryCD镜像网站逐个数,电驴资料库全集利用PHP程序设定防止MySQL注入或HTML表单滥web服务器和应用服务器的区别安卓android 系统支持什么视频格式
查看所有0条评论>>