Spontaneous Publicity
blogs are the new phone book

Child Collections in Asp.Net Custom Controls

May 23, 2007 09:47 by Luke

I have been developing custom web controls for many years now and I am just now getting comfortable with many of the advanced features afforded by the rich System.Web.UI.WebControls namespace. One feature I have implemented many different times over the years is having a web control which contains a list of sub items. For example, the Asp.Net GridView's collection of Columns or the DropDownList's collection of ListItems.

<asp:GridView ID="grid" runat="server"> 
    <Columns> 
        <asp:BoundField /> 
        <asp:ButtonField /> 
    </Columns> 
</asp:GridView>

There is so many things going on it a control like this that just seem like magic. How are the child items persisted in ViewState? How does the control know how to parse the child items? What if the whole world were a power ranger?

StateManagedItem

I have created a couple of classes that really simplify the process of creating lists of child items. The first of which is the class StateManagedItem. This class is the base class for all the objects which you wish to use as "Child Items" (like grid view columns or list items).

In order to manage state in Asp.Net, you have to implement the IStateManager interface. This interface allows objects to participate in the process of saving and retrieving things from ViewState. In order to generalize the whole process, I needed on other method to set an item as "Dirty" so I created the following interface:

public interface IStateManagedItem : IStateManager 
{ 
    void SetDirty(); 
}


So now we have the base interface for all state managed items. So here is the what the base class definition looks like:

public abstract class StateManagedItem : IStateManagedItem

This class provides it's own ViewState for items to save their state in which essentially allows "Child Items" to save their state just like custom web controls do. To illustrate this concept, lets create an example class called Appointment which will hold information about a calendar appointment.

public class Appointment : StateManagedItem

The definition for this class is very simple. Here is how the properties are implemented:

public DateTime? Start 
{ 
    get 
    { 
        object o = ViewState["Start"]; 
        return o == null ? null : (DateTime?)o; 
    } 
    set { ViewState["Start"] = value; } 
}

If you are familiar with using ViewState to store web control properties, this syntax should look fairly familiar. That that is all there is to our child item.

StateManagedCollection

The next helper class is a base class for the collection of items. In our example, we need a collection of Appointments. For this purpose, I have created a StateManagedCollection<T> class which can be used to hold the child items.

public abstract class StateManagedCollection<T> : IList<T>, ICollection<T>, IEnumerable<T>, IStateManager, 
    IList, ICollection 
    where T : class, IStateManagedItem, new()

If you are not familiar with generics and generic constraints, this is basically a list class which requires the child items to be a class (not a struct), derive from IStateManagedItem, and to have a default constructor.

So to create our list of Appointments, all we need to do is the following:

public class AppointmentCollection : StateManagedCollection<Appointment> 
{ 
}

That is it as far as the AppointmentCollection class goes. It doesn't require any more code.

Sample Control

So lets finish the example by creating a custom control which displays a list of Appointments. First we will derive from Composite Control:

[ParseChildren(true), PersistChildren(false)] 
public class AppointmentControl : CompositeControl

Note the ParseChildren and PersistChildren attributes, these are required to tell Asp.Net how to parse the markup inbetween the start and end tags of your control. This is part of the "Magic" I mentioned earlier.

Next, make a member of the class which is an AppointmentCollection:

private AppointmentCollection appointments = null;


And a public property to access that collection:

[ 
    Mergable(false), 
    PersistenceMode(PersistenceMode.InnerProperty), 
] 
public AppointmentCollection Appointments 
{ 
    get 
    { 
        if (appointments == null) 
        { 
            appointments = new AppointmentCollection(); 
            if (base.IsTrackingViewState) 
            { 
                appointments.TrackViewState(); 
            } 
        } 
        return appointments; 
    } 
}

This property also must have a couple of attributes - Mergable and PersistenceMode. These basically tell Asp.Net and Visual Studio how to handle and persist this property.

To use this control, first place the proper includ directive on your page:

<%@ Register TagPrefix="web" Namespace="Web.Example.Appointment" %>

Now we can declare our control markup as:

<web:AppointmentControl id="appointmentList" runat="server"> 
    <Appointments> 
        <web:Appointment Start="1/6/2007 8:00 AM" End="1/6/2007 11:30 AM" Description="Breakfast" /> 
        <web:Appointment Start="5/12/2007 9:30 AM" End="5/12/2007 9:45 AM" Description="Meeting" /> 
        <web:Appointment Start="5/12/2007 10:00 AM" End="5/12/2007 10:30 AM" Description="Break" /> 
    </Appointments> 
</web:AppointmentControl>

I won't go into the render code (you can download the source code to see all the gory details) but here is how the control renders:

You have 3 appointment(s)

  • Breakfast - (01/06/2007 08:00:00 - 01/06/2007 11:30:00)
  • Meeting - (05/12/2007 09:30:00 - 05/12/2007 09:45:00)
  • Break - (05/12/2007 10:00:00 - 05/12/2007 10:30:00)

You can download the full source code here. Hope this helps dispell some of the "Magic" of Asp.Net controls.

kick it on DotNetKicks.com

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:
Categories: .Net | C# | Development
Actions: E-mail | Permalink | Comments (10) | Comment RSSRSS comment feed

Related posts

Comments

June 4. 2007 12:41

Will


Hi Luke, good work! Are you looking for a new job? We're a local firm and hiring, let me know.

Thanks.
Will

Will

August 21. 2007 07:24

Ian Kulmatycki


Hi,

These base classes are a great idea. Well done.

ian

Ian Kulmatycki

October 11. 2007 01:17

Chris

I have been messing with the composite control examples online for days, sigh . . . Downloaded your code and bam done, simple elegant works like a charm, my composite control works great. Now I can sleep. Thanks

Chris

November 7. 2007 00:43

Steve

Just what I was looking for. There doesn't seem to be a lot of info on the net about how this is done, so thanks for this write-up!

Steve

November 29. 2007 01:01

naisioxerloro


Hi.
Good design, who make it?

naisioxerloro

December 9. 2007 00:46

[...] enums as well as some parsing methods similar to these. I have written about DelimitedList and the state managed classes before. I make heavy use of QueryStringHelper to build the query string which  represents the [...]

Asp.Net Control For Google Charts « Spontaneous Publicity

April 3. 2008 17:13

Nisar

hi guys.. need help here..

i want to use the Appointments statebag at later time, how would i do that?

actually, i have a button on my custom control appointmentControl class and when the user clicks on that i want to read all the appointments and this is what i did

foreach (Appointment appointment in Appointments)
{
//
}

but i'm getting Appointments count=0 ?
what exactly do i need to modify in order to get the items from viewstate?

any help is greatly appreciate.

thanks

Nisar

April 9. 2008 11:33

Nisar

after i add the following code in the .aspx page

<web:AppointmentControl id="appointmentList" runat="server">
<Appointments>
<web:Appointment Start="1/6/2007 8:00 AM" End="1/6/2007 11:30 AM" Description="Breakfast" />
<web:Appointment Start="5/12/2007 9:30 AM" End="5/12/2007 9:45 AM" Description="Meeting" />
<web:Appointment Start="5/12/2007 10:00 AM" End="5/12/2007 10:30 AM" Description="Break" />
</Appointments>
</web:AppointmentControl>

when i switch to design mode i get this error:

Appointments could not initialized. Details: 'Appointments' could not be added to the collection. Details: object does not match target type

any idea why this error pop-up ?

Nisar

April 25. 2008 00:19

aram

How can implements this with webcontrol such as checkbox?

Thanks in advance

aram

June 13. 2008 02:31

Zalak Shah

hi all ... it's a good idea

i have one more question .. what happen if i will use this control as custom control and i will add more then one control to same page .. how view state will manage the different view states for all the controls ???

Zalak Shah

Add comment


(Will show your Gravatar icon)  

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

October 6. 2008 08:15