SingingEels : Development Community & Resource

Login

Articles

  • ADO.NET (2)
  • ASP.NET (36)
  • LINQ (5)
  • Security (2)
  • Silverlight (3)
  • SQL (7)
  • Standards (5)
  • WCF (2)

Syndication

  • Articles RSS
  • Blogs RSS

Contribute

  • Our Authors List
  • Member Sign-Up
  • Suggestions Box

Populating Related Drop Down Lists With AJAX

(Jul 05 2007 - 05:23:03 PM by Rob Meade) - [print article]

Overview

Prior to ASP.Net the way we would add related drop down lists was by creating arrays in JavaScript and writing them out to the page. Due to the size and nature of the organization I work for (the NHS in the UK) these arrays could be considerable in size and typically made the page bloated.

For me JavaScript arrays always meant large amounts of data being written to the page. The majority of the data would be unnecessary for the bulk of users. Tabs and carriage returns in the JavaScript would normally be added (and often left in) to make the data more readable for debugging purposes. All of this combined with the actual data in the arrays themselves could create very large page sizes.

With my recent experimentation with AJAX and the start of a new project I was curious to see if it could help solve the problem in a better way, the following is a small example that might help some of you who wish to populate related drop down lists without having to store all possibilities in the page, but instead calling the data from the server as needed.

Getting Started

Lets start by creating a new AJAX Enabled Web Site.

New AJAX Enabled WebSite

Adding Some Controls

With our Default.aspx page in Design View, let’s add an UpdatePanel to our page. This should be placed beneath the ScriptManager control which has been added for us.

The UpdatePanel will provide us with an area of the screen that we can update asynchronously, in it we need to place our controls, specifically the ones we want to see update, lets add our two DropDownList controls to the UpdatePanel:

Adding Update Panel And Drop Down Lists

To make things a bit easier to follow, we will update the IDs of the two DropDownLists to something a little more meaningful. So we'll name them ddlShows and ddlCharacters respectively. If you now view the source of our page (default.aspx) it should resemble the following:

<form runat="server">
   <asp:ScriptManager ID="ScriptManager1" runat="server" />
   <asp:UpdatePanel ID="UpdatePanel1" runat="server">
       <ContentTemplate>
           <asp:DropDownList ID="ddlShows" runat="server" />
           <asp:DropDownList ID="ddlCharacters" runat="server" />
       </ContentTemplate>
   </asp:UpdatePanel>
</form>

A Little Bit About Triggers

In order for our UpdatePanel to actually do anything it needs to know what the trigger will be, meaning what is going to happen in order for the UpdatePanel to update.

By default the UpdatePanel has its ChildrenAsTriggers property set to True, so the two DropDownLists that we have added to our UpdatePanel will already act as Triggers, however, I like to actually see and know what's going on in my applications, so we're going to change the ChildrenAsTriggers property to False and add the Trigger manually.

There are also other reasons why you may not want ChildrenAsTriggers set to True, an example of which is mentioned in the AJAX online documentation. If you were to have two UpdatePanel controls and you want a PostBack from the first panel to update the content of the second panel but not update its own content.

Another reason for adding Triggers manually might be if the control you want to use as a Trigger is NOT inside the UpdatePanel but located elsewhere on your page.

One important thing to bare in mind is that if we change our ChildrenAsTriggers property to False we also need to change the UpdateMode property of our UpdatePanel from its default setting Always, to Conditional. If we do not do this an InvalidOperationException will be thrown because the combination of properties is not allowed.

An UpdateMode of Always means that the UpdatePanel will update with every PostBack, with it's UpdateMode set to Conditional the UpdatePanel will only update when one of its Triggers causes it to.

Update Panel Properties

Adding Our Trigger

Flick back to DesignView, select the UpdatePanel on the page and in the Properties window change the ChildrenAsTriggers property to False.

We will now add our Trigger, with the UpdatePanel still selected, click on the ellipsis (…) button for Triggers (located under Behavior) in the Properties window. The UpdatePanelTrigger Collection Editor window is now displayed. Click on the downward arrow on the Add button to select the type of trigger we want, in this case the AsyncPostBack Trigger.

AsnycPostBack Trigger

Now click in the ControlID field and select our first DropDownList (ddlShows), click in the Event field and select SelectedIndexChanged, click OK.

In order for our Trigger to work fully we need to ensure that the control which will act as a Trigger can post back to the server. Select our first DropDownList (ddlShows) and change its AutoPostBack property to True. If you view the source for our page (default.aspx.vb) it should resemble the following:

