Friday, February 13, 2009

MOSS Cascading DropDownLists - The Sexy Approach - 2 of 2

Disclaimer reiterated: Do NOT copy/paste all of this code and deploy straight to a production environment. I cannot stress this enough.

Link to previous post: MOSS Cascading DropDownLists - The Sexy Approach - 1 of 2

Welcome to the second half of my discussion about AJAX Cascading DropDownLists within SharePoint 2007. I apologize for not getting this posted earlier, but I've been swamped at work lately. There is a lot of code involved, and I would like to thank the following people for various bits and pieces which I borrowed (links to my code are at the bottom of the post):


As the manifest.xml and wsp_structure.ddf files are pretty straightforward if you've worked with SharePoint Solution packages before, I will not discuss these.

Here is a great walkthrough on the different elements required in creating a custom field type on MSDN: Walkthrough: Creating a Custom Field Type.

We'll start with the creation of the field type definition: fldtypes_cascadingdropdownlist.xml. This file is used to store the various configuration information related to your custom field type. Multiple field types can be defined within the same file, however, this is not advised. All of the fields at the top are standard custom field type definition attributes.
  • TypeName is the internal name for this field type
  • TypeDisplayName is what will be displayed on the "Create a new column" page
  • FieldTypeClass is where you reference the assembly containing your field type class
  • FieldEditorUserControl is the location of the ASCX file which defines the user interface for Custom Properties
  • PropertySchema is used to define custom properties.
    All of my custom properties are simple TEXT fields, which have fixed length and are hidden. This is so I can use other controls (like DropDownLists) for the creation of the column, to make selecting values easier on the user. These custom properties are where I store which list supplies the data and how it is displayed/filtered.
  • RenderPattern is not used in my solution.

The class and assembly referenced in the attribute FieldTypeClass is used as the base class for storing/retrieving custom and default properties for this field type. Please see the comments in CascadingDropDownList.cs for a more detailed breakdown of functionality. I have opted to use the ThreadData method for storing values on initial column creation as mentioned previously in the MSDN article by Wouter van Vugt.

In CascadingDropDownList.cs near the bottom, I am using the custom class CascadingDropDownListEditControl as the RenderingControl. The user control for this is incredibly simple: CascadingDropDownListEditControl.ascx. It is just a normal DropDownList inside of a SharePoint RenderingTemplate. The class file associated with this ASCX file is CascadingDropDownListEditControl.cs. There are a few key components of this class to note:
  • ddlCascading.ContextKey is used to pass the necessary information to access a SharePoint list to the Web Service which will be populating the DropDownLists on the client side.
  • ddlCascading.ParentControlID has to be set at runtime, as this is the ID of the control on the page, not the value stored within the column's properties.
  • Render method is overridden to circumvent the necessity of disabling EventValidation for the CascadingDropDown extender to function. This method obtains all possible values from the corresponding SharePoint lists, and registers them for event validation on the client side (potential performance hit if you have significantly large value lists).
  • ddlCascading.ServicePath is the location for the web service to pass the AJAX request to and ddlCascading.ServiceMethod is the method invoked within the given Web Service.

