Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

A Crash Course on Custom ASP.NET Data-bound Controls : Page 5

Data-bound controls require a data source property and a set of string properties that link to particular columns of the data source. In addition, they need an Items collection property to track all the building blocks of the control's user interface. Finally, a well-done data-bound control supports styles and custom events.


advertisement
The DataBind Method
Listing 2 shows the typical implementation of the DataBind method in a data-bound control. To start off, the method invokes itself on the base class. Next, it clears the collection of child controls and their ViewState. When the DataBind method is called, data-bound controls get fresh data and update their user interface. When this happens, it's a good and safe habit to clean up child controls and memory.

The control's ViewState is reset and an internal engine that tracks changes to the control's ViewState is turned on. (This is what TrackViewState really does.) Finally, the control builds the user interface.

The user interface of a typical composite control consists of a hierarchy of child controls. The method CreateControlHierarchy creates this hierarchy for the BarChart control. In ASP.NET 1.x, CreateControlHierarchy is not a framework method to override; however, most data-bound controls define it internally as a protected and virtual member. In this way, inherited controls can easily override it thus modifying the rendering of the control. Listing 3 shows the source code of the method.

By design, when CreateControlHierarchy returns all child controls have been created. This situation is notified to base classes through a writeable Boolean property—ChildControlsCreated. Defined on the Control class, the property just indicates whether the server control's child controls have been created.

Building the Control's User Interface
As Listing 3 shows, the CreateControlHierarchy method accepts a Boolean argument that the DataBind method sets to true. What's the role of this argument? Some hints come through the formal name of the parameter—useDataSource.

CreateControlHierarchy gets an enumerable collection of data and then starts building the BarChart's hierarchy of child controls. The BarChart control consists of an outermost table with a bunch of rows. The first row contains the title of the chart; the second row shows the subtitle. These items are not data-bound meaning that the rendering of these components is not influenced by the data source contents. The following code snippet demonstrates the creation of the title row. The row contains a single cell that spans over the number of columns. The BarChart control contains two columns—label and value.

private void CreateTitle(Table t) { // Create the table row BarChartItem item = new BarChartItem( BarChartItemType.Title); t.Rows.Add(item); TableCell cell = new TableCell(); cell.ColumnSpan = BarChart.ColumnsCount; cell.Text = Title; // // Fire HERE the ItemCreated event item.Cells.Add(cell); }

Next, the CreateControlHierarchy method creates all data-bound items and completes the structure with the footer row. Listing 4 details the creation of all data-bound items.

The Boolean argument originally passed to CreateControlHierarchy is forwarded to a couple of internal methods—GetDisplayData first and CreateBarChartItem next. The Boolean argument indicates how to build the control's infrastructure—from the data source or the ViewState. In other words, if the parameter useDataSource is true, the DataSource property is set to a non-null value; otherwise, the data source is empty and the BarChart control must rebuild itself from the ViewState.

Before going any further with more details, I need to bear down on the reasons that determine the value of the useDataSource parameter. When a data-bound control is first populated—that is, a call to DataBind is made—the parameter is set to true and a valid data source object is available.

When the host page posts back, the control needs to redraw itself but the data source may not be there. If the sender of the postback is the control itself, you handle one of its events and rebind fresh data. But what if the sender of the postback is another control in the page?

To minimize the workload, the control should not be re-bound to data if the data to display hasn't changed. So how can the control refresh?

The page ViewState doesn't contain the whole data source as a separate object, but each and every constituent control saved its visual properties to the ViewState. To make the BarChart data-bound control survive a postback, you must write its rendering code so that the phase of creation of constituent controls is separated from the data binding phase. In this way, the structure is correctly recreated after each postback and the binding takes place only if necessary. In addition, each constituent control—table cells, labels, textboxes—will automatically restore themselves from the ViewState thus recreating the overall interface. Let me illustrate this with an example.

You know from Figure 1 that the BarChart control is made of a collection of rows. Each row has two cells, the second of which is generated by the following code.

Label lblGraph = new Label(); Label lblText = new Label(); cell.Controls.Add(lblGraph); cell.Controls.Add(new LiteralControl("<br>")); cell.Controls.Add(lblText);

The horizontal bar is rendered through a Label control whose Width property is set to a percentage that represents the associated value. The Width property is assigned at binding time:



Label lblGraph = (Label) cell.Controls[0]; object o = DataBinder.Eval(dataItem, DataValueField); float val = Convert.ToSingle(o); float perc = 100*val/Maximum; lblGraph.Width =Unit.Percentage(perc);

When the page with the BarChart control posts back, what really matters is that the Label control be recreated in the same place. The Label control will then restore itself from the ViewState and maintains the assigned width.

The number of bars depends on the bound data source. When there's no data source—that is, after a postback—how can you determine the correct number of bars that need to be displayed? By design, this information is known only at binding time and to access it later you must save it somewhere. Where? In the ViewState.

if (useDataSource) ViewState[ViewStateItemCount] = itemCount;

This trick is widely used in most ASP.NET built-in controls (e.g., DataGrid) and sets a clear dependency between these controls and the ViewState. For the trick to work, though, it is essential that you add each newly created control to its parent in the hierarchy as soon as possible—ideally, right after instantiation.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap