Building a DataGrid Helper Control for ASP.NET 1.x: Part 3

Introduction

[Download Code]

In Part 1 and Part 2 of this series, we successfully built the DataGridHelper control that can be used to develop a data access page with sorting, paging, deleting, and updating capabilities without writing a single line of code.  I have had too much fun to stop now. In Part 3, we will address another limitation of DataGrid and DataGridHelper control--custom columns.

In ASP.NET 1.x, the easiest way to create custom column is by using the TemplateColumn. The TemplateColumn supports one-way databinding to display the data. The update code is not exactly pretty, as seen in a Microsoft QuickStart sample, because we have to find the control from the controls collection of the table cell and extract the value from the control. The up-coming ASP.NET 2.0 will support two-way binding in GridView templates.  For ASP.NET 1.x, Marcie Robillard’s MSDN article discussed an excellent idea of custom columns. The code related to the column is packaged in the custom column class instead of an aspx page, making it reusable. Although it takes some up-front cost to develop, custom columns are easier to use than TemplateColumn. The Metabuilders site has a few great custom columns. In this article, we will extend the idea further by giving the custom column class the responsibility to extract user input. We will also extend the DataGridHelper control to work with custom columns.

We will start by creating the necessary interface for the DataGridHelper control to work with custom columns. We will then build two custom columns that support such an interface. We will also discuss how to extend an existing custom column so we can use the DataGridHelper control with any custom column. We will finally demonstrate how to use the custom columns and conclude this article.

Let's define the interface first.

Introduction

[Download Code]

In Part 1 and Part 2 of this series, we successfully built the DataGridHelper control that can be used to develop a data access page with sorting, paging, deleting, and updating capabilities without writing a single line of code.  I have had too much fun to stop now. In Part 3, we will address another limitation of DataGrid and DataGridHelper control--custom columns.

In ASP.NET 1.x, the easiest way to create custom column is by using the TemplateColumn. The TemplateColumn supports one-way databinding to display the data. The update code is not exactly pretty, as seen in a Microsoft QuickStart sample, because we have to find the control from the controls collection of the table cell and extract the value from the control. The up-coming ASP.NET 2.0 will support two-way binding in GridView templates.  For ASP.NET 1.x, Marcie Robillard’s MSDN article discussed an excellent idea of custom columns. The code related to the column is packaged in the custom column class instead of an aspx page, making it reusable. Although it takes some up-front cost to develop, custom columns are easier to use than TemplateColumn. The Metabuilders site has a few great custom columns. In this article, we will extend the idea further by giving the custom column class the responsibility to extract user input. We will also extend the DataGridHelper control to work with custom columns.

We will start by creating the necessary interface for the DataGridHelper control to work with custom columns. We will then build two custom columns that support such an interface. We will also discuss how to extend an existing custom column so we can use the DataGridHelper control with any custom column. We will finally demonstrate how to use the custom columns and conclude this article.

Let's define the interface first.

Enhancing the DataGridHelper Control to Support Custom Columns

[Download Code]

Let us first examine how the DataGridHelper control extracts user input from a bound column:

BoundColumn bc = (_dataGrid.Columns[i] as BoundColumn);
if (bc != null)
{
 name = bc.DataField;
if (bc.ReadOnly)
 {
  val = e.Item.Cells[i].Text;
 }
 else
 {
  val = ((TextBox)e.Item.Cells[i].Controls[0]).Text;
 }
 col[name] = val;
}

The code contains the knowledge of the BoundColumn because we know how it works. However, we cannot expect the DataGridHelper control to have the knowledge all existing custom columns and all future custom columns; we cannot keep changing the code of DataGridHelper to add the knowledge of new custom columns. Thus our first step is to define an interface for DataGridHelper and custom columns to work together:

public interface IDataGridColumn
{
 void GetValues(TableCell cell, Hashtable col);
}

All custom columns designed to work with DataGridHelper control must implement this interface. The interface has a single method called GetValues. The DataGridHelper will pass a TableCell object and a Hashtable object to the custom column. The custom column class is responsible for extracting the input from the TableCell and adds the field/value pair to the Hashtable. This design can work with custom columns that contain more than one input control. For example, a phone number column could have a control for the area code and another control for the phone number; a zip code column could have a control for the zip code and another control for the zip extension. Since the custom columns are responsible for extracting the inputs, we only need to add the following code in the _dataGrid_UpdateCommand function of DataGridHelper to support custom columns:

IDataGridColumn ic = _dataGrid.Columns[i] as IDataGridColumn;
if (ic != null)
{
 ic.GetValues(e.Item.Cells[i], col);
}

That is it. It is so simple! Next, we will introduce two custom DataGrid columns that implement IDataGridColumn interface.

Creating Custom DataGrid Columns

[Download Code]

We included two custom DataGrid columns in this article: the CheckBoxColumn and the DropDownColumn. The custom DataGrid columns included in this article were created in a similar way to the custom columns on the MetaBuilders site and in Marcie Robillard’ MSDN article. We encourage you to read Marcie Robillard’s MSDN article if you are interested in creating custom columns. In this article, we will focus on the feature and the difference in our implementation.

The CheckBoxColumn is a very simple class and has only two properties:

DataField The data field to bind to.
ReadOnly The CheckBoxColumn always render data as CheckBox. The CheckBox is only enabled when the item is an edit item and the ReadOnly property is equal to false.

