Everyone recognizes the problem with dropdownlists which are dependant on eachother. We select something from dropdownlist A and then dropdownlist B can be filled with items based on your selection in A. Simple to build with an automatic postback and event handlers on the controls, but nobody likes the page refresh. So time to get our hands on jQuery and JSON.
Let’s suppose we have an application page in which we have two dropdownlist:
<wssawc:DVDropDownList ID=”dropdownlistA” runat=”server”></wssawc:DVDropDownList>
<wssawc:DVDropDownList ID=”dropdownlistB” runat=”server”></wssawc:DVDropDownList>
With a simple jQuery script, we can hook up the two dropdownlists, so that a change in dropdownlistA triggers the loading of items in dropdownlistB without refreshing the page. We use the jQuery selector, select[id$='dropdownlistA'], because we know that the above tag is rendered by SharePoint as a select control. The ID however is prepended with a unique part, so we let jQuery search for select controls where the ID ends on our ID. Always use the complete ID you specify in the design file to avoid multiple results in jQuery select statements.
<script type=“text/javascript”>
$(document).ready(function() {
$(“select[id$='dropdownlistA']“).change(function() {
//clear previous items from dropdownlist
$(“select[id$='dropdownlistB']“).html(“”);
//select value from master dropdownlist
var ddlASelectedValue = $(“select[id$='dropdownlistA'] > option[selected]“).attr(“value”);
if (ddlASelectedValue != ”) {
//if mastervalue not empty, select items for slave dropdownlist
$.getJSON(‘ValuesDdlB.ashx?SelectedValue=’ + ddlASelectedValue, function(valuesB) {
$.each(valuesB, function() {
//add every item to slave dropdownlist
$(“select[id$='dropdownlistB']“).append(
$(“<option></option>”).val(this['ID']).html(this['Name']));
});
});
}
});
});
</script>
We have to create an .ashx file (Generic Handler), which is called by jQuery like in the script above (getJSON call). Right click the project in Visual Studio and select ‘Add’, ‘New item …’. If we have the option to uncheck ‘Place code in a separate file’, we choose the Generic Handler as template. Otherwise take a Text File or Code File and override the extension to ashx. In SharePoint we can only use the ashx files without a separate code file behind. First add a @WebHandler directive and a class definition that implements the IHttpHandler interface. Next we have to implement the IsReusable and the ProcessRequest method. To reference external dll’s, we can use the @Assembly directive.
Imports System.Web
Imports System.Web.Services
Public Class ValuesDllB
Implements System.Web.IHttpHandler
Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim pattern As String = “”"Name”":”"{0}”",”"ID”":”"{1}”"”
Dim sbValues As StringBuilder = New StringBuilder()
Dim values() As String
If (context.Request.QueryString(“SelectedValue”) = “MasterValue1″) Then
values = New String() {“subvalue1mastervalue1″, “subvalue2mastervalue1″}
Else
values = New String() {“subvalue1mastervalue2″, “subvalue2mastervalue2″}
End If
Dim i As Integer = 0
For Each value As String In values
sbValues.Append(“{“ & String.Format(pattern, value, i) & “},”)
i += 1
Next
context.Response.ContentType = “application/json”
context.Response.ContentEncoding = Encoding.UTF8
context.Response.Write(“[" + sbValues.ToString().Substring(0, sbValues.Length - 1) + "]“)
context.Response.End()
End Sub
ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
The above file has to be deployed in the \LAYOUTS\ folder, like we would do with custom application pages. Try to use a subfolder to avoid file name conflicts across multiple solutions.
Ready we are, time to test it!
Update 20 august 2009: Since most advantages also have disadvantages, I have to tell you that there is also one here. The second dropdownlist is provisioned by jQuery and you will notice that in code, the SelectedIndex, SelectedItem and SelectedValue are not set. So time for some other jQuery. First add a textbox under the dropdownlists and a div tag with display style set to none (to hide it).
<div style=”display:none;”>
<wssawc:InputFormTextBox ID=”selectedValueDdlB” runat=”server”>
</wssawc:InputFormTextBox>
</div>
Next add the jQuery script to catch a change in dropdownlist B and set the textbox field.
$(“select[id$='dropdownlistB']“).change(function() {
var pid = $(“select[id$='dropdownlistB'] > option[selected]“).attr(“value”);
if (pid != ”) {
$(“input[id$='selectedValueDdlB']“).val(pid);
}
});
In code you can read out the value of this textbox.