ASP.NET 揭秘 第二十八章 开发自定义控件(ASP.MET)
1.Creating a Simple Control(建立简单的自定义控件)
通过声明 Inherits 完成控件的继承,通过重载控件的Render 方法
完成控件的重画.(类似于WinForm中的Paint)
Render 方法只有一个参数:HtmlTextWriter 类,你可以通过HtmlTextWriter 类的Write方法显示文字.
示例代码如下:
Listing 28.1 HelloWorld.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace myControls
Public Class HelloWorld: Inherits Control
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter )
objTextWriter.Write( "Hello World!" )
End Sub
End Class
End Namespace
2.Using HtmlTextWriter(使用HtmlTextWriter对象)
HtmlTextWriter对象包括以下主要方法:
AddAttribute— Adds an HTML attribute that is rendered with a tag when the RenderBeginTag method is called; for example, Color="Red"
添加一个HTML命名属性标记
AddStyleAttribute— Adds a Style attribute that is rendered with a tag when the RenderBeginTag method is called; for example, Style="Font:Arial;"
添加一个样式命名标记
RenderBeginTag— Renders an opening HTML tag with attributes; for example, <Font>
生成一个命名开始标记
RenderEndTag— Renders the closing HTML tag for the last tag opened with the RenderBeginTag method; for example, </Font>
生成一个命名结束标记
Write— Renders text
生成文字
WriteAttribute— Renders an attribute name and value pair; for example, Color="Red"
添加下个命名属性
WriteBeginTag— Renders the beginning of an HTML tag without the closing >; for example, <Font
添加一个命名开始标记,注意不包括结束后缀'>'
WriteFullBeginTag— Renders a full opening HTML tag; for example, <Font>
添加一个HTML命名开始标记
WriteLine— Renders text followed by a line terminator
添加一行文字
WriteLineNoTabs— Renders text without automatically adding tabs
添加一行文字,不自动包含'Tab'
WriteStyleAttribute— Renders a style attribute name and value pair; for example, Style="Font:Arial;"
添加一个样式命名标记
如下示例:
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter )
Dim strText As String
strText = "<font color=""green"">Hello World!</font>"
objTextWriter.Write( strText )
End Sub
示例二:
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter )
objTextWriter.AddAttribute( "color", "green" )
objTextWriter.RenderBeginTag( "font" )
objTextWriter.Write( "Hello World!" )
objTextWriter.RenderEndTag()
End Sub
在示例二中,输出的内容如下:
<font color="green">Hello World!</font>
3.Adding Properties and Methods to Controls(为自定义控件添加属性方法)
通过 Public 为自定义控件添加属性和方法,和WinForm下是一样的.
示例:
Listing 28.3 ShowColor.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace myControls
Public Class ShowColor: Inherits Control
Public Text As String
Public Color As String
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter )
objTextWriter.AddAttribute( "Color", Color )
objTextWriter.RenderBeginTag( "Font" )
objTextWriter.Write( Text )
objTextWriter.RenderEndTag()
End Sub
End Class
End Namespace
使用属性访问器:
你可以使用Get和SET访问器建立属性.这样你可以在对应的过程中进行其它操作处理.
使用方法:
可以定义一个Public方法,在控件外部可以直接调用.
示例代码下:
Listing 28.7 ReverseMethodControl.vb
Imports System
Imports System.Web
Imports System.Web.UI
Imports Microsoft.VisualBasic
Namespace myControls
Public Class ReverseMethodControl: Inherits Control
Public Text As String
Public Sub Reverse()
Text = StrReverse( Text )
End Sub
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter )
objTextWriter.Write( Text )
End Sub
End Class
End Namespace
调用方法如下:
Sub Page_Load
ctrlReverse.Text = "Hello World!"
ctrlReverse.Reverse()
End Sub
4.Parsing Inner Content(分[剖]析包括的内容)
以下我们讨论如何为控件添加用户属性,下面我们看看如何通过添加值,在Control's的开始结束标记里,为控件分配对应的值(集合).
每个有子控件的控件,都有一个Controls集合,当添加Text到Control's的开始结束标记里时,Text实际上是添加到了Contrls集体中的LiteralControl中. 如果你添加一个TextBox控件,两个控件将添加到Controls集合中, LiteralControl 和 TextBox.
示例代码如下:
Listing 28.9 ShowGreen.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace myControls
Public Class ShowGreen: Inherits Control
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter )
Dim strInnerText As String
If IsLiteralContent
strInnerText = CType( Controls( 0 ), LiteralControl ).Text
objTextWriter.AddAttribute( "color", "green" )
objTextWriter.RenderBeginTag( "font" )
objTextWriter.RenderBeginTag( "b" )
objTextWriter.Write( strInnerText )
objTextWriter.RenderEndTag()
objTextWriter.RenderEndTag()
End If
End Sub
End Class
End Namespace
以上面示例中,Render 方法中的IsLiteralContent 方法用来判断,控件在开始和结束标记中是否包含literal Content(文字内容),如果控件包含LiteralControl近件,IsLiteralContent 方法返回为Ture.
通过Controls 集合,返回LiteralControl ,再通过正确的类型转换,strInnerText变量被分配到了Text属性中.strInnerText 变量输出的文字使用的是绿粗体.
5.Adding Child Controls to a Control(为控件添加一个或多个子控件)
许多标准自定义控件在定义中使用了控件,如:ListBox,RadioButtonList
如下所示,ListBox控件中包含了三个ListItem控件,当ListBOx显示时,甩的有ListItem控件的Text被显示到了ListBox控件中.
<asp:ListBox
Runat="Server">
<asp:ListItem Text="First Item" />
<asp:ListItem Text="Second Item" />
<asp:ListItem Text="Third Item" />
</asp:ListBox>
你可以建立轻量的ListGox控件,需要定义两个控件,并把第二个控件作为子控件嵌入到第一个控件内.
示例代码如下:
Listing 28.11 SimpleRotator1.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace myControls
Public Class SimpleRotator1: Inherits Control
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter)
Dim objRandom As New Random
Dim intRanIndex As Integer
Dim objSelectedItem As RotatorItem1
If Controls.Count > 0 Then
intRanIndex = objRandom.Next( Controls.Count )
objSelectedItem = CType( Controls( intRanIndex ), RotatorItem1 )
objTextWriter.Write( objSelectedItem.Text )
End If
End Sub
End Class
Public Class RotatorItem1 : Inherits Control
Public Text As String
End Class
End Namespace
在以下示例中,第一个控件SimpleRotator1,和第二个RotatorItem1控件.
以SimpleRotator1,控件显示时,控件中的Controls集合被选中,对应的Value(Text属性)被选中并显示.
6.Examining Custom Controls and Events(完成自定义控件的事件处理)
所有的控件都有以下6个事件:
DataBinding— This event is typically raised when the DataBind method is called.
在数据绑定时触发.
Disposed— This event occurs when the control is released from memory. This is a good place to close expensive resources, such as database connections.
在控件销毁时触发.
Init— This event occurs when the control is first initialized.
在控件第一次初始化时触发.
Load— This event occurs when the control is loaded into the Page object.
在控件加载到页面时触发.
PreRender— This event occurs right before the control's Render method is called.
在控件的Render方法调用后触发.
Unload— This event occurs when the control is unloaded from memory.
在控件从内存时卸载时触发.
示例代码如下:
Listing 28.17 ControlEvents.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace myControls
Public Class ControlEvents
Inherits Control
Protected Overrides Sub OnInit( e As EventArgs )
Context.Response.Write( "<li> OnInit Event!" )
End Sub
Protected Overrides Sub OnLoad( e As EventArgs )
Context.Response.Write( "<li> OnLoad Event!" )
End Sub
Protected Overrides Sub OnPreRender( e As EventArgs )
Context.Response.Write( "<li> OnPreRender Event!" )
End Sub
End Class
End Namespace
为控件添加自定义事件:
许多ASP.NET标准控件触发自定义事件:如AdRotator控件触发AdCreated事件
Calendar 控件触发DayRender事件.
通过以下两步可以为控件添加自定义事件:
a.Declare an event by using the Event statement.
通过Event 声明事件.
b.Raise the event by creating a subroutine that contains the RaiseEvent statement.
在子过程中通过RaiseEvent 触发事件.
示例代码如下:
Listing 28.18 RandomEvent.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace myControls
Public Class RandomEvent
Inherits Control
Public Event Event1( s As Object, e As EventArgs )
Public Event Event2( s As Object, e As EventArgs )
Protected Sub OnEvent1( e As EventArgs )
RaiseEvent Event1( Me, EventArgs.Empty )
End Sub
Protected Sub OnEvent2( e As EventArgs )
RaiseEvent Event2( Me, EventArgs.Empty )
End Sub
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter )
Dim objRandom As New Random
Dim intRanSelect As Integer
intRanSelect = objRandom.Next( 2 )
If intRanSelect = 0 Then
OnEvent1( EventArgs.Empty )
Else
OnEvent2( EventArgs.Empty )
End If
End Sub
End Class
End Namespace
7.Participating in Postbacks(处理"提交后")
设想一下,如果你希望你的网站上全有文本框都是Blue背景,Yelow前景,最简单的办法是建立一个定义具有正确属性的自定义控件.如下示例:control in Listing 28.20 renders an <input> tag with Blue背景,Yelow前景.
Listing 28.20 BlueTextBox.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace myControls
Public Class BlueTextBox
Inherits Control
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter)
objTextWriter.AddAttribute( "Name", Me.UniqueID )
objTextWriter.AddStyleAttribute( "background-color", "Blue" )
objTextWriter.AddStyleAttribute( "color", "Yellow" )
objTextWriter.RenderBeginTag( "input" )
objTextWriter.RenderEndTag
End Sub
End Class
End Namespace
如果你提义你的的BlueTextBox控件,就会发现BlueTextBox控件不能保持已有的值,也就是说,这个控件不支持PostBacks.
对了让BlueTextBox控件支持PostBacks,你需要现实IPostBackDataHandler接口:该接口定义了以下两个方法.
LoadPostData— Processes the data being posted back to the server
处理服务器端已存在的提交后的数据.
RaisePostDataChangedEvent— Raises the change event for a control
触发相应的事件.
示例代码如下:
Public Class BlueTextBoxPostBack
Inherits Control
Implements IPostBackDataHandler
Public Text As String
Public Function LoadPostData( PostDataKey As String, Values As NameValueCollection) As
Boolean _
Implements IPostBackDataHandler.LoadPostData
Text = Values( Me.UniqueID )
Return False
End Function
Public Sub RaisePostDataChangedEvent() _
Implements IPostBackDataHandler.RaisePostDataChangedEvent
' Raise Change Event
End Sub
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter )
objTextWriter.AddAttribute( "Name", Me.UniqueID )
objTextWriter.AddStyleAttribute( "background-color", "Blue" )
objTextWriter.AddStyleAttribute( "color", "Yellow" )
objTextWriter.AddAttribute( "value", Text )
objTextWriter.RenderBeginTag( "input" )
objTextWriter.RenderEndTag
End Sub
End Class
在上面示例中,我们添加了两个过程,LoadPostData过程有以下两个参数:
PostDataKey :represents the unique ID for the control
指出当前控件的唯一ID.
Values :represents the values of all the form elements being posted back to the server.
批出服务器端已存在的提交后的数据.
在LoadPostData过程中,我们注意到为当前控件的Values集合中的当前值分配了Text变量,接成返回False.
注意:RaisePostBackDataChanged过程在LoadPostData过程返回为True时被调用.
在Render 方法中,你可以通过AddAttribute 添加名称为Value的属性,同时为Value属性分配Text变量.
为了提交RaisePostBackDataChanged事件,可以进行以下处理:
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Collections.Specialized
Namespace myControls
Public Class BlueTextBoxChanged
Inherits Control
Implements IPostBackDataHandler
Public Event TextChanged( s As Object, e As EventArgs )
Public Property Text As String
Set
ViewState( "Text" ) = Value
End Set
Get
If ViewState( "Text" ) <> Nothing Then
Return ViewState( "Text" )
Else
Return String.Empty
End If
End Get
End Property
Public Function LoadPostData( PostDataKey As String, Values As NameValueCollection ) As
Boolean _
Implements IPostBackDataHandler.LoadPostData
Dim strNewValue As String
strNewValue = Values( Me.UniqueID )
If strNewValue <> Text Then
Text = strNewValue
Return True
Else
Return False
End If
End Function
Public Sub RaisePostDataChangedEvent() _
Implements IPostBackDataHandler.RaisePostDataChangedEvent
OnTextChanged( EventArgs.Empty )
End Sub
Protected Sub OnTextChanged( e As EventArgs )
RaiseEvent TextChanged( Me, e )
End Sub
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter )
objTextWriter.AddAttribute( "Name", Me.UniqueID )
objTextWriter.AddStyleAttribute( "background-color", "Blue" )
objTextWriter.AddStyleAttribute( "color", "Yellow" )
objTextWriter.AddAttribute( "value", Text )
objTextWriter.RenderBeginTag( "input" )
objTextWriter.RenderEndTag
End Sub
End Class
End Namespace
处理PostBacks事件;
你可以使用以下三种ASP.NET标准控件向Form提交数据:Button, ImageButton, LinkButton.当你提交一个Form时,所有的这三个控件自动生成并执行客户端JSP脚本.
以下介绍如果建立自动生成并JSP脚本,并触发PostBack事件的Form元素.
Listing 28.26 TextBoxColor.vb
[View full width]
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Collections.Specialized
Namespace myControls
Public Class TextBoxColor
Inherits Control
Implements IPostBackDataHandler
Implements IPostBackEventHandler
Public BoxColor As String = "Blue"
Public Text As String
Public Sub RaisePostBackEvent(EventArgument As String) _
Implements IPostBackEventHandler.RaisePostBackEvent
If (eventArgument = "Red" )
Me.BoxColor = "Red"
Else
Me.BoxColor = "Blue"
End If
End Sub
Public Function LoadPostData(PostDataKey As String, Values As NameValueCollection) As
Boolean _
Implements IPostBackDataHandler.LoadPostData
Dim strNewValue As String
Text = Values( Me.UniqueID )
Return False
End Function
Public Sub RaisePostDataChangedEvent() _
Implements IPostBackDataHandler.RaisePostDataChangedEvent
' Raise Change Event
End Sub
Protected Overrides Sub Render( objTextWriter As HtmlTextWriter )
objTextWriter.AddAttribute( "Name", Me.UniqueID )
objTextWriter.AddStyleAttribute( "background-color", BoxColor )
objTextWriter.AddStyleAttribute( "color", "Yellow" )
objTextWriter.AddAttribute( "value", Text )
objTextWriter.RenderBeginTag( "input" )
objTextWriter.RenderEndTag
objTextWriter.WriteLine( "<p>" )
objTextWriter.AddAttribute( "Type", "Button" )
objTextWriter.AddAttribute( "Value", "Display Red!" )
objTextWriter.AddAttribute( "OnClick", "JScript:" & Page.GetPostBackEventReference( Me,
"Red" ) )
objTextWriter.RenderBeginTag( "Input" )
objTextWriter.RenderEndTag()
objTextWriter.WriteLine( " " )
objTextWriter.AddAttribute( "Type", "Button" )
objTextWriter.AddAttribute( "Value", "Display Blue!" )
objTextWriter.AddAttribute( "OnClick", "JScript:" & Page.GetPostBackEventReference( Me,
"Blue" ) )
objTextWriter.RenderBeginTag( "Input" )
objTextWriter.RenderEndTag()
End Sub
End Class
End Namespace
GetPostBackEventReference返顺一个客户端JSP脚本的引用,这个引用将导致触发Form的服务器端的PostBacks.
第一个参数指出触发哪一个控件服务器端PostBacks事件.在上面示例中,使用me表示当前控件.
第二个参数是要选的,做为PostBacks事件的参数被传到服务器端的PostBacks.在上面示例中,使用了"Red".
当Display Red! button is rendered 时,以下代码发送到了浏览器
<input Type="Button" Value="Display Red!" OnClick="JScript:__doPostBack ('ctrl4','Red') " />
Display Red! button 触发了客户端名为__doPostBack 的JSP脚本过程,注意:控件的ID被做为参数传递到了该过程中.
当选择查看源码时,__doPostBack内容如下:
function __doPostBack(eventTarget, eventArgument) {
var theform = document.ctrl1
theform.__EVENTTARGET.value = eventTarget
theform.__EVENTARGUMENT.value = eventArgument
theform.submit()
}
__doPostBack初始了两个参数,并向服务器端进行提交.
也就是执行了以下过程:
Public Sub RaisePostBackEvent(EventArgument As String) _
Implements IPostBackEventHandler.RaisePostBackEvent
If (eventArgument = "Red" )
Me.BoxColor = "Red"
Else
Me.BoxColor = "Blue"
End If
End Sub
在上面过程中,修改了控件的背景颜色.
当上在的控件显示Text Box时,可以使用BoxColor属性设置控件的背景颜色.
8.Creating Composite Controls(建立复合控件)
类似于WinForm下的复合控件的建立.
一个简单的登录的复合控件示例如下:
Listing 28.28 Login.vb
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace myControls
Public Class Login
Inherits Control
Implements INamingContainer
Public Property Username As String
Get
Me.EnsureChildControls()
Return CType( Controls( 2 ), TextBox ).Text
End Get
Set
Me.EnsureChildControls()
CType( Controls( 2 ), TextBox ).Text = Value
End Set
End Property
Public Property Password As String
Get
Me.EnsureChildControls()
Return CType( Controls( 5 ), TextBox ).Text
End Get
Set
Me.EnsureChildControls()
CType( Controls( 5 ), TextBox ).Text = Value
End Set
End Property
Protected Overrides Sub CreateChildControls()
Me.Controls.Add( New LiteralControl( "<div style=""border: 5px " & _
"inset #cccccc;background-color:#eeeeee;width:50%;padding:10px"">" ) )
' Add Username
Me.Controls.Add( New LiteralControl( "<b>Username:</b> " ) )
Me.Controls.Add( New TextBox )
Me.Controls.Add( New LiteralControl( "<p>" ) )
' Add Password
Dim txtPass As New TextBox
Me.Controls.Add( New LiteralControl( "<b>Password:</b> " ) )
txtPass.TextMode = TextBoxMode.Password
Me.Controls.Add( txtPass )
Me.Controls.Add( New LiteralControl( "<p>" ) )
' Add Submit Button
Dim btnButton As New Button
btnButton.Text = "Login!"
Me.Controls.Add( btnButton )
Me.Controls.Add( New LiteralControl( "</div>" ) )
End Sub
End Class
End Namespace
通过AddHandler ,处理复合控件中的事件.
示例代码如下:
Listing 28.30 LoginEvent.vb
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace myControls
Public Class LoginEvent
Inherits Control
Implements INamingContainer
Sub CheckPassword( s As Object, e As EventArgs )
Dim strUsername, strPassword As String
Dim lblLabel As Label
strUsername = CTYPE( Controls( 2 ), TextBox ).Text
strPassword = CTYPE( Controls( 5 ), TextBox ).Text
lblLabel = CTYPE( Controls( 9 ), Label )
If strUsername = "joe" and strPassword = "secret" Then
lblLabel.Text = "Welcome Joe!"
Else
lblLabel.Text = "Invalid Password!"
End If
End Sub
Protected Overrides Sub CreateChildControls()
Me.Controls.Add( New LiteralControl( "<div style=""border: 5px " & _
"inset #cccccc;background-color:#eeeeee;width:50%;padding:10px"">" ) )
' Add Username
Me.Controls.Add( New LiteralControl( "<b>Username:</b> " ) )
Me.Controls.Add( New TextBox )
Me.Controls.Add( New LiteralControl( "<p>" ) )
' Add Password
Dim txtPass As New TextBox
Me.Controls.Add( New LiteralControl( "<b>Password:</b> " ) )
txtPass.TextMode = TextBoxMode.Password
Me.Controls.Add( txtPass )
Me.Controls.Add( New LiteralControl( "<p>" ) )
' Add Submit Button
Dim btnButton As New Button
btnButton.Text = "Login!"
AddHandler btnButton.Click, AddressOf CheckPassword
Me.Controls.Add( btnButton )
Me.Controls.Add( New LiteralControl( "</div>" ) )
' Add Label Control
Dim lblLabel As New Label
lblLabel.EnableViewState = False
Me.Controls.Add( lblLabel )
End Sub
End Class
End Namespace
9.Inheriting from Existing Controls(继承现有的控件)
你可以继承任何一个ASP.NET Framework 中现有的HTML或者Web Control.
以下示例通过重载RenderContents方法,完成更高级的自定义控件的应用.
Public Class myWebControl
Inherits WebControl
Overrides Protected Sub RenderContents( objTextWriter As HtmlTextWriter )
objTextWriter.Write( "Hello World!" )
End Sub
End Class
10.Accessing the Current Context(访问当前上下文环境信息)
通过自定义控件的Context 属性中的Response 和 Request 对象来访问.
如:通过Request 对象的 UserHostAddress 和 UserAgent 得到用户信息.
示例代码:
strHostAddress = Context.Request.UserHostAddress
strUserAgent = Context.Request.UserAgent
同理,也可以通过Request对象,得到当前浏览器的相应信息.
示例代码:
strBrowserType = Context.Request.Browser.Browser
strVersion = Context.Request.Browser.Version
blnJavascript = Context.Request.Browser.Javascript
11.Debugging Controls(自定义控件的调试)
使用自定义控件的Context 属性中的Trace对象显示跟踪信息.
示例代码:
Listing 28.36 ControlTrace.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace myControls
Public Class ControlTrace
Inherits Control
Overrides Protected Sub OnLoad( e As EventArgs )
Context.Trace.Warn( "Executing OnLoad Method!" )
End Sub
Overrides Protected Sub OnPreRender( e As EventArgs )
Context.Trace.Warn( "Executing OnPreRender Method!" )
End Sub
Overrides Protected Sub Render( objTextWriter As HtmlTextWriter )
Context.Trace.Warn( "Executing Render Method!" )
objTextWriter.WriteLine( "Hello World!" )
End Sub
End Class
End Namespace
总结:本章祥细的描述了在ASP.NET下自定义控件的开发.你可以通过继承和复合现在Web Control建立自已的包括多种属性,方法,事件.的WEB控件.
同时也介绍了自定义控件的Context 属性中的 Request, Response, Trace 等对象.
下一章将介绍高级自定义控件的发开,包括包含模板,数据绑定等.