CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Eric Wise

Business & .NET

Dropdownlist Autopostback in a Datagrid EditItemTemplate

Here's a fun one I did for the IAmBrilliant project.  There are several places in the system where there is entering and editing of address information.  So we wanted to come up with a nice little user control that could be shared across pages.  (You can download the source page here since community server is a pain in the ass about displaying html tags)

So as you can see from the source page we have a datagrid with a variety of template columns that allows editing existing rows and adding new items in the footer.  The template columns are also used to format the addresses nicely.

Two things I'd like to draw your attention to is the state and county dropdownlists.  It was a user requirement that the state be bound from the database and that when you select a state it populates the county dropdowns with all the counties for that state.  Of course this also introduces the complexity on edit of not only handling the postback gracefully, but setting the selected index of the dropdown list.

Step 1: Populating the state list

Looking at the html file you'll see that we are using a function in the codebehind file to populate the state dropdown- Datasource='<%# GetStates() %>'

The concept that you can set properties on your objects by calling a codebehind function is a pretty powerful one.  All you have to do is declare the method being called as protected or public and then the page can access them.  I am using my RAD Pattern in this application, so the GetStates() method is as simple as this:

    Protected Function GetStates() As DataTable
        _states = Me.GetDomainManager.ListSummary(New BLL.StateQuery)
        Return _states
    End Function

Notice that it returns a datatable, and it stores the table in the _states variable for use in step 2.

 

Step 2: Setting the index

In the case of adding a state all we have to do is call GetStates() and we're finished since it is a new address and they have not picked a state yet.  However, in the EditItemTemplate we must set the state dropdown to the index of the state recorded in the database.  We remember stashing the datatable used to bind the dropdown in the _states variable, now we're going to write a method to find the index of the state from the database.  To do this, we'll call the method from our ascx file like such:

SelectedIndex='<%# GetStateIndex(DataBinder.Eval(Container, "DataItem.State")) %>

In the code behind we have created a method that takes in the state of the address and passes out the index of the state in the datatable like such:

    Protected Function GetStateIndex(ByVal stateCode As String) As Integer
        For i As Integer = 0 To _states.DefaultView.Count - 1
            If _states.DefaultView(i)("StateCode") = stateCode Then
                Return i
            End If
        Next

        Return 0
    End Function

Like magic, the dropdownlist will now be set to the proper state when a user clicks edit.

 

Step 3: Handling the state postback

Now let's look at our second requirement, populating the county list from the state dropdown.  For this to work, we must register the SelectedIndexChanged event for dropdown in both the edititem and the footer.  We'll do this in the ItemCreated() event of the datagrid which is fired every time an item is added to the grid.

    Private Sub dgrAddresses_ItemCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) Handles dgrAddresses.ItemCreated
        If e.Item.ItemType = ListItemType.EditItem Then
            AddHandler CType(e.Item.FindControl("ddlState"), DropDownList).SelectedIndexChanged, AddressOf state_selectedIndexChanged
        End If

        If e.Item.ItemType = ListItemType.Footer Then
            AddHandler CType(e.Item.FindControl("ddlAddState"), DropDownList).SelectedIndexChanged, AddressOf addState_selectedIndexChanged
        End If
    End Sub

Now when the postback is called from our dropdowns, it knows what method to call.

 

Step 4: Populating the county list

Now we must set up the methods to handle the postback event referenced above (addState_selectedIndexChanged and state_selectedIndexChanged).  Each of these methods grabs the sender (state dropdown) to find out which state they picked then queries the database to populate the county list for that state like such:

    Private Sub state_selectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim query As New BLL.CountyQuery("CountyName")
        query.County = New BLL.County
        query.County.StateCode = CType(sender, DropDownList).SelectedItem.Text

        Dim ddlCountyList As DropDownList = CType(dgrAddresses.Items(dgrAddresses.EditItemIndex).FindControl("ddlCounty"), DropDownList)
        ddlCountyList.DataSource = Me.GetDomainManager.ListSummary(query)
        ddlCountyList.DataBind()
    End Sub

    Private Sub addState_selectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim query As New BLL.CountyQuery("CountyName")
        query.County = New BLL.County
        query.County.StateCode = CType(sender, DropDownList).SelectedItem.Text

        Dim FooterIndex As Integer = dgrAddresses.Controls(0).Controls.Count - 1
        Dim ddlCountyList As DropDownList = CType(dgrAddresses.Controls(0).Controls(FooterIndex).FindControl("ddlAddCounty"), DropDownList)
        ddlCountyList.DataSource = Me.GetDomainManager.ListSummary(query)
        ddlCountyList.DataBind()
    End Sub