This web service is defined within the files CascadingWebService.asmx and CascadingWebService.cs. The ASMX file is very simple, with just a reference to the assembly containing the web service class. Within the class, there are two functions: GetListItemsPageMethod and SortNameValuePair. The name for GetListItemsPageMethod can vary as long as it is the same here as it is in CascadingDropDownListEditControl.cs for ddlCascading.ServiceMethod. However, the *signature* for this method must be exactly as displayed. The contextKey at the end is optional (if you don't have to supply additional information for the AJAX request), but the rest of the parameters, as well as the security modifier and return type must be the same as I've used. SortNameValuePair, on the other hand, is a custom function I created to allow for sorting the collection of CascadingDropDownNameValues, since List.Sort does not know how the name-value pairs should be compared.

And the final component to this solution is the Properties class for the Cascading DropDownList. The Properties class is used for defining the way in which custom list properties (specified within the field type definition fldtypes_cascadingdropdownlist.xml) are displayed on the column creation/modification page. Again, please reference the comments within CascadingDropDownListPropControl.cs for a description of what is happening. It displays and modifies the custom column properties (which are defined as TEXT elements) using a Text Box, a Button, and several DropDownLists. CascadingDropDownListPropControl.ascx is a little more involved than the other ASCX files, but much of the HTML was simply to ensure that the properties for this custom field control looked like the properties for out-of-the-box field controls.

For the Name/Value columns, the source SharePoint list needs to be established as follows:
  • For each Cascading DropDownList you plan to have, you need a column to represent its potential values.
  • Be sure to enter all possible values for Parent, Child, Grandchild, etc within each list item
  • Here is a picture of my sample list: Bird Classifications

I tried to supply a lot of code, with the hope that it would be self-explanatory. If you need further explanation of what is happening, or why I did it this way, please do not hesitate to comment below. Here are some pictures of a sample implementation for Bird Sightings, using 3 Cascading DropDownLists (Family[Parent] -> Genus[Child] -> Species[Grandchild]):

Limitations:
  • The Cascading DropDownList controls must be displayed in order on the page, in the order you want their relationship to be (Parent before Child, and Child before Grandchild, etc.) If the Child is before the Parent, then the Child control will not be able to find the Parent control on the page.
  • It is possible to modify the Parent field of a Cascading DropDownList to a control which appears lower on the form page. The filtering of values in the "Parent Column" property only removes the current field.
  • If you have a Parent->Child->Grandchild relationship, and different Parent values have the same Child values in the source SharePoint list, then all of the potential Grandchildren for each Child will be visible if that Child is selected, regardless of which value is entered into Parent.

This control has already received widespread use within my organization. If you have any recommendations on improvement to remove these limitations, I would love to hear about the modifications you have made to the below code. I hope you enjoyed this deep-dive into employing the AjaxControlToolkit's CascadingDropDown within SharePoint using a Custom Field Control.

Enjoy!
--andrew

Links to solution files:

14 comments:

  1. Hi Andrew. I'd like to thank you for your hard work on these solutions. They were very instructive. I've managed to get them working after a bit of tinkering. I was wondering whether you've done any development to allow the CascadingDropDownList field type to work with document library views? Thanks again.

    Regards,
    Matt

    ReplyDelete
  2. Matt,
    I'm glad to hear you were able to get it to work. I hope it didn't involve too much extra work.

    As far as document library views, you should be able to display and use the columns for filtering, sorting, and grouping just like any of the default column types. Was there a feature in particular you were attempting but unable to do with a CascadingDropDownList column?

    It is a known deficiency that in "Datasheet View" custom column types are read-only.

    ReplyDelete
  3. How about posting a wsp file for us of the non-coding type :)

    ReplyDelete
  4. Anonymous,
    My apologies, I had intended this more as an exercise in programming and showing how to mesh the technologies rather than a production-ready solution. Unfortunately, I won't have the time to package this up in a WSP for distribution.

    ReplyDelete
  5. Could you help me understand why I'd be getting the following error on the CascadingWebService.asmx file in VS:

    "Could not load file or assembly 'Companyname.Intranet.FieldControls.CascadingDropDownList, Version=1.0.0.0, Cutlure=neutral, PublicKeyToken=9f4da0011c638ec5' or one of its dependencies. The system cannot find the file specified."

    If you could point me in the right direction I'd really appreciate it.

    ReplyDelete
  6. I finally got this working after qutie a bit of work. The is just an awesome tool. I do have one question though. I am unable to get it to work in a custom list form. I just get an error when the customer form page loads. Is this possible?

    ReplyDelete
  7. Anonymous,
    You receive this message within Visual Studio since the DLL isn't installed on your local system. I am not sure of the best method for debugging web services, but with the code supplied, I placed the ASMX file here (12 Hive\ISAPI\CompanyName), and the assembly "Companyname.Intranet.FieldControls.CascadingDropDownList.dll" into the GAC.

    Once you install it on the server in this manner (mine was done automatically via manifest.xml and wsp_structure.ddf), you can test it by going to http://servername:port/_vti_bin/CompanyName/CascadingWebService.asmx. (_vti_bin corresponds to ISAPI on the file system).

    Good luck!

    ReplyDelete
  8. Paul,
    I was able to reproduce this problem. I had not tested it in a custom list form, but you are correct that it does not function properly. If I set the default ListFormWebPart on the page to Hidden, then I get a general error message. If I then uncheck the Hidden box for the ListFormWebPart, the page loads up properly, but with both web parts for list input.

    I will need to do some extensive testing and development in order to determine if this problem is correctable. In the interim, I know the JavaScript method I wrote about previously for customizing input forms works with the CascadingDropDownList field control.

    Sorry I was not yet able to determine the cause of this error.

    ReplyDelete
  9. Hi Andrew

    thanks for the post..i was able to create the service and field and field controls..

    Created 2 list as in ur eg.
    Added the cascading dropdownlist field to ,say, list2.I was able to add in the list,parent column,filter.
    But when I click New ->list item in list2, I get unexpected error.Not clues in log file and debug mode.
    Can u thru some light?

    ReplyDelete
  10. Hi Andrew,
    It was an Ajax error: Unable to cast object of type 'System.Web.Configuration.ScriptingScriptResourceHandlerSection' to type 'System.Web.Configuration.ScriptingScriptResourceHandlerSection'.

    Found help here:
    http://dsen-25.blogspot.com/2009/06/unable-to-cast-object-of-type.html

    Now I am able to create the list item.But For one field am getting 2 dropdown list side by side.te second one is filled with the correct data.Am I missing something??

    Thanks

    ReplyDelete
  11. Hi Andrew

    The 2 dropdownlist for the same field and the loading of the child fields got rectified as soon as I removed CascadingDropDownListControl rendering template entry.

    looks like wen i installed ajaxcontroltoolkit,it got copied.

    Thanks again.

    ReplyDelete
  12. Anonymous,
    I apologize for not responding sooner, but I'm glad to see you got it working!

    Thanks for the link you supplied; it is very useful if you had an old version of the Ajax toolkit installed.

    ReplyDelete
  13. Hi
    The solution works fine for me with a slight problem which is kinda annoying..
    the field properties are not saved the first time-
    -I create a field of type Cascading, set it's properties and click "Apply" then I need to re-enter to the field properties and set them all over again(and it's working like charm after this)

    Anyone has a clue how to solve it ?

    Thanks.

    ReplyDelete
  14. Anonymous,
    I remember running into this issue myself as I was working through getting this to work the first time. The DataCogs post at the top referenced another site by Aaron Robertson-Hodders who details the troubles of assigning custom properties to SharePoint fields during the Create and Edit phases of column modification.

    You can find the posts here: http://vspug.com/aaronrh/2007/05/

    ReplyDelete