Render RadioButtonList as an Unordered List UL

In my latest project, I found the need to use the RadioButtonList which spits out the absolute nastiest HTML to render it unusable (for me anyway). The control gives you to HTML options using the RepeatLayout attribute.

Flow spits out a series of span’s and label’s with BR’s if you don’t specify RepeatDirection of Horizontal. And choosing Table gives you a nicely unaccessible table you can’t format easily. Because of these limitation’s, I have always stayed away from CheckBoxList and RadioButtonList controls.

My solution is to write my own RadioButtonList, but to spit it out as an Unordered List which I can style very easily horizontally or vertically, little or lots of padding, all with CSS.

I unfortunately have been forced to regress back to ASP.NET 1.1 and VB.NET for a little while until the company upgrades to 2.0. Hence why I wrote the following instead of writing a CSS Control Adapter.

Imports System
Imports System.ComponentModel
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls

Namespace MyControls

 <DefaultProperty("Text"), _
ToolboxData("<{0}:ULRadioButtonList runat=server />") _
, Description("Creates a RadioButtonList using an Unordered List.")> _
Public Class ULRadioButtonList
Inherits RadioButtonList

 Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)

//"SBE 01/28/2007:  just in case...not sure if this is needed or not
Controls.Clear()

//"SBE 01/28/2007:  not sure why I am leaving quotes as a param...I was just undecided if I would ever need to use " instead of "
Dim inputFormatString As String = "<input id={0}{1}{0} name={0}{2}{0} type={0}radio{0} value={0}{3}{0} {4} />"
Dim labelFormatString As String = "<label for={0}{1}{0}>{2}</label>"

//"SBE 01/28/2007:  if user sets the cssclass, add it to the <ul> tag
If (Not MyBase.CssClass Is Nothing AndAlso MyBase.CssClass <> "") Then
     writer.WriteLine("<ul class="" & MyBase.CssClass & "">")
Else
     writer.WriteLine("<ul>")
End If

//"SBE 01/28/2007:  loop through the dataitems
For index As Integer = 0 To Items.Count - 1
     writer.Indent += 1
     writer.WriteLine("<li>")
     writer.Indent += 1

     Dim inputBuilder As New StringBuilder
     Dim labelBuilder As New StringBuilder
     Dim checked As String = ""

     If (Items(index).Selected) Then
          checked = "checked"
     End If

     inputBuilder.AppendFormat(inputFormatString, """", MyBase.ClientID & "_" & index.ToString(), MyBase.UniqueID, Items(index).Value, checked)
     labelBuilder.AppendFormat(labelFormatString, """", MyBase.ClientID & "_" & index.ToString(), Items(index).Text)

     writer.WriteLine(inputBuilder.ToString())
     writer.WriteLine(labelBuilder.ToString())

     writer.Indent -= 1

     writer.WriteLine("</li>")
     writer.WriteLine()
     writer.Indent -= 1

 Next

 writer.WriteLine("</ul>")

 End Sub

 End Class

End Namespace

Before I started writing this, I found a beautiful website SimplyGold who had already wrote something similar for the CheckBoxList. Thanks for saving me some time!

Now playing: MenomenaMuscle n’ Flo

Prevent Multiple Button Clicks in ASP.NET – How to Implement the Netflix "Please Wait…" Button

Netflix has a nice submit button so that when you click it, it changes to “Please Wait…” and disables itself to prevent you from clicking it again.


before submit


after submit

I have problems all the time with my users doing this, and I never spent time trying to solve it.

You would think all you need to do is wire up the button to some Javascript that would just disable the button. That is what I thought. The problem is once you do that, it prevents the postback from happening.

The way around this is to have a script run “OnBeforeUnload” so that it is still disables it late enough to not prevent postback. The only problem with this is that it would disable the button on each Unload request. We must solve this also.

I realize you can throw all this in a custom button, but since I just got this to work, here is my quick solution:

Put the following in your page's base class (assuming you have one). If not, throw it in your codebehind.


public void SetOnClickDisable(System.Web.UI.WebControls.Button control)
{
control.Attributes.Add("onclick","formSubmitted = true;");

string javascript;

javascript = "if (!formSubmitted) return;";
javascript += "var button = document.getElementById('" + control.ID + "');";
javascript += "button.value = \" Please Wait... \";";
javascript += "document.body.style.cursor = 'wait';";
javascript += "button.disabled = true;";

RegisterStartupScript("onbeforeunload", "");
}

The first line tells the function that gets called that the button was pressed triggering postback. This will prevent every Unload from running this function. The rest assembles the function to disable the Button you pass into it.

Next, call it from your page.


base.SetOnClickDisable(SubmitSearch);

Thats all you need to get it working. Now some cool css to make it look like a Netflix button. In your css file (or in the page if you want) put:


.netflix-button
{
font: 11px arial,helvetica, sans-serif;
font-weight: bold;
letter-spacing: 1;
color: #333333;
cursor: pointer;
cursor: hand;
background-image : url(/img/button.gif);
background-color : White;
}

And then in your aspx page put:



You also need the background for the image which you can get here and save it in the path you specify in your css .netflix-button class above.

***Understand that this is a Netflix image, so ONLY use this as an example. In fact, I have no right to even link to it, but I figure that it is easier to show it this way, and Netflix won't care as long as we don't rip it off.

Note that this works with the validation controls also. The buttons will not be disabled if the postback is stopped in the validation stage.

The main problem right now is that this will only work for one button on a page. It isn't the worst deal in the world because it isn't that often that I have multiple buttons on a page that spawn long running operations. In any case, if anyone wants to solve that, go for it!

If I left anything out, let me know as this works successfully over here. And if it works, let me know too!