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:
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:
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:
ProtectedFunction GetStateIndex(ByVal stateCode AsString) AsInteger For i AsInteger = 0 To _states.DefaultView.Count - 1 If _states.DefaultView(i)("StateCode") = stateCode Then Return i EndIf Next
Return 0 EndFunction
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.
PrivateSub dgrAddresses_ItemCreated(ByVal sender AsObject, ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) Handles dgrAddresses.ItemCreated If e.Item.ItemType = ListItemType.EditItem Then AddHandlerCType(e.Item.FindControl("ddlState"), DropDownList).SelectedIndexChanged, AddressOf state_selectedIndexChanged EndIf
If e.Item.ItemType = ListItemType.Footer Then AddHandlerCType(e.Item.FindControl("ddlAddState"), DropDownList).SelectedIndexChanged, AddressOf addState_selectedIndexChanged EndIf EndSub
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:
PrivateSub state_selectedIndexChanged(ByVal sender AsObject, ByVal e As System.EventArgs) Dim query AsNew 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() EndSub
PrivateSub addState_selectedIndexChanged(ByVal sender AsObject, ByVal e As System.EventArgs) Dim query AsNew BLL.CountyQuery("CountyName") query.County = New BLL.County query.County.StateCode = CType(sender, DropDownList).SelectedItem.Text
Dim FooterIndex AsInteger = 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() EndSub
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:
PrivateSub dgrAddresses_ItemDataBound(ByVal sender AsObject, 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 AsNew 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")) IfNot IsNothing(li) Then li.Selected = True EndIf EndIf
EndSub
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!