<form runat="server">
   <asp:ScriptManager ID="ScriptManager1" runat="server" />
   <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
       <ContentTemplate>
           <asp:DropDownList ID="ddlShows" runat="server" AutoPostBack="True" />
           <asp:DropDownList ID="ddlCharacters" runat="server" />
       </ContentTemplate>
       <Triggers>
           <asp:AsyncPostBackTrigger ControlID="ddlShows" EventName="SelectedIndexChanged" />
       </Triggers>
   </asp:UpdatePanel>
</form>

As you can see the UpdatePanelTrigger Collection Editor has automatically added the code for the Triggers section of the UpdatePanel for us, you can have many triggers in this section, all of which can trigger the UpdatePanel to update but for this example one is all we need.

How About Some Data

We could just jump straight in and hard code some values, but I prefer to build some supporting methods which can be used time and time again, this could well help us to expand the project at a later stage.

To start with we'll add the following method which will define the structure of our DataTable. Our DataTable will contain two DataColumns, the first will store an ID for the items added to the DataTable, the second will hold the actual text that we want to display for the DropDownList items.

Private Sub DefineDataTable(ByRef dataTable As Data.DataTable)

   ' declare variables

   Dim dataColumn As Data.DataColumn

   ' instantiate

   dataColumn = New Data.DataColumn

   ' define data structure

   dataColumn.ColumnName = "ID"
   dataColumn.DataType = Type.GetType("System.Int32")
   dataColumn.AutoIncrement = True
   dataColumn.AutoIncrementSeed = 0
   dataColumn.AutoIncrementStep = 1

   ' add first column definition to datatable

   dataTable.Columns.Add(dataColumn)

   ' add second column definition to datatable

   dataTable.Columns.Add("Text", Type.GetType("System.String"))

   ' housekeeping

   dataColumn.Dispose()

End Sub

In the above code you can see that the ID column requires a little more work to define than the String column, in addition you'll notice that the AutoIncrementSeed has been set to start at 0 (zero) - this is by design and will be explained later.

Next we'll add a method which will enable us to populate DataRows that are contained within our DataTable.

Private Sub PopulateDataRow(ByRef dataTable As Data.DataTable, ByVal text As String)

   ' declare variables

   Dim dataRow As Data.DataRow

   ' instantiate

   dataRow = dataTable.NewRow

   ' populate datarow

   dataRow("Text") = text

   ' add row to datatable

   dataTable.Rows.Add(dataRow)

End Sub

With our DataTable structure definable and a method to populate it, lets add two more supporting methods which will create new ListItems and populate our DropDownLists.

Private Function CreateListItem(ByVal value As Integer, ByVal text As String) As ListItem

   ' declare variables

   Dim listItem As ListItem

   ' instantiate

   listItem = New ListItem

   ' populate

   listItem.Value = value
   listItem.Text = text

   ' return listItem

   Return listItem

End Function
Private Sub PopulateDropDownList(ByRef dropDownList As DropDownList, ByVal id As Integer, ByVal text As String)

   ' populate

   dropDownList.Items.Add(CreateListItem(id, text))

End Sub

CreateListItem tables a value and some text and uses these to populate its properties, we can call this method many times to create ListItems for either of our DropDownLists - reuse!

PopulateDropDownList is passed a reference to a DropDownList, an integer and a string. By telling this method which DropDownList to populate we can use it for both ddlShows and ddlCharacters - reuse! We'll create the following method to populate the DropDownLists with data:

Private Sub PopulateShows()

   ' declare variables

   Dim dataTable As Data.DataTable
   Dim dataRow As Data.DataRow

   ' instantiate

   dataTable = New Data.DataTable

   ' define datatable

   DefineDataTable(dataTable)

   ' populate datarows

   PopulateDataRow(dataTable, "Please Select")
   PopulateDataRow(dataTable, "AirWolf")
   PopulateDataRow(dataTable, "KnightRider")
   PopulateDataRow(dataTable, "The A-Team")

   ' iterate

   For Each dataRow In dataTable.Rows

       PopulateDropDownList(ddlShows, dataRow.Item("ID"), dataRow.Item("Text"))

   Next

End Sub

PopulateShows will use all of our supporting methods in order to populate ddlShows with the three 80's TV shows shown above (AirWolf, KnightRider, The A-Team).

In it we first create a new instance of a DataTable object; we then call our DefineDataTable method passing it a reference to our DataTable. With the structure defined we can now add rows of data to it by calling PopulateDataRow.

You'll notice that the first of these calls is for "Please Select", this is why we set the AutoIncrementSeed to 0 (zero) earlier on. Often you'll find that the "Please Select" values will be 0 (zero) or "". All of the shows will have IDs 1, 2 and 3 respectively.

Next we need a method to populate the characters for these shows in our second DropDownList (ddlCharacters) so we'll create the following method:

