来自asp.net快速入门教程 自定义控件(2)
目录:
1。开发模板控件
2。开发模板数据绑定控件
3。示例
4。重写控件分析
5。定义自定义控件生成器
'-------------------------------------------------------------------------------------------------------
1。开发模板控件
ASP.NET 页框架允许控件开发人员通过使用模板创作将用户界面同控件逻辑分开的控件。
通过将 UI 提供为模板标记之间的参数,页面开发人员可以自定义控件的显示。
模板控件具有一个或多个类型为 System.Web.UI.ITemplate 的属性:
Public Property <TemplateContainer(GetType(Template1VB))> MessageTemplate As ITemplate
属性(以上方括号中的)指定容器(父)控件的类型。
ITemplate 接口具有一个方法 InstantiateIn,
该方法动态创建控件实例。
这在 CreateChildControls 方法中的 ITemplate 属性上调用:
Protected Overrides Sub CreateChildControls()
If MessageTemplate <> Null Then
MessageTemplate.InstantiateIn(Me)
End if
...
End Sub
EXP
'*******************************************************************************************************
'Template1.aspx
<%@ Register TagPrefix="TemplateControlSamples" Namespace="TemplateControlSamplesVB"
Assembly="TemplateControlSamplesVB" %>
<script runat=server language=VB>
Sub Page_Load()
DataBind()
End Sub
</script>
<html>
<body>
<form method="POST" runat="server">
非模板版本:
<TemplateControlSamples:Template1VB Message="Hello World!" runat=server/>
<hr>
模板版本:
<TemplateControlSamples:Template1VB Message="Hello World!" runat=server>
<MessageTemplate>
<b><i><u>
<%# Container.Message%>
</u></i></b>
</MessageTemplate>
</TemplateControlSamples:Template1VB>
</form>
</body>
</html>
'*******************************************************************************************************
'Template1.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace TemplateControlSamplesVB
Public Class TemplateItem : Inherits Control : Implements INamingContainer
Private _message As String = Nothing
Public Sub New(Message As String)
_message = message
End Sub
Public Property Message As String
Get
Return _message
End Get
Set
_message = Value
End Set
End Property
End Class
<ParseChildren(true)> Public Class Template1VB : Inherits Control : Implements INamingContainer
Private _messageTemplate As ITemplate = Nothing
Private _message As String = Nothing
Public Property Message As String
Get
Return _message
End Get
Set
_message = Value
End Set
End Property
<TemplateContainer(GetType(TemplateItem))> Public Property MessageTemplate As ITemplate
Get
Return _messageTemplate
End Get
Set
_messageTemplate = Value
End Set
End Property
Public Overrides Sub DataBind()
EnsureChildControls()
MyBase.DataBind()
End Sub
Protected Overrides Sub CreateChildControls()
' 如果已指定了模板,则用它创建子控件。
' 或者,使用消息值创建一个 literalcontrol
If Not (MessageTemplate Is Nothing)
Controls.Clear()
Dim I As New TemplateItem(Me.Message)
MessageTemplate.InstantiateIn(I)
Controls.Add(I)
Else
Me.Controls.Add(New LiteralControl(Me.Message))
End If
End Sub
End Class
End Namespace
'*******************************************************************************************************
'源代码
<html>
<body>
<form name="_ctl0" method="POST" action="Template1.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE"
value="dDwxNTU1ODQ4ODI1O3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDM+Oz47bDx0PDtsPGk8MD47PjtsPHQ8O2w8aTwwPjs+O2w8dDxAPEhlbGx
vIFdvcmxkITs+Ozs+Oz4+Oz4+Oz4+Oz4+Oz46Evtn977RC4zTo9XRvZY4yd5Zhg==" />
非模板版本:
Hello World!
<hr>
模板版本:
<b><i><u>
Hello World!
</u></i></b>
</form>
</body>
</html>
'*******************************************************************************************************
'-------------------------------------------------------------------------------------------------------
2。开发模板数据绑定控件
下面的示例显示用模板创建数据绑定控件的更复杂应用。此示例中定义的 Repeater 控件类似于
System.Web.UI.WebControls.Repeater 控件。
'*******************************************************************************************************
'源代码
<html>
<body>
<form name="_ctl0" method="POST" action="Repeater1.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE"
value="dDw5ODQxMjIxNTY7dDw7bDxpPDI+Oz47bDx0PDtsPGk8MT47PjtsPHQ8cDxsPE51bUl0ZW1zOz47bDxpPDQ+Oz4+Ozs+Oz4+Oz4+Oz7
I2DGeKAoLV5Z3/Z2sGqke7Z+8hw==" />
值:<input name="MyList:_ctl0:MyValue" type="text" value="1" id="MyList__ctl0_MyValue" />
<hr align=left width=200>
值:<input name="MyList:_ctl1:MyValue" type="text" value="2" id="MyList__ctl1_MyValue" />
<hr align=left width=200>
值:<input name="MyList:_ctl2:MyValue" type="text" value="3" id="MyList__ctl2_MyValue" />
<hr align=left width=200>
值:<input name="MyList:_ctl3:MyValue" type="text" value="4" id="MyList__ctl3_MyValue" />
<hr align=left width=200>
<input type="submit" name="_ctl1" value="更新" />
</form>
</body>
</html>
'*******************************************************************************************************
'Repeater1.aspx
<%@ Register TagPrefix="TemplateControlSamples" Namespace="TemplateControlSamplesVB"
Assembly="TemplateControlSamplesVB" %>
<html>
<script language="VB" runat=server>
Sub Page_Load(Sender As Object, E As EventArgs)
If Not (IsPostBack)
Dim Values As New ArrayList
Values.Add("1")
Values.Add("2")
Values.Add("3")
Values.Add("4")
MyList.DataSource = Values
MyList.DataBind()
End If
End Sub
Private Sub Btn1_Click(Sender As Object, E As EventArgs)
' 现在不进行任何操作。仅演示保留该值
' 的位置....
End Sub
</script>
<body>
<form method="POST" action="Repeater1.aspx" runat=server>
<TemplateControlSamples:Repeater1VB id="MyList" runat=server>
<ItemTemplate>
值:<asp:textbox id="MyValue" Text="<%#Container.DataItem%>" runat=server/>
<hr align=left width=200>
</ItemTemplate>
</TemplateControlSamples:Repeater1VB>
<asp:button Text="更新" OnClick="Btn1_Click" runat=server/>
</form>
</body>
</html>
'*******************************************************************************************************
'RepeaterItem.vb
Imports System
Imports System.Collections
Imports System.Web
Imports System.Web.UI
Namespace TemplateControlSamplesVB
Public Class RepeaterItemVB : Inherits Control : Implements INamingContainer
Private _ItemIndex As Integer
Private _DataItem As Object
Public Sub New(ItemIndex As Integer, DataItem As Object)
MyBase.New()
_ItemIndex = ItemIndex
_DataItem = DataItem
End Sub
Public ReadOnly Property DataItem As Object
Get
return _DataItem
End Get
End Property
Public ReadOnly Property ItemIndex As Integer
Get
return _ItemIndex
End Get
End Property
End Class
End Namespace
'*******************************************************************************************************
Repeater1.vb
Imports System
Imports System.Collections
Imports System.Web
Imports System.Web.UI
Namespace TemplateControlSamplesVB
<ParseChildren(true)> Public Class Repeater1VB : Inherits Control : Implements INamingContainer
Private _itemTemplate As ITemplate = Nothing
Private _dataSource As ICollection = Nothing
Public Property DataSource As ICollection
'ICollection从IEnumerable接口继承,定义了集合的所有枚举数,大小和同步方法
Get
Return _dataSource
End Get
Set
_dataSource = Value
End Set
End Property
<TemplateContainer(GetType(RepeaterItemVB))> public Property ItemTemplate As ITemplate
Get
return _itemTemplate
End Get
Set
_itemTemplate = value
End Set
End Property
' 重写以防止文本控件被添加为子控件
Protected Overrides Sub AddParsedSubObject(O As Object)
End Sub
' 重写以创建重复的项目
Protected Overrides Sub CreateChildControls()
Dim O As Object = ViewState("NumItems")
If Not (O Is Nothing)
' 清除所有现有的子控件
Controls.Clear()
Dim I As Integer
Dim NumItems As Integer = CInt(O)
For I = 0 To NumItems - 1
' 创建项目
Dim Item As RepeaterItemVB = New RepeaterItemVB(I, Nothing)
' 从模板初始化项目
ItemTemplate.InstantiateIn(Item)
' 将项目添加到子控件集合中
Controls.Add(Item)
Next
End If
End Sub
' 重写以从数据源创建重复的项目
Protected Overrides Sub OnDataBinding(E As EventArgs)
MyBase.OnDataBinding(e)
If Not DataSource Is Nothing
' 清除所有现有的子控件
Controls.Clear()
' 清除现有子控件的所有以前的视图状态
ClearChildViewState()
' 重复数据源,为每个数据项创建一个新项目
Dim DataEnum As IEnumerator = DataSource.GetEnumerator()
'IEnumerator:支持在集合上进行简单迭代,是所有枚举的基接口
' 拥有两个重要的成员
' movenext方法和current属性
'GetEnumerator:返回可循环访问 CollectionBase 实例的枚举数
'Public Overridable Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Dim I As Integer = 0
Do While (DataEnum.MoveNext())
' 创建项目
Dim Item As RepeaterItemVB = New RepeaterItemVB(I, DataEnum.Current)
' 从模板初始化项目
ItemTemplate.InstantiateIn(Item)
'当由类实现时,定义子控件和模板所属的 Control 对象。然后在内联模板中定义这些子控件。
'Sub InstantiateIn( ByVal container As Control )
'container :包含内联模板中的实例化控件的 Control 对象。
'开发模板服务器控件时,不需要实现此方法,.NET Framework 会为您提供该实现。
' 将项目添加到子控件集合中
Controls.Add(Item)
I = I + 1
Loop
' 防止再次创建子控件
ChildControlsCreated = true
' 存储在回发方案的视图状态中创建的项目数目
ViewState("NumItems") = I
End If
End Sub
End Class
End Namespace
'*******************************************************************************************************
'-------------------------------------------------------------------------------------------------------
3。示例
下面的示例修改前面的示例,使页使用者可以在回发期间遍历其 Items 集合,以从中取出值。
'*******************************************************************************************************
'源代码
<html>
<body>
<form name="_ctl0" method="POST" action="Repeater2.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE"
value="dDwxMjA4OTUwMTczO3Q8O2w8aTwyPjs+O2w8dDw7bDxpPDE+Oz47bDx0PHA8bDxOdW1JdGVtczs+O2w8aTw0Pjs+Pjs7Pjs+Pjs+Pjs
+7lryNqjDQHRT1wII0UtqJ+ATZL0=" />
值:<input name="MyList:_ctl0:MyValue" type="text" value="1" id="MyList__ctl0_MyValue" />
<hr align=left width=200>
值:<input name="MyList:_ctl1:MyValue" type="text" value="2" id="MyList__ctl1_MyValue" />
<hr align=left width=200>
值:<input name="MyList:_ctl2:MyValue" type="text" value="3" id="MyList__ctl2_MyValue" />
<hr align=left width=200>
值:<input name="MyList:_ctl3:MyValue" type="text" value="4" id="MyList__ctl3_MyValue" />
<hr align=left width=200>
<input type="submit" name="_ctl1" value="更新" />
</form>
</body>
</html>
'*******************************************************************************************************
Repeater2.vb
Imports System
Imports System.Collections
Imports System.Web
Imports System.Web.UI
Namespace TemplateControlSamplesVB
<ParseChildren(true)> Public Class Repeater2VB : Inherits Control : Implements INamingContainer
Private _itemTemplate As ITemplate = Nothing
Private _dataSource As ICollection = Nothing
Private _repeaterItems As RepeaterItemCollectionVB = Nothing
Public Property DataSource As ICollection
Get
Return _dataSource
End Get
Set
_dataSource = Value
End Set
End Property
<TemplateContainer(GetType(RepeaterItemVB))> public Property ItemTemplate As ITemplate
Get
return _itemTemplate
End Get
Set
_itemTemplate = value
End Set
End Property
Public ReadOnly Property Items As RepeaterItemCollectionVB
Get
return _repeaterItems
End Get
End Property
' 重写以防止文本控件被添加为子控件
Protected Overrides Sub AddParsedSubObject(O As Object)
End Sub
' 重写以创建重复的项目
Protected Overrides Sub CreateChildControls()
Dim O As Object = ViewState("NumItems")
If Not (O Is Nothing)
' 清除所有现有的子控件
Controls.Clear()
Dim I As Integer
Dim Items As New ArrayList
Dim NumItems As Integer = CInt(O)
For I = 0 To NumItems - 1
' 创建项目
Dim Item As RepeaterItemVB = New RepeaterItemVB(I, Nothing)
' 从模板初始化项目
ItemTemplate.InstantiateIn(Item)
' 将项目添加到子控件集合中
Controls.Add(Item)
' 保存 ArrayList 中的项目,以更新项目集合
Items.Add(Item)
Next
' 用新添加的项目更新项目集合
_repeaterItems = New RepeaterItemCollectionVB(Items)
End If
End Sub
' 重写以从数据源创建重复的项目
Protected Overrides Sub OnDataBinding(E As EventArgs)
MyBase.OnDataBinding(e)
If Not DataSource Is Nothing
' 清除所有现有的子控件
Controls.Clear()
' 清除现有子控件的所有以前的视图状态
ClearChildViewState()
Dim Items As New ArrayList
' 重复数据源,为每个数据项创建一个新项目
Dim DataEnum As IEnumerator = DataSource.GetEnumerator()
Dim I As Integer = 0
Do While(DataEnum.MoveNext())
' 创建项目
Dim Item As RepeaterItemVB = New RepeaterItemVB(I, DataEnum.Current)
' 从模板初始化项目
ItemTemplate.InstantiateIn(Item)
' 将项目添加到子控件集合中
Controls.Add(Item)
' 保存 ArrayList 中的项目,以更新项目集合
Items.Add(Item)
I = I + 1
Loop
' 用新添加的项目更新项目集合
_repeaterItems = New RepeaterItemCollectionVB(Items)
' 防止再次创建子控件
ChildControlsCreated = true
' 存储在回发方案的视图状态中创建的项目数目
ViewState("NumItems") = I
End If
End Sub
End Class
End NameSpace
'*******************************************************************************************************
'RepeaterItem.vb
Imports System
Imports System.Collections
Imports System.Web
Imports System.Web.UI
Namespace TemplateControlSamplesVB
Public Class RepeaterItemVB : Inherits Control : Implements INamingContainer
Private _ItemIndex As Integer
Private _DataItem As Object
Public Sub New(ItemIndex As Integer, DataItem As Object)
MyBase.New()
_ItemIndex = ItemIndex
_DataItem = DataItem
End Sub
Public ReadOnly Property DataItem As Object
Get
return _DataItem
End Get
End Property
Public ReadOnly Property ItemIndex As Integer
Get
return _ItemIndex
End Get
End Property
End Class
End Namespace
'*******************************************************************************************************
'RepeaterItemCollection.vb
Imports System
Imports System.Collections
Imports System.Web
Imports System.Web.UI
Imports System.Web.Util
Namespace TemplateControlSamplesVB
Public Class RepeaterItemCollectionVB : Implements ICollection
Private Items As ArrayList
Public Sub New(Items As ArrayList)
MyBase.New()
Me.Items = Items
End Sub
Public ReadOnly Property All As RepeaterItemVB()
Get
return CType(Items.ToArray(), RepeaterItemVB())
End Get
End Property
Public ReadOnly Property Count As Integer Implements ICollection.Count
Get
Return Items.Count
End Get
End Property
Public ReadOnly Property IsSynchronized As Boolean Implements ICollection.IsSynchronized
Get
Return false
End Get
End Property
Public ReadOnly Property SyncRoot As Object Implements ICollection.SyncRoot
Get
Return Me
End Get
End Property
Public Default ReadOnly Property Item(Index As Integer) As RepeaterItemVB
Get
Return CType(Items(Index), RepeaterItemVB)
End Get
End Property
Public Sub CopyTo(OutputArray As Array, Index As Integer) Implements ICollection.CopyTo
Dim E As IEnumerator = Me.GetEnumerator()
Do While (E.MoveNext())
OutputArray.SetValue(E.Current, Index)
Index = Index + 1
Loop
End Sub
Public Function GetEnumerator() As IEnumerator Implements ICollection.GetEnumerator
Return Items.GetEnumerator()
End Function
End Class
End Namespace
'*******************************************************************************************************
'Repeater2.aspx
<%@ Register TagPrefix="TemplateControlSamples" Namespace="TemplateControlSamplesVB"
Assembly="TemplateControlSamplesVB" %>
<html>
<script language="VB" runat=server>
Public Sub Page_Load(Sender As Object, E As EventArgs)
If Not (IsPostBack)
Dim Values As New ArrayList
Values.Add("1")
Values.Add("2")
Values.Add("3")
Values.Add("4")
MyList.DataSource = Values
MyList.DataBind()
End If
End Sub
Private Sub Btn1_Click(Sender As Object, E As EventArgs)
Dim X As Long
For X = 0 To MyList.Items.Count - 1
Dim MyValue As TextBox = MyList.Items(X).FindControl("MyValue")
MyValue.Text = (Int32.Parse(MyValue.Text) + 1).ToString()
Next
End Sub
</script>
<body>
<form method="POST" action="Repeater2.aspx" runat=server>
<TemplateControlSamples:Repeater2VB id="MyList" runat=server>
<ItemTemplate>
值:<asp:textbox id="MyValue" Text="<%#Container.DataItem%>" runat=server/>
<hr align=left width=200>
</ItemTemplate>
</TemplateControlSamples:Repeater2VB>
<asp:button Text="更新" OnClick="Btn1_Click" runat=server/>
</form>
</body>
</html>
'*******************************************************************************************************
'-------------------------------------------------------------------------------------------------------
4。重写控件分析
正如在检索内部内容中看到的,
如果控件 A 在页上的其控件标记中有嵌套控件,
页分析器会将那些控件的实例添加到 A 的 Controls 集合。
这通过调用 A 的AddSubParsedObject 方法来实现。
每个控件从 Control 继承此方法,
默认实现只不过将子控件插入到控件层次结构树中。
通过重写 AddSubParsedObject 方法,控件可以重写默认的分析逻辑。
注意,这里的讨论有些简化,下一个示例给出更多细节。
下面的示例定义重写默认分析逻辑的自定义控件 CustomParse1。
当分析特定类型的子控件时,它将其添加到集合。
CustomParse1 的呈现逻辑基于该集合中的项数。示例中还定义了一个简单的自定义控件 Item。
注意:如果自定义控件是从 WebControl 派生的,
它将不具有示例中描述的分析逻辑,
因为 WebControl 是用 ParseChildrenAttribute(ChildrenAsProperties= true) 标记的,
这导致不同的分析逻辑。
EXP
'*******************************************************************************************************
'CustomParse1.aspx
<%@ Register TagPrefix="CustomParsingControlSamples" Namespace="CustomParsingControlSamples"
Assembly="CustomParsingControlSamplesVB" %>
<html>
<body>
<form method="POST" runat=server>
<CustomParsingControlSamples:CustomParse1VB SelectedIndex="2" runat=server>
<CustomParsingControlSamples:ItemVB Message="1" runat=server/>
<CustomParsingControlSamples:ItemVB Message="2" runat=server/>
<CustomParsingControlSamples:ItemVB Message="3" runat=server/>
<CustomParsingControlSamples:ItemVB Message="4" runat=server/>
</CustomParsingControlSamples:CustomParse1VB>
</form>
</body>
</html>
'****************************************************************************************
'CustomParse1.vb
Imports System
Imports System.Collections
Imports System.Web
Imports System.Web.UI
Namespace CustomParsingControlSamples
Public Class CustomParse1VB : Inherits Control
Private _items As New ArrayList
Private _selectedIndex As Integer = 0
Public Property SelectedIndex As Integer
Get
Return _selectedIndex
End Get
Set
_selectedIndex = Value
End Set
End Property
Protected Overrides Sub AddParsedSubObject(Obj As Object)
If (TypeOf Obj Is ItemVB)
_items.Add(Obj)
End If
End Sub
Protected Overrides Sub Render(Output As HtmlTextWriter)
If (SelectedIndex < _items.Count)
Dim SelectedItem As ItemVB = CType(_items(SelectedIndex),ItemVB)
Output.Write(SelectedItem.Message)
End If
End Sub
End Class
End Namespace
'******************************************************************************************
'Item.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace CustomParsingControlSamples
Public Class ItemVB : Inherits Control
Private _message As String
Public Property Message As String
Get
Return _message
End Get
Set
_message = Value
End Set
End Property
End Class
End Namespace
'*******************************************************************************************************
'-------------------------------------------------------------------------------------------------------
5。定义自定义控件生成器
ASP.NET 页框架使用称为控件生成器的类来处理页上控件标记中的声明。
每个 Web 窗体控件都与默认的控件生成器类 System.Web.UI.ControlBuilder 关联。
默认的控件生成器为它在控件标记中遇到的每个嵌套控件将子控件添加到 Controls 集合。
另外,它为嵌套控件标记之间的文本添加 Literal 控件。
通过将自定义控件生成器类与控件关联,可以重写此默认行为。
这通过对控件应用控件生成器属性来实现,如下面的示例所示。
Public Class <ControlBuilderAttribute(GetType(CustomParse2ControlBuilderVB))> _
CustomParse2VB : Inherits Control
以上方括号里的元素为公共语言运行库属性,
该属性将 CustomParse2ControlBuilder 类与 CustomParse2 控件关联。
通过从 ControlBuilder 派生并重写其方法,可以定义您自己的自定义控件生成器。
下面的示例定义一个自定义控件生成器,
它重写从 ControlBuilder 继承的 GetChildControlType 方法。
此方法返回要添加的控件类型,并可用来决定将要添加哪些控件。
在示例中,控件生成器仅在标记名称为“customitem”时才添加子控件。
除了添加了自定义属性外,该控件的代码与上一示例非常相似。
'*********************************************************************************
'CustomParse2.aspx
<%@ Register TagPrefix="CustomParsingControlSamples" Namespace="CustomParsingControlSamples"
Assembly="CustomParsingControlSamplesVB" %>
<html>
<body>
<form method="POST" runat=server>
<CustomParsingControlSamples:CustomParse2VB SelectedIndex="2" runat=server>
<customitem Message="1"/>
<customitem Message="2"/>
<customitem Message="3"/>
<customitem Message="4"/>
</CustomParsingControlSamples:CustomParse2VB>
</form>
</body>
</html>
'******************************************************************
'CustomParse2.vb
Imports System
Imports System.Collections
Imports System.Web
Imports System.Web.UI
Namespace CustomParsingControlSamples
Public Class CustomParse2ControlBuilderVB : Inherits ControlBuilder
Public Overrides Function GetChildControlType(TagName As String, Attributes As IDictionary) As Type
If TagName = "customitem"
Return GetType(CustomParsingControlSamples.ItemVB)
End If
Return Nothing
End Function
End Class
<ControlBuilderAttribute(GetType(CustomParse2ControlBuilderVB))> Public Class CustomParse2VB : Inherits
Control
Private _items As New ArrayList
Private _selectedIndex As Integer = 0
Public Property SelectedIndex As Integer
Get
Return _selectedIndex
End Get
Set
_selectedIndex = Value
End Set
End Property
Protected Overrides Sub AddParsedSubObject(Obj As Object)
If (TypeOf Obj Is ItemVB)
_items.Add(Obj)
End If
End Sub
Protected Overrides Sub Render(Output As HtmlTextWriter)
If (SelectedIndex < _items.Count)
Dim SelectedItem As ItemVB = CType(_items(SelectedIndex), ItemVB)
Output.Write(SelectedItem.Message)
End If
End Sub
End Class
End Namespace
'******************************************************************************
'Item.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace CustomParsingControlSamples
Public Class ItemVB : Inherits Control
Private _message As String
Public Property Message As String
Get
Return _message
End Get
Set
_message = Value
End Set
End Property
End Class
End Namespace
'******************************************************************************