Adding Non-Selectable ListItems To An ASP.NET DropDownList Control

Recently asked on Yahoo! Answers:

Im working in dropdown list box in asp .net
where i wan d dropdown list box to have headin for each 10 item, and the heading should not get selected during run time. Plz help

This is easy to do if you dynamically bind each ListItem to your DropDownList; basically, you introduce a while loop to the mix to occasionally insert the requested number of breaks. Then, we add a CustomValidator control to ensure the user can’t select one of the heading items.

I won’t have a working example but I will provide a text file at the end which contains all the code.

Step 1: The DropDownList Control

I’m assuming this questioner wants to dynamically bind some data to the DropDownList; say, some records from a database will be the data source for the ListItems.

If so, on the ASP.NET page itself, we simply declare a DropDownList control. We’re also going to add two other controls: A Label, which will advise us of any errors; and a CustomValidator.

<asp:DropDownList runat="server" ID="ddlMyList" />
<asp:Label runat="server" ID="lblStatus" />
<asp:CustomValidator runat="server" ID="cvMyList"
	OnServerValidate="cvMyList_onValidate"
	ErrorMessage="You cannot select this item. Please select a different item"
	Display="Dynamic" />

Notice that unlike the usual ASP.NET validator parameters, this CustomValidator control does not have a ControlToValidate property declared. Since I’m handling the validation via code, I don’t need to set that property; in fact, it’s to my advantage to not set that property.

The CustomValidator does, however, have an OnServerValidate event handler, which I have set to be an as-yet-undescribed subroutine. More on this shortly.

Aside: Of course, you could always create the headings and ListItems by hand:

<asp:DropDownList runat="server" ID="ddlMyList">
	<asp:ListItem Value="" Text="Heading 1" />
	<asp:ListItem Value="0" Text="0" />
	<asp:ListItem Value="1" Text="1" />
	<asp:ListItem Value="2" Text="2" />
	<asp:ListItem Value="3" Text="3" />
	<asp:ListItem Value="4" Text="4" />
	<asp:ListItem Value="5" Text="5" />
	<asp:ListItem Value="6" Text="6" />
	<asp:ListItem Value="7" Text="7" />
	<asp:ListItem Value="8" Text="8" />
	<asp:ListItem Value="9" Text="9" />
	<asp:ListItem Value="" Text="Heading 2" />
	<asp:ListItem Value="10" Text="10" />
	<asp:ListItem Value="11" Text="11" />
	<asp:ListItem Value="12" Text="12" />
	<asp:ListItem Value="13" Text="13" />
	<asp:ListItem Value="14" Text="14" />
	<asp:ListItem Value="15" Text="15" />
	<asp:ListItem Value="16" Text="16" />
	<asp:ListItem Value="17" Text="17" />
	<asp:ListItem Value="18" Text="18" />
	<asp:ListItem Value="19" Text="19" />
</asp:DropDownList>

Step 2: The Page_Load Event Handler

Let’s focus on how we would dynamically bind headings to the DropDownList.

First, we need a subroutine to do the binding; I’ll call it ddlMyList_dataBind. It will be invoked by the Page_Load event.

Generally speaking, you only want to databind a control if the page is not posting back. If you don’t check for postback, and you rebind the control before you handle user input, the control will reset — and the user’s input / choices will be lost.

Sub Page_Load(ByVal Sender As Object, E As EventArgs)
	If Not Page.IsPostBack Then
		'bind the dropdownlist
		ddlMyList_dataBind()
	End If
End Sub

Step 3: The ddlMyList_dataBind Subroutine

For this example, I am assuming the questioner has the values for his DropDownList stored in a SQL Server database. Of course, you can bind a DropDownList’s ListItem values to virtually any data structure — XML, an array, whatever.

I am assuming the table that stores the ListItem values has two fields: text_field and value_field, where text_field holds the information to be displayed to the user and value_field holds the data that will be the ListItem’s value.