Private Sub PopulateCharacters(ByVal show As String)

   ' declare variables

   Dim dataTable As Data.DataTable
   Dim dataRow As Data.DataRow

   ' instantiate

   dataTable = New Data.DataTable

   ' define datatable

   DefineDataTable(dataTable)

   ' add please select entry

   PopulateDataRow(dataTable, "Please Select")

   ' determine which characters to populate

   Select Case show

       Case "1"

           PopulateDataRow(dataTable, "Dominic Santini")
           PopulateDataRow(dataTable, "Stringfellow Hawke")

       Case "2"

           PopulateDataRow(dataTable, "Kit")
           PopulateDataRow(dataTable, "Michael Knight")

       Case "3"

           PopulateDataRow(dataTable, "B.A")
           PopulateDataRow(dataTable, "Face")
           PopulateDataRow(dataTable, "Hannibal")
           PopulateDataRow(dataTable, "Murdock")

   End Select

   ' clear existing items

   ddlCharacters.Items.Clear()

   ' iterate

   For Each dataRow In dataTable.Rows

       PopulateDropDownList(ddlCharacters, dataRow.Item("ID"), dataRow.Item("Text"))

   Next

End Sub

Once again we are able to use all of our supporting methods - reuse! The show ID is passed to this method and will be used to determine which characters will populate ddlCharacters. Note also the call to clear the existing items, without this, each time we select a show more and more characters will be added to ddlCharacters.

Wrap It Up

We have everything we need to populate a DropDownList based on the selection of another DropDownList using AJAX except for a few lines of code which we will now add to the Page_Load method (default.aspx.vb).

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

   ' check for asyncPostBack

   If Page.IsPostBack = True Then

       If ScriptManager1.IsInAsyncPostBack = True Then

           ' determine sending control

           Select Case ScriptManager1.AsyncPostBackSourceElementID.ToLower

               Case "ddlshows"

                   ' populate characters

                   PopulateCharacters(ddlShows.SelectedValue.ToLower)

           End Select

       End If

   Else

       ' populate shows

       PopulateShows()

   End If

End Sub

In the above code we first check to see if the page has posted back to the server, if it hasn't then we call PopulateShows which will fill ddlShows with our three 80's tv shows. If the page has posted back then we check to see if it has been posted asynchronously, in this example we're assuming this will be the case as no other controls are present on the form.

Next we want to determine which control has caused the asynchronous post back, we obtain the ID of the control by using our ScriptManager object and its AsyncPostBackSourceElementID property. If our asynchronous post back was caused by a SelectedIndexChanged event on ddlShows the new call PopulateCharacters and pass in the ID of the show that was selected.

Run It!

So, we build and run the application. You see that the shows are populated on the Page_Load, you select one and the characters are populated in the second DropDownList. What's to say this was done with AJAX and not just that it all happened so fast that you didn't notice your browser loading the new page? Well, lets do one final thing to prove it.

Return to the design view and above the UpdatePanel add a Label. Select the Label and in the Properties window change its ID to lblDateTime and remove the text value. Now lets amend our PopulateShows method so that when it's called it updates the Text property of lblDateTime to equal the current date and time. Amend PopulateShows so that it resembles the following:

Private Sub PopulateShows()

   ' declare variables

   Dim dataTable As Data.DataTable
   Dim dataRow As Data.DataRow

   ' instantiate

   dataTable = New Data.DataTable

   ' define datatable

   DefineDataTable(dataTable)

   ' populate datarows

   PopulateDataRow(dataTable, "Please Select")
   PopulateDataRow(dataTable, "AirWolf")
   PopulateDataRow(dataTable, "KnightRider")
   PopulateDataRow(dataTable, "The A-Team")

   ' iterate

   For Each dataRow In dataTable.Rows

       PopulateDropDownList(ddlShows, dataRow.Item("ID"), dataRow.Item("Text"))

   Next

   ' populate

   lblDateTime.Text = Now

End Sub

Build and run the application again. Because PopulateShows is only called when the page is not being posted back we know that the date/time displayed in the label will only occur once. Now select a show and you'll see again that the characters are updated in ddlCharacters and the date/time does not change.

Thanks for Reading