Note in the addState_selectedIndexChanged how the FooterIndex is found.  It's something to remember if you ever need to access the footer row of a datagrid.

 

Step 5: Don't forget about editing the county!

A small gotcha you run into when editing an existing record is that we've loaded and set the state index and now we have to handle the county dropdown!  The proper place to do this is in the datagrid's ItemDataBound event since at this point the state dropdown has already been filled and set.  (For more on the order of events see MSDN)

So in our grid itemdatabound we want to see if any of the rows is currently in edit mode, and if so grab the value of the state to load the county list like such:

    Private Sub dgrAddresses_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) Handles dgrAddresses.ItemDataBound
        If dgrAddresses.EditItemIndex >= 0 Then
            If e.Item.ItemIndex = dgrAddresses.EditItemIndex Then
                'load and set county
                Dim drv As DataRowView = CType(e.Item.DataItem, DataRowView)

                Dim query As New BLL.CountyQuery("CountyName")
                query.County = New BLL.County
                query.County.StateCode = drv("state")

                Dim ddlCountyList As DropDownList = CType(e.Item.FindControl("ddlCounty"), DropDownList)
                ddlCountyList.DataSource = Me.GetDomainManager.ListSummary(query)
                ddlCountyList.DataBind()

                Dim li As ListItem = ddlCountyList.Items.FindByText(drv("county"))
                If Not IsNothing(li) Then li.Selected = True
            End If
        End If

    End Sub

Note the use of the DataRowView object.  This is a good way to grab the data from the datasource while the grid is being bound.

 

Conclusion

Datagrids in ASP .NET are very powerful and flexible.  I hope that the techniques I've shown you today can help you to provide a better user experience!


Published Jul 18 2005, 01:35 PM by Eric Wise
Filed under: ,

Comments

Nicholas said:

This must be painfully slow with ViewState enabled..
# July 18, 2005 3:37 PM

Eric Wise said:

Not in the slightest...

The state list is cached. The postback loads are only called if someone edits or adds a new record.

We have several hundred concurrent users in our applications and there's been no performance problems.
# July 19, 2005 6:12 AM

Christopher Steen - Learning .NET said:

ASP.NET Page Profiling using Page Tracing
Channel 9 Forums &#187; The Videos &#187; Don Box - What goes into...
# July 21, 2005 9:15 PM

Erhan Hosca said:

slow ..slow.. slow

assume USA expands to cover 12000 states in the not so distant future..

consider stuffing the list into a Dictionary so that you won't have to write silly looping code like this ..

Protected Function GetStateIndex(ByVal stateCode As String) As Integer
For i As Integer = 0 To _states.DefaultView.Count - 1
If _states.DefaultView(i)("StateCode") = stateCode Then
Return i
End If
Next

Return 0
End Function
# August 8, 2005 4:16 PM

Eric Wise said:

Well, the suggestion of expansion to 12000 states is kind of silly.

Are you suggesting that searching a dictionary (which certainly does not "magically" jump to the state), is all that much faster than enumerating through 50 records?
# August 11, 2005 11:11 PM

Ali said:



The source code for this example is not working ..
It is missing some files & classes ..

Can you provide the full working examples, please ... ?

I really think that this is a very professional way of using datagrids ...

Thanks ,,
# November 29, 2005 12:21 AM

Marcel Ardaia said:

Good Morning,

Could you help me to translate the code to C#?

Thank's

marcel.ardaia@gmail.com

# October 31, 2006 6:19 AM

Srinivas said:

The source code for this example is not working ..

It is missing some files & classes ..

Can you provide the full working examples, please ... ?

# April 5, 2007 6:19 PM

Marcia said:

Thanks heaps for the tip to finding the FooterIndex!

# May 30, 2007 10:35 PM

DIEPHUS said:

That`s FOR Marcel Ardaia

private void dgrAddresses_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)

{

   if (dgrAddresses.EditItemIndex >= 0) {

       if (e.Item.ItemIndex == dgrAddresses.EditItemIndex) {

           //load and set county

           DataRowView drv = (DataRowView)e.Item.DataItem;

           BLL.CountyQuery query = new BLL.CountyQuery("CountyName");

           query.County = new BLL.County();

           query.County.StateCode = drv("state");

           DropDownList ddlCountyList = (DropDownList)e.Item.FindControl("ddlCounty");

           ddlCountyList.DataSource = this.GetDomainManager.ListSummary(query);

           ddlCountyList.DataBind();

           ListItem li = ddlCountyList.Items.FindByText(drv("county"));

           if ((li != null))

               li.Selected = true;

       }

   }

}

# February 7, 2008 3:45 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!

Our Sponsors

Free Tech Publications