...
    View Article        

Current Articles | Categories | Search | Syndication

Page: 1 of 7
Previous Page | Next Page
posted @ Monday, May 12, 2008 6:03 AM by SkySigal
How DataBinding used to be done before ASP.NET 2.0

Before we can look at the simplified way of making DataBindable controls in ASP.NET we should look back at how it was done in ASP.NET 1.1, and what the MS architects were trying to improve on.

 

The Three key rendering methods of an IEnumerable DataBound WebControl

Traditionally, the Lifecycle of a CompositeControl, that is not DataBindable, will ensure that the rendering process of child controls starts at EnsureChildControls(), which in turn calls CreateChildControls(), which will do the work of instantiating the ChildControls and adding it to the control.
In other words, the code flow is:

  • EnsureControls ()
    • CreateChildControls ()

In IEnumerable DataBound controls, where the Templated rows are ChildControls, we change things a bit, and move the core of the child-creating code from CreateChildControls() to a private method that by convention only is called CreateControlHierarchy().

So in an IEnumerable DataBound Control, the code flow is now as follows:

  • EnsureControls ()
    • CreateChildControls ()
      • CreateControlHierarchy () 


Why do we move the code from CreateChildControls to another method?
We move the code because we actually need to create the rows using different logic, depending on whether the request is a First Request, and the user is requesting DataBind to be performed, or a PostBack, where we want the framework to recreate the rows from ViewState, and not from the original Data. 

In other words, CreateControlHierarchy() is used by both CreateChildControls(), and DataBind() -- it just gets called with a different bool flag to indicate which type of logic to apply:

  • EnsureControls()
    • CreateChildControls()
      • CreateControlHierarchy(false)
  • DataBind()
    • CreateControlHierarchy(true)
 
Modifying the CreateChildControls method

Fixing up the CreateChildControls to reroute to CreateControlHierarchy is easy enough -- its just a cut/paste job to move to the CreateControlHierarchy method all the statements that create new child controls and add them to this control.

Just don't forget that we are calling CreateControlHierarchy with the flag=false:

protected override void CreateChildControls() {
    //Always clear first (the reason why makes sense when called later by DataBind())
    base.Controls.Clear();
    //Note that we don't ClearChildViewState() or TrackViewState()
    //Call CreateControlHierarchy -- with no dataBinding.
    CreateControlHierarchy(false);//Notice the flag=false!!!
    //base.ClearChildState();
    //Set the flag so that CreateChildControls doesn't get called
    //again, unless by DataBind() -- which is the only exception.
    ChildControlsCreated = true;
}

Note:
We'll cover the contents of CreateControlHierarchy() method in just a second.

Modifying the DataBind method

The other way of getting to CreateControlHierarchy(), namely the DataBind() method, is not very difficult to modify either -- we're just ensuring that we are clearing out everything (and I mean everything) before we redraw the controls by calling CreateControlHierarchy, this time with the argument flag set to true:

public void DataBind(){
    base.OnDataBinding(EventArgs.Empty);
    // Reset the control's state.
    Controls.Clear();
    ClearChildViewState();
    // Create the control hierarchy using the data source.
    CreateControlHierarchy(true);
    //ViewState from here on in:
    TrackViewState();
    //Mark that we are done:
    ChildControlsCreated = true;
}