Our databinding subroutine does the following:

  • Declares our database connection and command, plus a simple looping variable.
  • Opens a connection to the database and populates a data reader with the appropriate database records.
  • Checks if there are any records in the data reader.
  • If there are, we go through the records in the data reader one by one.
  • If this is the first item in the list, or the current item is divisible by 10, we add a generic heading as a ListItem with no value; in this case, 10 dashes. It is critical that our heading ListItems do not have a value. More on that shortly.
  • We add a ListItem, with both text and value properties, to the DropDownList. It is critical that all the ListItems from the data source have a value.
  • The looping variable is incremented by 1.
  • If there is a database error, or there are no records to display, we report that fact as the text of the Label control,  and we disable the DropDownList.
Sub ddlMyList_dataBind()
	'Subroutine that binds listitems to dropdownlist
	'This requires the System.Data and System.Data.SqlClient namespaces

	'Open connection to database; declare SQL statement
	'You really should use parameterized queries, but we'll do this as straight SQL
	Dim objConn As New SqlConnection("connection string")
	Dim objCmd As New SqlCommand("your SQL statement", objConn)
	objCmd.CommandType = CommandType.Text

	'We need a generic loop counter
	Dim I As Integer = 0

	Try
		'connect to db
		objConn.Open()

		'create a data reader and populate it with SQL statement results
		Dim objReader As SqlDataReader = objCmd.ExecuteReader()

		'if the reader has records, let's bind the results
		If objReader.HasRows() Then

			'go through all items in the recordset
			'we'll read the items one by one
			Do While objReader.Read()

				'add a heading at the top of the list and every 10 additional items
				If I Mod 10 = 0 Then
					ddlMyList.Items.Add(New ListItem("----------"))
				End If

				'add the current data reader item
				ddlMyList.Items.Add(New ListItem(objReader("text_field", "value_field"))

				'increment the loop counter
				I += 1

			Loop

		Else

			'report that there are no items to display in the list
			' and disable the dropdownlist
			lblStatus.Text = "There are no records for this item"
			ddlMyList.Enabled = False

		End If

		'close the connection
		objConn.Close()

		'clean up the database objects
		objCmd.Dispose()
		objConn.Dispose()

	Catch ex As Exception

		'if there is an error, report it
		' and disable the dropdownlist
		lblError.Text = ex.Message
		ddlMyList.Enabled = False

	End Try

End Sub

Step 4: The CustomValidator Control

We’ve purposefully set the heading ListItems to have no value because we want to ensure users cannot select those headings. And the way we’re going to do that is with our CustomValidator control.

The cvMyList_onValidate subroutine, named to handle the OnServerValidate event for our CustomValidator control, is going to check the SelectedValue property of our DropDownList.

Since only the headings have no value, if the selected value of ddlMyList is a null or empty string, we know the user has selected a heading; and we can then invalidate the page and display an error message.

Sub cvMyList_onValidate(ByVal Sender As Object, E As ServerValidateEventArgs)

	'if the selected value is empty,
	'then the page is not valid;
	'display the validator's error message
	If String.IsNullOrEmpty(ddlMyList.SelectedValue) Then
		E.IsValid = False
	Else
		E.IsValid = True
	End If

End Sub
If you’d like to learn more about the CustomValidator control, check out Scott Mitchell’s excellent article at 4GuysFromRolla.com.

4 Comments

  1. I really hope Priya is putting me on.

    But in the off chance he isn’t, let me point out that there are about a million code converters on the Web, and that changing the declarations and control structures from VB.NET to C# should be within the powers of all but the n00biest of n00bs.

    So sure, I could give Priya the C# code — I could do the arduous task of translating code such as

    Dim objConn As New SqlConnection("connection string")
    Dim objCmd As New SqlCommand("your SQL statement", objConn)
    

    to its C# equivalent:

    SqlConnection objConn = new SqlConnection("connection string");
    SqlCommand objCmd = new SqlCommand("your SQL statement", objConn);
    

    But I’m not going to do so.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!