Skip navigation.

The Economy of NichesAll recent postsWizard Control and Browser Back Button

Building a Composite Databound Control the 2.0 Way

ASP.NET 2.0 shipped with a new server control, CompositeDataBoundControl, which considerably cuts down on the amount of code you have to write by hand. Since its documentation is a little out of whack, I decided to point out a couple of things.

The old way

Back in them ol’ dark days of ASP.NET 1.x, you had to write quite a lot of plumbing code to build a databound server control. Scott Mitchell has a thorough article, Building DataBound Templated Custom ASP.NET Server Controls, which explains this process inside out. The technique is also covered at great lengths in Nikhil’s book.

The new way

CompositeDataBoundControl alleviates some of the pain by implementing boring plumbing. In a fairly simple control all you have to do is override CreateChildControls, as the sample code at MSDN demonstrates.

I believe one thing it misses is that you need to pay attention to the dataBinding parameter. It is true when you call DataBind() and false when the control is being rebuilt from view state on postback. Therefore the while (e.MoveNext()) { ... } loop needs to populate a row only when dataBinding is true.

I put together a sample control that builds a list of books in an unordered list (<ul>). The calling code sets the control’s DataSource property to a list of books and calls DataBind(). In this scenario, CreateChildControls will hold this book list in its dataSource parameter, and dataBinding is true.

It is important to return the correct number of items you added as it’s stored in view state, as Reflector reveals:

protected internal 
      override void PerformDataBinding (IEnumerable data)
{
  …
  int num1 = this.CreateChildControls(data, true);
  base.ChildControlsCreated = true;
  this.ViewState["_!ItemCount"] = num1;
}

On postback, the familiar paremeterless CreateChildControls() kicks off another round of binding, this time with a fake data data source and dataBinding set to false:

protected internal override void CreateChildControls()
{
  this.Controls.Clear();
  object obj1 = this.ViewState["_!ItemCount"];
  …
  object[] objArray1 = new object[(int) obj1];
  this.CreateChildControls(objArray1, false);
  base.ClearChildViewState();
}

Order of control population matters

Here’s a bit of view state trivia. What’s the difference between the following code snippets:

// Original
if (dataSource != null)
{
  CheckBoxList bookList = new CheckBoxList ();
  Controls.Add (bookList);

  IEnumerator e = dataSource.GetEnumerator ();
  while (e.MoveNext ()) { … }
}

// Modified

if (dataSource != null)
{
  CheckBoxList bookList = new CheckBoxList ();

  IEnumerator e = dataSource.GetEnumerator ();
  while (e.MoveNext ()) { … }

  Controls.Add (bookList);
}

Basically, the second sample adds the fully populated book list at the end, once looping is done.

The difference is huge

The second sample will lose list items on postback because there’s nothing for them in view state so they can’t be rebuilt.

Again, Nikhil explained the “why” is his book (“View Sate and Child Controls”, page 310). You can also read Scott’s explanation of this nuance. When you come back, it should be clear why Controls.Add (bookList) appears at the top.

Comments

Comment permalink 1 Smv |
Hi, first of all thank you for the nice post. I know it's very old but since I found it useful and (maybe) discovered a problem in it, I'd like to point it out.
I've played a little with your sample code (running on Mono) ad noticed that the same problem described in 'Order of control population matters' is present inside the while loop:

while (e.MoveNext ())
{
ListItem bookItem = new ListItem ();

/* If the control is rebuilding itself with data from
* view state, dataBinding is false and dataSource contains an array
* of null elements. */
if (dataBinding)
{
Book book = (Book) e.Current;
bookItem.Text = string.Format ("{0}: {1:C}", book.Title, book.Price);
}

bookList.Items.Add (bookItem);
itemCount++;
}

Should be:

while (e.MoveNext ())
{
ListItem bookItem = new ListItem ();
// Add bookItem BEFORE changing its properties.
bookList.Items.Add (bookItem);

/* If the control is rebuilding itself with data from
* view state, dataBinding is false and dataSource contains an array
* of null elements. */
if (dataBinding)
{
Book book = (Book) e.Current;
bookItem.Text = string.Format ("{0}: {1:C}", book.Title, book.Price);
}
itemCount++;
}
Otherwise the first bookItem properties are not preserverd in the ViewState.
You will notice this at the second PostBack of the page.
Maybe it's just a Mono problem, I haven't tried with MS .NET yet.
Bye
Comment permalink 2 Milan Negovan |
I'd be curious to know if this reversal is a Mono issue. My sample code came from a real-world app (it wasn't books, but still) and I know it worked.

If you find something, please share. :)

Emails and Notifications

Would you like to be notified when somebody responds to this post?  Would you like to have these comments emailed to you?

TrackBacks

Sorry, TrackBacks are not allowed.

Submit your comment

Please enter only text since all HTML tags except hyperlinks will be stripped. Hyperlinks will become live links. Any comments with flaming or offensive language will be deleted. Be courteous to other posters. Thank you.

Your name (required):
Your email (optional):
Your site's URL (optional):
Enter this number
Type in the number above:
Comment (required):