Practical .NET

Integrating DataViews with Multiple Cascading DropDown Lists

Peter Vogel solves a reader's problem by integrating data from multiple dropdown lists, the FormView and some Ajax magic that calls code in a method in an ASPX page.

In a comment to a tip in our ".NET Tips and Tricks" blog, a reader noted that what he needed to was an "example of how to have a large number of combo/drop that are dependent on each other in a dataview" -- including dependencies that involved several dropdown lists rather than just one. So here's that example which incorporates some Ajax technology that lets you work with values from multiple dropdown lists at once.

Assume three cascading dropdown lists displayed in a FormView's EditItemTemplate, all tied to tables in the Northwind database. The user puts the DataView in Edit mode to access the dropdown lists. The first dropdown list displays a list of customers and the user selects a customer from that list. Initially, the second dropdown list is empty, but after the user selects a customer in the first list, this list displays the sales orders for the selected customer. When the user selects a sales order from this second list, the final dropdown list displays the selected order's details. The customer list controls the orders list; the orders list controls the details list.

The first dropdown list, showing all customers, can be filled at the server before sending the page to the browser. But the best solution for the other two dropdown list is some Ajax. This is surprisingly easy to do -- and you don't even have to write any JavaScript (though you must first download Microsoft's Ajax toolkit from the CodePlex site).

First, drag the CascadingDropDown extender from the Toolbox and drop it on each of the orders and details dropdown lists. You'll also need to add a ScriptManager control at the top of your page (otherwise the CascadingDropDowns won't work).

Next, set seven properties on each CascadingDropDown extender (you can find the property list for the extenders nestled in the property list for the dropdown list that you dropped them on). The first four properties just set messages to display to the user and tie the CascadingDropDown to the controlling dropdown list:

  • Prompt Text: A message that provides a hint to the user about what the user needs to do to get the dropdown list to display data (e.g. "[First, select a customer]")
  • Empty Text: A message to display if there is no matching data (e.g. "[No orders for this customer]")
  • Loading Text: A message to display while data is being fetched from the server (e.g. "Please wait…")
  • ParentId: The Id of the dropdown list that controls the data fetched by the CascadingDropDown extender (e.g. for the orders dropdown list, that's the customer dropdown list)

The next two properties tie the CascadingDropDowns to a method in the page's code file that will return the data that the lists need. The ServiceMethod property specifies the method for the CascadingDropDown to call to retrieve their data (e.g., if you're going to call that method GetCCDData, you'll need to put "GetCCDDatat" in the CascadingDropDowns' ServiceMethod property.)

To be able to distinguish between the two CascadingDropDowns, set each CascadingDropDown's ContextKey property to different values (for instance, set the Orders CascadingDropDown's Category property to "Orders," and the Order Details CascadingDropDown's Category property to "Order_Details"). The ContextKey property's value will be passed "as is" to your method.

Handling Multiple Dropdowns

The seventh property is the Category property; this starts to address the reader's issue with needing access to multiple dropdown list values. Set the Category property on both of the CascadingDropDown extenders to the same value; then when either of the CascadingDropDowns call your method, you'll be passed the current data from all controlling dropdown lists that have data selected. This means that when the Orders CascadingDropDown executes, CustomerId will be passed; when the Order_Details CascadingDropDown executes, both the CustomerId and the OrderId will be passed. These are passed in a string that can be parsed using a ParseKnownCategoryValuesString method that comes with the Ajax Toolkit.

The next step is to write the server-side code. Add that GetCCDData method to your page's code file and decorate it with the WebMethod attribute to call it from JavaScript (note: you must also use the parameter names shown in this code):

   <System.Web.Services.WebMethod()>
  Public Shared Function GetCCDData(
       knownCategoryValues As String,
       category As String,
       contextKey As String) As 
              CascadingDropDownNameValue()
 

ASP.NET will call this method every time the user selects a new value in a controlling dropdown list, and will pass all the relevant data automatically.

The first thing to do in that method is parse the incoming data that arrives in the knownPropertyValues parameter, to separate out the data from the various controlling dropdownlists:

 
Dim DropDownData As StringDictionary
  DropDownData = CascadingDropDown.
ParseKnownCategoryValuesString(
                       knownCategoryValues)
  Dim key As String

The method must return a CascadingDropDownNameValue object for each item to be displayed in the dropdown list that called it. Fortunately, creating a CascadingDropDownName value is easy -- just create it and then:

  • Set its name property to the value to be displayed in the dropdown list
  • Set its value property to some associated piece of data.

The sample code here takes care of generating those CascadingDropDownNameValue objects using LINQ and Entity Framework (because LINQ + EF is faster to write -- ADO.NET code would work as well).

 
<System.Web.Services.WebMethodAttribute()>
  <System.Web.Script.Services.ScriptMethodAttribute()>
  Public Shared Function GetCCDData(
       knownCategoryValues As String,
       category As String,
       contextKey As String) As 
CascadingDropDownNameValue()
    Dim oc As New northwndModel.northwndEntities
    Dim dat As System.Linq.IQueryable(Of
CascadingDropDownNameValue)
    dat = Nothing

    Dim DropDownData As StringDictionary
    DropDownData = CascadingDropDown.
ParseKnownCategoryValuesString(
knownCategoryValues)
    Dim key As String
        
    Select Case contextKey
      Case "Orders"
        key = DropDownData.Values(0)
        dat = From o In oc.Orders
              Where o.CustomerID = key
              Select New CascadingDropDownNameValue() 
                       With {
                     .name = o.OrderID,
                     .value = o.OrderID()
                    }
       Case "Order_Details"
         key = DropDownData.Values(1)
         dat = From od In oc.Order_Details
               Where od.OrderID = key
               Select New CascadingDropDownNameValue() 
                        With {
                      .name = od.Product.ProductName,
                      .value = od.ProductID
                     }
    End Select
    Return dat.ToArray

  

End Function

These tools begin to address some of the issues that my reader raised -- integrating multiple dropdown lists and, where necessary, gaining access to the value of all of them in a single method call.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

Subscribe on YouTube