创建 BulletedList 控件以替换 asp:BulletedList
我需要一个简单的控件来显示基于强类型字符串列表的列表。
引言
我想在网页上显示一个项目列表(在 <ul>
标签内渲染的 <li>
标签),该列表基于我从某个数据源收集的强类型字符串对象列表。
我注意到 .NET 中有一个 BulletedList
控件,所以我尝试了一下。我将 DataSource
设置为我的 List(Of String)
并调用了 DataBind()
。
它奏效了——勉强奏效。项目符号列表显示了我列表中的每个字符串的内容,但它坚持对每个项目进行 HTML 编码。由于列表中有超链接(<a href="somepage.htm">click me</a>),它被渲染到输出流中为 <a href="somepage.htm">click me</a>,导致我在页面上没有获得超链接。真令人沮丧。感谢微软的自动编码!
背景
我花了一些时间研究 BulletedList
控件并搜索这个问题,但它似乎只是 BulletedList
的一个“特性”,并且无法控制(我可能是错的!)。我没有使用 asp:BulletedList
,而是编写了一个 Repeater
来渲染这些项目,但在 .aspx 页面中看起来很混乱,而且我对此并不满意——我想要一个控件,因为我希望在几个地方使用它——一个 Repeater
可能只适用于一个实例,但不是多个。
我放弃了框架的另一个短视的“特性”,出去享用了一个肉丸三明治和燕麦葡萄干饼干,还有一杯健怡可乐。
在三明治店里,一边看着七月的雨水拍打着外面的路面,我决定自己写一个控件来做我想做的事情——我认为我的需求非常基本,而且我认为我花费在搜索解决方案上的时间,本可以自己解决它。现在,我只需要回到马路对面回到办公室,穿着短袖衬衫,在大雨中。我爱曼彻斯特。
开发代码
我的第一步是创建一个 .vb 类文件,我将其命名为 BulletedList.vb。我将该类放在一个命名空间中,以便以后方便注册它,并使其继承自 Control
。
接下来,我创建了一个名为“Items
”的属性,该属性提供对我想渲染的项目列表的读/写访问。请记住,我想使用强类型的字符串列表。我将项目保存在控件的 ViewState
中。
Public Property Items() As List(Of String)
Get
If IsNothing(ViewState("Items")) Then
' Instantiate the list of items to a new list; which will allow us to do ".Items.Add" from elsewhere in code without
' having to worry about null values checks.
ViewState("Items") = New list(Of String)
End If
Return DirectCast(ViewState("Items"), List(Of String))
End Get
Set(ByVal value As List(Of String))
ViewState("Items") = value
End Set
End Property
因此,我现在可以保存要在页面上显示的字符串列表。接下来是告诉控件它应该如何渲染它们。我只是覆盖了控件的“Render
”方法,并提供了一些自定义逻辑。
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
' Only output something if there are items in the collection:
If Items.Count > 0 Then
' Output start tag <ul>
writer.RenderBeginTag(HtmlTextWriterTag.Ul)
' Output all the items
For Each s As String In Items
writer.RenderBeginTag(HtmlTextWriterTag.Li)
writer.Write(s)
writer.RenderEndTag()
writer.WriteLine()
Next
' Output end tag </ul>
writer.RenderEndTag()
End If
MyBase.Render(writer)
End Sub
如您所见,这相当直接。如果列表中有任何项目,我将创建开头的 <ul>
标签,然后将每个包含在 <li>
标签中的项目写出来。一旦所有项目都已写出,我将关闭 <ul>
标签,并调用基类的 Render
方法(它实际上并没有做什么,只是为了完整性)。
HTML 编码文本
我理解从安全角度考虑 HTML 编码文本的意图;从数据存储中检索的 HTML 数据(尤其是在数据来自未知来源时)在显示到网页之前应始终进行 HTML 编码,以帮助防止注入攻击。
出于这个原因,我提供了另一个属性 EncodeHtml
,允许控件的用户决定是否要在输出渲染之前对其进行编码。该属性是一个简单的布尔值,保存在 ViewState
中。
Public Property EncodeHtml() As Boolean
Get
If IsNothing(ViewState("EncodeHtml")) Then
Return False
Else
Return DirectCast(ViewState("EncodeHtml"), Boolean)
End If
End Get
Set(ByVal value As Boolean)
ViewState("EncodeHtml") = value
End Set
End Property
然后,我对 Render
方法进行了修改,以考虑此属性,并在需要时对文本进行 HTML 编码。这标志着控件实现的结束。
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
' Only output something if there are items in the collection:
If Items.Count > 0 Then
' Output start tag <ul>
writer.RenderBeginTag(HtmlTextWriterTag.Ul)
' Output all the items
For Each s As String In Items
writer.RenderBeginTag(HtmlTextWriterTag.Li)
If EncodeHtml Then
writer.Write(HttpContext.Current.Server.HtmlEncode(s))
Else
writer.Write(s)
End If
writer.RenderEndTag()
writer.WriteLine()
Next
' Output end tag </ul>
writer.RenderEndTag()
End If
MyBase.Render(writer)
End Sub
Using the Code
使用代码很简单。首先,在 .aspx 文件的顶部注册控件。
<%@ Register TagPrefix="SCC" Namespace="SCC.WebUserControls" %>
接下来,在 .aspx 标记中添加控件,您希望项目符号列表出现的位置。
<SCC:BulletedList ID="blHyperlinks" runat="server" /> <!-- Render all items "as-is" -->
<SCC:BulletedList ID="blSafeHtml" runat="server" EncodeHtml="true" /> <!-- Encode HTML -->
最后,在 Page_Load
或类似方法中,将控件绑定到您所需的字符串列表。
Dim Hyperlinks As New List(Of String)
Me.blHyperlinks.Items = Hyperlinks
就是这样。“应该可以正常工作”。
可下载的文件包含一个完整的 XML 注释代码列表供您查阅。欢迎提出任何意见(特别是,如果您能告诉我 asp:BulletedList
是否可以配置为 **不** HTML 编码您从强类型字符串列表中绑定到它的文本!)。
历史
- 1.0(原始,2008 年 7 月 14 日):原始版本。
- 1.1(2008 年 7 月 15 日):修复了控件渲染时列表为空的 null 引用问题。