I hope you found this, my first article, easy to follow and that it has been of some use to you. Review the source code for yourself to see how it's done. Here's a link to a zip file containing the web site project.

  • Aug 01 2007 - 07:17:14 AM deusBlue

    Thanks for helping me dip my big toe into AJAX

  • Aug 15 2007 - 07:27:47 AM tancev

    Hi,

    Your articles are super but I have one question.

    In pure ASP.NET, without AJAX, I am using this code for showing alert from C# code:

    private void ShowMessage(string value)

    {

    string _script = @"<script language=JavaScript>";

    _script += @"alert(""" + value + @""");";

    _script += @"</script>";

    if (!ClientScript.IsStartupScriptRegistered("clientScript"))

    ClientScript.RegisterClientScriptBlock(this.GetType(), "clientScript", _script);

    }

    I am calling that function from buttonClick event. Problem is when i put that button inside UpdatePanel. If I do so, ShowMessage function will not fire!

    My question is how to show alert, popup window, from AJAX!

  • Aug 16 2007 - 03:21:37 AM Rob Meade

    Hi,

    Thank you for your comments.

    With regards to your query, you can adapt my example above by doing the following which should work for you, you'll have to convert my VB example to C# obviously.

    Add a Page_Render method:

    Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender

    ' reg client side

    RegisterClientSideScriptBlock()

    End Sub

    Add a method for adding the script block:

    Private Sub RegisterClientSideScriptBlock()

    ' declare variables

    Dim scriptBlock As String

    ' populate

    scriptBlock = " function ShowMessage(message)"

    scriptBlock += " {"

    scriptBlock += " alert(message);"

    scriptBlock += " }"

    ScriptManager.RegisterClientScriptBlock(Page, GetType(Page), "ShowMessage", scriptBlock, True)

    End Sub

    Now add your button to the UpdatePanel and set its "OnClientClick" property to call your JavaScript function, in this case:

    ShowMessage("Hello World!")

    Hope this helps.

    Regards

    Rob

  • Oct 18 2007 - 11:01:32 AM cameronmcox

    I am desperately hunting for a way to do third dynamic related drop-down boxes in a row...I am a beginner at all of this...this is the one task I can not get past.

    My user will select from the first drop down (which is populated by a table in my SQL DB), once the selection is made I need the second drop down box to be populated (another table in my SQl database that has a parentID (first drop-down) and a ChildID. once the selection from the second is made the third should populate sutomatically (table in SQl DB that has ParentID(second drop down box) and ChildID(third drop down)

    Please help me...how do I make three to work in a row and all the drop downs are populated from table in the SQl DB.

    Thank you for your help...remember I am a beginner!

  • Oct 20 2007 - 11:23:55 AM Rob Meade

    Hi,

    Give the article above a go, its pretty straight forward and should be easy enough to get you started with the two drop downs, you will see very easily how you can just add another function, similar to "PopulateCharacters" for the third drop down list.

    In the above article I chose to not demonstrate it with the data coming from a database (if I had the article would have been significantly longer), I did however set it up in such a way that you could return a datatable populated with your values from a sql helper object which could be used instead of my hard-coded datatables.

    My suggestion would be first, get the above example working, then modify it so that a third drop down is populated with some values (maybe put some years in as values for the films/characters etc). Once you have all three working successfully, simply switch where the datatables are populated from and bingo - all done.

  • Jan 28 2008 - 03:17:19 PM ricker_silva

    Hi there

    I've been doing that kind of stuff with a 4 set of dropDownLists. tweo for an initial date and other two for a final date. First I didi it by doing async postbacks but the time dealy was really long. I mean, you selected the year and 3 seconds later you got the months loaded. I did this because dates need to fit some restrictions like last date available in the DB and so on.

    What i did then was to improve the code by using just one call to the DB. Then I realize that by calling at the pageload I don't even need to make postbacks, so I write it on javascript all the control events and Voila!! it all work perfectly.

    ...however. Now, when I click on the search button i have, this button verifies the SelectedValue property of the DDLs and iot is alwasy on -1. Wha't i think is happening is that the changes made with javascript are not reflected on server side.

    What can I do, have you some sugestions??

    thank you

  • Sep 29 2009 - 12:11:54 PM techron

    Thanks. This works much better than using Cascading Drop Down Extender, which messes up client side validation.

    So others can find it, this was the error that CascadingDropDown was causing: Microsoft JScript runtime error: Sys.WebForms.PageRequestManagerServerErrorException: Invalid postback or callback argument. Event validation is enabled using <pages enableEventValidation="true"/> in configuration or <%@ Page EnableEventValidation="true" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.

  • Jan 28 2010 - 10:18:11 AM bbryan211

    I use this example and it won't load the second dropdown list, I use the exact same code and I can't figure out why is not working.

    Could somebbody please help me?

You must be logged in to add comments. If you have not already done so, you can create an account here. If you already are a member, you first need to login before you can comment.

Senior Systems Developer, NHS - UK

People to Follow

Experts in the categories related to this article.

  • Jonathan Carter

Related Blogs

These are the most recent blog posts related to this article.

  • Follow up to Self Sorting GridView
  • A Change to the MVC "ActionSelectionAttribute"?
  • How to Handle "Side Content" in ASP.NET MVC
  • LINQ to SQL - Am I Hitting The Database?
  • ASP.NET MVC - Issue with CSS Class Name on ActionLinks

Related Ads

SingingEels.com as of Mar 15 2010 - 02:59:11 PM - (0.3750192)