1. Original Entry + Comments2. Write a Comment3. Preview Comment
New comments for this entry are disabled.


June 02, 2007  |  Cascading DropDownList Controls -- Insert mode  |  10587 hit(s)

In response to the post about using casccading (dependent) drop-down lists, a number of people have wondered how you can use similar drop-downs in Insert mode in a FormView control. (Insert mode isn't supported in GridView controls.)

The technique is quite similar to how you use them in Update mode. As with Update mode, in the templates for the FormView control, you have a master and a dependent DropDownList control, each bound separately to a data source control that's inside the template. (The FormView control itself is bound to a data source control that includes Select, Update, and Insert queries for the actual data record.) The data source control for the master DropDownList runs an un-parameterized Select query. The data source control for the dependent DropDownList control runs a query that depends on the current value of the master list.

As with Update mode, the dependent list must establish the parameter value at run time, but you cannot do this entirely programmatically declaratively. So you need code for the same situations:
  • In the FormView control's DataBound event, which reads the current value of the master list and passes it to the query for the dependent list.
  • In the SelectedIndexChanged event of the master list, to update the contents of the dependent list when a new selection is made in the master list.
  • In the FormView control's ItemInserting event, to read the current value of the dependent list and set parameters that will be passed to the Insert query.


Because this is so similar to how you manage the DropDownList controls in Update mode, the code for the insert scenario is very similar, and in fact you can share code for both update and insert modes.

Of course, you also have to create the InsertItemTemplate in the FormView control and create a button/link to be able to switch to it. Again, quite similar to what's done for Update mode. I won't show you the InsertItemTemplate (it's boringly the same as the EditItemTemplate, but here's the big change, haha, for enabling Insert mode:
<ItemTemplate>
<!-- rest of template here -->
<asp:LinkButton ID="Edit" runat="server"
CommandName="Edit">Edit</asp:LinkButton>
<asp:LinkButton ID="New" runat="server"
CommandName="New">New</asp:LinkButton>

</ItemTemplate>
And the data source control for the FormView control with its new Insert query (wrapped for clarity here!):
<asp:AccessDataSource ID="CustomersDataSource" 
runat="server"
DataFile="~/App_Data/cars.mdb"
SelectCommand="SELECT [CustomerID], [Name], [Manufacturer],
[Model] FROM [Customers]"
UpdateCommand="UPDATE [Customers] SET [Name] = ?, [Manufacturer] = ?,
[Model] = ? WHERE [CustomerID] = ?"
InsertCommand="Insert Into [Customers] ([Name], [Manufacturer], [Model])
Values (?, ?, ?)">

<UpdateParameters>
<asp:Parameter Name="Name" Type="String" />
<asp:Parameter Name="Manufacturer" Type="String" />
<asp:Parameter Name="Model" Type="String" />
<asp:Parameter Name="CustomerID" Type="Int32" />
</UpdateParameters>


<InsertParameters>
<asp:Parameter Name="Name" Type="String" />
<asp:Parameter Name="Manufacturer" Type="String" />
<asp:Parameter Name="Model" Type="String" />
</InsertParameters>

</asp:AccessDataSource>

I'll show you here the small differences in code as well. There's a working page here and a source listing here. (At the moment, the links on the sample page to the source listing and explanation are wrong! I'll fix those later. Fixed.)

There is really only one even slightly tricky thing here ... when you switch to Insert mode, all the controls are blank. Per our code, the master drop-down list is populated, but there's no existing data value for the manufacturer value. (Because there's no data at all -- it's a new blank record.) So there's is no master value that can be passed to query for the dependent data. What I did for this scenario was to reuse the code that is used when a selection is made in the master list. That logic extracts a parameter not from the current data record, but from whatever is selected in the master list. (Does that make sense? Hope so.)

The code for this scenario requires updates to the DataBound event; because I wanted to do the same thing I did when an item in the master list is selected, I refactored the code slightly. Here's the whole thing, changes in bold:
Protected Sub FormView1_DataBound(ByVal sender As Object, _
ByVal e As System.EventArgs)
If FormView1.CurrentMode = FormViewMode.Edit Then
Dim dv As System.Data.DataRowView = FormView1.DataItem
listModels = FormView1.FindControl("listModels")
dataSourceModels = FormView1.FindControl("ModelsDataSource")
Dim m As String = dv("Manufacturer")
dataSourceModels.SelectParameters("Manufacturer").DefaultValue = m
listModels.DataBind()
If Not IsDBNull(dv("model")) Then
listModels.SelectedValue = dv("Model")
End If
End If
If FormView1.CurrentMode = FormViewMode.Insert Then
' Need to pre-populate the listModels control when
' the FormView control is switched into Insert mode.
' The listManufacturers list is populated automatically
' by the data source control, but the dependent
' dropdown is not.
PopulateDependentDropDown()

End If
End Sub


Protected Sub listManufacturers_SelectedIndexChanged(ByVal sender _
As Object, ByVal e As System.EventArgs)
PopulateDependentDropDown()
End Sub


Protected Sub PopulateDependentDropDown()
dataSourceModels = FormView1.FindControl("ModelsDataSource")
listManufacturers = FormView1.FindControl("listManufacturers")
listModels = FormView1.FindControl("listModels")
dataSourceModels.SelectParameters(0).DefaultValue = _
listManufacturers.SelectedValue
listModels.DataBind()
End Sub
Anyway, I hope this is useful.

Update 4 June I fixed a bunch of text (not code) in this entry that was confusing, incomplete, or wrong. Moral: Don't try to write blog entries when you're tired. Should be better now, altho I'm always open to suggestions for clarification.




willwad   30 Aug 07 - 9:01 AM

Thank you for the code. I've been trying it with a DetailsView and have gotten it to work for the most part, except the dependent dropdownlist needs to be null for some selections of the master dropdownlist (my database is setup to handle null values for the dependent). The code works great in DetailsViewMode.Insert, but not in DetailsViewMode.Edit. In EditMode the depedent ddl selected value keeps persisting from the current record in the database, thus my depedent ddl always contains the DataBind values from the original master ddl selection, plus any values from the new master ddl selection. Any ideas what's going on? Thanks.

 
mike   09 Sep 07 - 8:17 AM

@willwad -- I don't know what's going on exactly; are you saying that in Edit mode, the dependent list is trying to bind to (potentially) a value that's null? In any event, the way I'd probably have a look is to handling the DataBinding event and see what values each of the controls is getting. Dunno offhand what else to look at. :-(

 
John O'Looney   13 Sep 07 - 12:50 PM

Mike,
Thank you so much for your code. I Have been able to get the edit mode to work but not the insert mode. I keep geting an "Object reference not set to an instance of an object" error. It in the insert mode it can't seem to find the selected value of the first dropdown list. I am desparate....If you are there and can help...I will be most thankful.

John O'Looney
olooney@cviog.uga.edu


 
Stephen J Holzer   04 Oct 07 - 3:56 AM

Thanks Mike!!!!!!!!
I have been trying to do the same thing off and on for weeks. I needed to do it in the InsertItemTemplates of a DetailsView.

I really appreciate your sharing of this code!

Steve


 
Mikael   16 Nov 07 - 7:27 AM

I attempted coding this on my machine and got this error

No value given for one or more required parameters.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.OleDb.OleDbException: No value given for one or more required parameters.

Source Error:


Line 61: listModels = FormView1.FindControl("listModels")
Line 62: dataSourceModels.SelectParameters(0).DefaultValue = listManufacturers.SelectedValue
Line 63: listModels.DataBind()
Line 64: End Sub
Line 65:


Can you steer me in the right direction here?


 
Mark   29 Nov 07 - 10:57 AM

Hey, Mike,

Great article. I wanted you to know that of the four articles on cascading DDLs I've read and tried, your's was the easiest to understand, and the only one that worked for me!

I do have one question, though. In my setup the FormView loads initially in Insert mode. Though the dynamic databinding works fine, the full list of unfiltered items from the datasource for the second-dependent DDL populates that ddl. Only after a selection is made in the first-master DDL is the second DDL properly filtered and populated.

It doesn't look right that no selection is made in the first DDL, but there is already a selection in the second one (let alone a full, unfiltered list at that!).

I've tried adding in various places in the functions created for this code a blank or something at index 0 in the DDL. I even tried using the "AppendDataBoundItems" property for the second DDL (that seems to "break" the whole dynamic binding routine, oddly enough). I can't seem to find the right location to just add something like "Select..." or even a blank when the no selection is made in the first-master DDL.

Could you offer a suggestion on how to implement this?

Thanks!


 
mike   12 Dec 07 - 8:53 AM

Hi, Mark. Sorry for the delay in responding, I didn't initially see your comment. Assuming that I understand your question correctly, I would do the following:

1) Set a width for the second drop-down list so that it has a fixed width even when it's empty.

2) In a Page_Load event handler, do something like this:
Protected Sub Page_Load()
If Not IsPostBack Then
listModels = FormView1.FindControl("listModels")
listModels.Enabled = False
listModels.Items.Clear()
End If
End Sub

3) In the PopulateDependentDropDown method, re-enable the second drop-down list by adding a line like this:
Protected Sub PopulateDependentDropDown()
dataSourceModels = FormView1.FindControl("ModelsDataSource")
listManufacturers = FormView1.FindControl("listManufacturers")
listModels = FormView1.FindControl("listModels")
listModels.Enabled = True ' Re-enables the drop-down list
dataSourceModels.SelectParameters(0).DefaultValue = _
listManufacturers.SelectedValue
listModels.DataBind()
End Sub

HTH,

-- Mike


 
hatem   01 Apr 08 - 1:09 PM

hi, could you please convert this code to C#

 
Ron   10 Feb 10 - 5:52 PM

Hi Mike, question for you? Will this apply to the Listview control as well? I've used a listview and need to do a cascading dropdown (select dept from drop down list, display list of employees from the selected dept in a second ddl.

 
Vic S.   21 Jul 10 - 10:23 AM

This is GREAT... I have been looking for this kind of solution (specifically for cars) for some time, and this is the only sight that has helped. I would like a sample of the "cars.mdb" please so I can see exactly how things are working if possible though..

If you can, I would appreciate it.

Thanks again for the great solution.


 
Adrian Correa   24 Mar 11 - 12:24 AM

Hi Mike! -- You've saved my life! Thanks!
Just 2 comments:
1- LINQ: I tried to use it with LINQ "tables" and it just failed. - Dur to I could use SQLdatasources in the program, I used them and... GREAT!
2- Insert Mode: The cascading logic worked just fine, even without changing any codebehind. I copy/pasted the ddl's and datasources from one template to the other. - But it is not saving the dependant value in the database. - The "BIND" is not working. Maybe because I have the same names in both Templates. - I'm gonna change them and let you know.

- And I must also say: yours is the only solution that worked for me!


 
Adrian Correa   24 Mar 11 - 1:18 AM

Hi again...
I found what was happening.
It didn't save the dependant ddl selectedvalue in the database because it was required to put some code in the Formview_ItemInserting method. :)

Sorry, I didn't see it in your code. :)

Thanks again!