Notice that the above DataBind() code accomplishes several things:

  1. Clear the Controls collection.
  2. Clear the ViewState of the child controls (no use having ViewState for controls that don't exist!).
  3. Create the child controls using the data source.
  4. Ensure ViewState tracking is on.
Writing the CreateChildHierarchy Method to take into account both approaches

It's the CreateChildHierarchy() method where we have to pay attention, as it is the control that is doing the heavy work now -- the CreateChildControls and DataBind have been demoted to being nothing more than wrappers to it, with only a bool flag's difference between the two.

void CreateControlHierarchy(bool useDataSource){
  if (useDataSource){
  ...
  }else{
  ...
  }
}

 

The LifeCycle of an IEnumerable DataBindable ASP.NET 1.1 Control
Filling in each part is better explained by examining the control during a real run through.

 

First Request

On a first request, because it is a DataBound control, the web user will be setting its DataSource property and then invoking its DataBind method before the Control gets to the PreRender stage, and automatically triggers the call to EnsureChildControls.

DataBind then calls CreateControlHierarchy() with the bool argument set to true, who then iterates through the DataSource making a TemplateContainer+ItemTemplate for each dataItem:

void CreateControlHierarchy(bool useDataSource){ if (useDataSource){ int counter = 0;//Important counter... //ie: just been called by DataBind foreach (object dataItem in dataSource){ //For each dataItem, make a templateContainer + template, then bind it: //Make Container and add to Controls: ItemTemplateContainer container = new ItemTemplateContainer(dataItem); rowsPanel.Controls.Add(container);//Add to Page BEFORE InstantiateIn so TrackingViewState() called NOW. //Make Template and instantiate it with Container: this.ItemTemplate.InstantiateIn(container); //DataBind now: container.DataBind();//which triggers OnDataBinding of child controls within Template. counter++; } ViewState['RowsCreated']=counter; //Save this for PostBack. }else{
//We'll cover this part below in a second...
}
}

The creating of the TemplateContainer and the Template has been covered thoroughly by now (in another article) -- but do pay special attention that at the end of looping through the dataItems and creating templates for each one that we ViewState only the number of rows created, and do not persist the datasource in any way.  What is going on is that we are viewstating how many items to rehydrate, but leaving it up to the template items themselves to rehydrate themselves on postback (separation of work...).

Another point to notice is that the only thing that will be ViewStated is the actual appearance of the control -- not the DataSource. This makes crazy sense actually. Imagine DataBinding to a query of 1000 records, 10 cells. We only want to bind to one cell, and specifically the 10 rows of page 5. In other words, we only need to persist 50 cells of data -- and NOT 10000 cells.

On PostBack

On PostBack, the web programmer will probably not be calling DataBind() (and he shouldn't if the page is decently designed)-- so the page will follow its normal course of sooner (end of Init) or later (PreRender) calling EnsureChildControls, which will call CreateChildControls, which will call CreateControlHierarchy, except this time with the bool flag set to false:

 

 

void CreateControlHierarchy(bool useDataSource){ ...//We've already covered this part above... }else{ int rowsToRebuild = ViewState['RowsCreated']; //Make an IEnumerable dummy array of exactly the same size: ArrayList dummyDataSource = new ArrayList(rowsToRebuild); //Technically, you don't even need to make a dummy datasource, you //could very well simply for (int i=0;i<rowsToRebuild;i++) //and achieve the same thing, but ...trust me...do it this way... foreach (object dataItem in dataSource){ ItemTemplateContainer container =
new ItemTemplateContainer(dataItem); //Add to Page BEFORE InstantiateIn //so TrackingViewState() called NOW. rowsPanel.Controls.Add(container); //Make Template and instantiate it with Container: this.ItemTemplate.InstantiateIn(container); //Do NOT databind this time, //and let the framework Rehydrate it: //Do not Do this on postback!//container.DataBind(); } } }

 

What just happened? Magic!

We were able to throw away the DataSource after it was used on the first page load, and on postback we relied not on DataBinding, but on the ViewState rehydration technology to restore the LOOK of the data, not the data itself.

In essence, on first request, we didn't have any rows/frame, etc -- we only had a DataSource. You could say we had DATA, and no INTERFACE.
Once the rows were created and sent, we discarded the data, just viewstating the INTERFACE as ASP.NET does with just about all its controls.

Therefore, on Postback, we don't have any DATA left -- we only have INTERFACE.

This is pretty wild weird stuff, if coming from the older ASP or PHP model. But it did what we want.

powered by metaPost
Page: 1 of 7
Previous Page | Next Page

Comments

Currently, there are no comments. Be the first to post one!
Click here to post a comment

             
Copyright 2007 by Sky Sigal