Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / XHTML

Creating a BulletedList control to replace the asp:BulletedList

3.67/5 (8 votes)
17 Jul 2008CPOL4 min read 1   150  
I needed a simple control to display a bulleted list based on a strongly typed list of strings.

Introduction

I wanted to display a bulleted list of items (<li> tags rendered within a <ul> tag) on a web page, based on a strongly typed list of string objects which I had gathered from some data source.

I noticed that there is a BulletedList control available in .NET, so I tried it. I set the DataSource to my List(Of String) and called DataBind().

It worked — sort of. The bulleted list displayed the content of each string in my list, but it insisted on HTML-encoding every item. As I had hyperlinks within the list (<a href="somepage.htm">click me</a>), it was rendering to the output stream as &lt;a href="somepage.htm"&gt;click me&lt;/a&gt; such that I did not get a hyperlink in the page. Frustrating. Thanks for that auto-encoding, Microsoft!

Background

I spent a little while looking into the BulletedList control and Googling this problem, but it seemed to be just a "feature" of the BulletedList which wasn't controllable (I might be wrong!). Instead of using the asp:BulletedList, I wrote a Repeater to render out the items, but it looked messy in the .aspx page, and I wasn't really happy with it — I wanted a control as I wanted to use it in a few places — a Repeater might have been OK for just one instance, but not several.

I threw my hands up at another short-sighted "feature" of the framework, and went out to enjoy a meatball sub and oatmeal and raisin cookie, with a diet coke on the side.

In the sandwich shop, whilst watching the July rain driving at the pavements outside, I decided to just write a control myself to do what I wanted — I thought I had quite a basic requirement here, and decided that in the time I spent Googling for the solution, I could have solved it myself. Now, all I had to do was get back across the road to the office, in a short sleeved shirt, in the pouring rain. I love Manchester.

Developing the Code

My first step was to create a .vb class file, which I called BulletedList.vb. I put the class inside a namespace to make it easy to register it later on, and made it inherit from Control.

Next, I created a property called "Items" which provides read/write access to the list of items I wanted to render. Remember, I wanted to use a strongly typed list of strings. I save the items within the ViewState of the control.

VB
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

So, I can now hold a list of the strings I want to display on the page. The next thing is to tell the control how it should render them. I simply overrode the "Render" method of the control, and provided some custom logic:

VB
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

As you can see, it is reasonably straightforward. If there are any items in the list, then I create the opening <ul> tag, and then write out each item contained within <li> tags. Once all the items have been written, I close the <ul> tag, and call the base Render method (it doesn't actually do anything, it's just there for completeness).

HTML Encoding the Text

I appreciate the sentiment of HTML-encoding the text from a security point of view; that HTML data retrieved from data stores (especially where that data has come from an unknown source) should always be HTML-encoded before it is displayed out onto web pages to help prevent injection attacks.

For this reason, I provided another property, EncodeHtml, to allow the user of the control to decide whether they want the output to be encoded before it gets rendered. The property is a simple boolean value saved in the ViewState:

VB
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

I then amended the Render method to take account of this property, and HTML-encode the text if desired. This concludes the control's implementation.

VB
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

Using the code is simple. First, register the control at the top of the .aspx file:

ASP.NET
<%@ Register TagPrefix="SCC" Namespace="SCC.WebUserControls" %>

Next, add the control into the .aspx markup where you want the bulleted list to appear:

ASP.NET
<SCC:BulletedList ID="blHyperlinks" runat="server" /> <!-- Render all items "as-is" -->

<SCC:BulletedList ID="blSafeHtml" runat="server" EncodeHtml="true" /> <!-- Encode HTML -->

Finally, in Page_Load or similar, bind the control to your desired list of strings:

VB
Dim Hyperlinks As New List(Of String)
Me.blHyperlinks.Items = Hyperlinks

and that's it. "Should just work".

The downloadable file contains a fully XML-commented code listing for your perusal. Any comments welcome (particularly, if you can tell me whether the asp:BulletedList can be configured to not HTML-encode the text that you bind to it from a strongly typed list of strings!).

History

  • 1.0 (Original, 14 July 2008): Original version.
  • 1.1 (15 July 2008): Fixed a null-reference when the control rendered, if no items were in the list.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)