The design of DropDownColumn is somewhat different to the design of Marcie Robillard and MetaBuilders. Both the Marcie Robillard version and the MetaBuilders version expose a DataSource property for users to populate the data source. Since DataSource is not the only way to populate a DropDownList control, we decide to expose the underlying DropDownList control instead so that users can populate it in any way of their choice. The other important difference is the optimization we made for the population of the DropDownList. Since the DropDownList is used only in edit mode, we should not populate the DropDownList until we need to. For this reason, DropDownColumn exposes the LoadDropDownList event in which we can populate the DropDownList. If we care more about the simplicity than optimization, we could still populate the DropDownList in the Page_Int or Page_Load event using the DropDownList property of the DropDownColumn class. The following is the complete list of DropDownColumn properties:

DataField  The data field to be displayed. It is also the data field used for to populate the name/value pair used for update if the DataValueField is not supplied. This field is required.
DataFormatString  Used to format the data field displayed.
DataValueField  If supplied, the DataValueField and selected value of the DropDownList would be used to form the name/value pair for update.
DropDownList Used to access the underlying DropDownList. Note that for optimization purpose, the actually DropDownList object is only instantiated when it is necessary to instantiate the DropDownList, or this property is accessed, whichever occurs first.
ReadOnly  If the value of this property is true, the DropDownList is disabled.

Let’s first take a look of the implement the IDataGridColumn interface in the CheckBoxColumn:

public void GetValues(TableCell cell, Hashtable col)
{
 bool val = ((CheckBox)cell.Controls[0]).Checked;
 col[_dataField] = val;
}

We simply extract the value of the CheckBox control.

Now let us take a look of the implement the IDataGridColumn interface in the DropDownColumn:

public void GetValues(TableCell cell, Hashtable col)
{
 string name;
 object val;
   
 if  (this.DataValueField == null || this.DataValueField == "")
 {
  name = this.DataField;
  val = _ddl.SelectedItem.Text;
 }
 else
 {
  name = this.DataValueField;
  val = _ddl.SelectedItem.Value;
 }
 col[name] = val;
}

Note that we extract the text or value of the SelectedItem depending on whether the DataValueField property is set.

Next, we will discuss how to use an arbitrary custom DataGrid column with the DataGridHelper control.

Using Third-Party Custom DataGrid Columns with the DataGridHelper Control

[Download Code]

There are two ways to use a DataGrid column that does not implement the IDataGridColumn interface with the DataGridHelper control. If the source code is available, we can add the implementation for the IDataGridColumn interface. If the source code is not available, we could create a new class that is inherits from the custom DataGrid column class and implements the IDataGridColumn interface, as shown in the following example:

public class MyBoundColumn: ThirdPartyBoundColumn, IDataGridColumn
{
 public void GetValues(TableCell cell, System.Collections.Hashtable col)
 {
  //Implement the code to extract the input here
 }
} 

In the next section, we will show the use of the custom columns through an example.

Using the DataGridHelper Control with Custom Columns

[Download Code]

The DataGridHelper.aspx page demonstrates the use of DataGridHelper control with custom columns. A live demo can be accessed at http://www.dotneteer.com/projects/DataGridHelper/v3/DataGridHelperTest.aspx.

The custom columns are created and added to the DataGrid in the page_event as shown below:

private void DataGridHelperTest_Init(object sender, System.EventArgs e)
{
 DropDownColumn ddcSupplier = new DropDownColumn();
 ddcSupplier.DataField = "CompanyName";
 ddcSupplier.DataValueField = "SupplierID";
 ddcSupplier.HeaderText = "Supplier";
 ddcSupplier.SortExpression = "CompanyName";
 ddcSupplier.LoadDropDownList +=new LoadDropDownListEventHandler(ddcSupplier_LoadDropDownList);
 dataGridProducts.Columns.AddAt(4,ddcSupplier); 

 DropDownColumn ddcCategory = new DropDownColumn();
 ddcCategory.DataField = "CategoryName";
 ddcCategory.DataValueField = "CategoryID";
 ddcCategory.HeaderText = "Category";
 ddcCategory.SortExpression = "CategoryName";
 ddcCategory.LoadDropDownList +=new LoadDropDownListEventHandler(ddcCategory_LoadDropDownList);
 dataGridProducts.Columns.AddAt(5,ddcCategory); 

 CheckBoxColumn cbcDiscontinued = new CheckBoxColumn();
 cbcDiscontinued.DataField = "Discontinued";
 cbcDiscontinued.HeaderText = "Discontinued";
 cbcDiscontinued.SortExpression = "Discontinued";
 dataGridProducts.Columns.AddAt(11,cbcDiscontinued);
}

Note that it is possible to add the custom columns declaratively using tags. However, that would break the design-time behavior of the DataGrid control because it does not recognize the custom columns for the two DropDownColumn classes. Note that we also attached event handlers to the LoadDropDownList event of the DropDownColumn class. Since the two event handlers are nearly identical, we only show one below:

private void ddcCategory_LoadDropDownList(object Sender, LoadDropDownListEventArgs e)
{
 DropDownList ddl = e.DropDownList;
 if (sqlConnection.State != ConnectionState.Open)
  sqlConnection.Open();

 SqlCommand cmd = new SqlCommand("select CategoryID, CategoryName from Categories",sqlConnection);
 SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
 ddl.DataTextField = "CategoryName";
 ddl.DataValueField = "CategoryID";
 ddl.DataSource = dr;
 ddl.DataBind();
}

In the next section, we conclude this article.

Conclusion

[Download Code]

In this article, we have shown a way to use custom columns to enhance the functionality of a data access page based on the DataGrid control. We have also designed a generic way for DataGridHelper to work with custom columns. Although this method is not quite as easy to use as the two-way data-binding promised in the upcoming ASP.NET 2.0, it is a more reusable way to extend the functionality of the DataGrid control.

Resources:

阅读更多
个人分类: WEBFORM
上一篇Building a DataGrid Helper Control for ASP.NET 1.x: Part 2
下一篇ASP.NET 2.0 Developers Overview
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