Introduction
Many of you may have seen or even used the accordion control that comes with the Atlas Control Toolkit. This custom server control can be data bound, and provides similar functionality, although with several differences.
Background
I wrote this control primarily because I needed the functionality of an accordion to conserve space in the search area of a web application that I recently finished building for a client. Initially, I had been attempting to use both the accordion and a group of collapsible panels that are available with the Atlas Control Toolkit. While these are both excellent controls, neither really suited my needs without substantial customization. As a result, I decided to take the time and write a server control that would meet my needs. My requirements dictated that the accordion be able to maintain its selection state (to allow the user to go back to the page and revise their search). The accordion also had to contain checkboxes that could quickly all be selected or deselected, and that could be individually selected or deselected. There was also the requirement that if a pane in an accordion was expanded, all other open panes with no checked checkboxes be closed. Again, the purpose was to conserve space. Additionally, the users felt that the accordion should show them the number of selections that they had made in a particular panel.
Caveat
This control was written with a specific purpose in mind, and probably would not be reusable as it currently is. I intend to take the time to eventually make it reasonably generic. Possibly add templates to the content area, etc. The purpose of this article is really to provide an example of how to implement a relatively complex composite data-bound control.
Using the code
The basic idea behind the accordion is that it can bind to a data-source like any other data-bound control. What makes it slightly different, however, is that it actually binds to a collection of collections. Each pane within the accordion binds to an individual collection. The header for each pane corresponds to a property on the collection. For example, if the pane were binding to a DataTable
, the header text might correspond to the TableName
property. If the pane were binding to a collection, it would be necessary to create a custom collection that exposes the property that you would like to bind to the header. For example:
public class NamedList<T> : List<T>
{
private readonly string m_strListTitle;
public NamedList(string _strListTitle)
{
m_strListTitle = _strListTitle;
}
public string ListTitle
{
get
{
return m_strListTitle;
}
}
}
Here, you could bind the accordion pane on the ListTitle
property, and that is the text that would appear on the header.
The accordion control is relatively simple to use: just drop it onto your page, and either set its properties via the designer, or declaratively. See the sample project included for an example. The accordion control can be data bound to any data-source that implements IEnumerable
or IListSource
. Note that currently it will not bind properly to a DataSet
. If you need to bind to DataTable
s, instead of adding them to a DataSet
, add them to an IList<DataTable>
. At some point, I will add support for DataSet
s.
Points of interest
Maintaining the expanded/collapsed state of each pane provided a bit of a challenge, but once I realized the basic technique of using hidden input fields and combining that with ensuring that the accordion pane objects implement IPostBackDataHandler
, it was relatively simple to implement.
As another point of interest, I hadn't really had much of a background in injecting JavaScript. This control makes heavy use of this technique, and it really amazes me what you can achieve with JavaScript.
History