Tabular Report

Description:    The tabular report displays a list of products grouped by product category.

Overview:   The tabular report shows a basic way of grouping related data (in this case, grouping products by category). The grouping is done by nesting a DataGrid control inside of a DataList control.

Additionally, this report demonstrates how to implement interactive sorting using the DataGrid Server Control.

For this report, sorting is enabled for each column in the Product Details header, except for the Quantity per Unit and Extended Price columns. The sortable columns can be sorted in ascending or descending order. Sortable columns are displayed as red and underlined when the user hovers over the column header.

In addition to sorting, the DataGrid also features the ability to selectively control the formatting of a row based on custom criteria. In this case, Products which are out of stock (designated with a quantity of zero) will be displayed inred.

The extended price is calculated as the Datagrid is data binding. It is displayed as the right most column.

Implementation Notes:    To group the related data, a DataGrid control is nested inside of a DataList control.  In this case, DataGrid is chosen for it's ability to implement sorting and to examine its data source during runtime.

The above diagram shows how the DataList and the DataGrid work in tandem. The DataList displays the category name and summary information, while the DataGrid returns the products details based on the Category ID passed in from the DataList for each category.

The DataList's data source is a TabularReportCollection returned from the GetHeader function. To correlate each category with its details, the DataGrid's data source is assigned dynamically by calling the GetDetails function for each category as the DataList is binding.

		
	<asp:datagrid id=Datagrid1 runat="server" 
	DataSource='<%# GetDetails((int)DataBinder.Eval(Container.DataItem, "CategoryID")) %>' ... >
		

Sorting is a simple task to implement with ASP.NET. The DataGrid's AllowSorting property is set to True and an event handler for the sorting is added to the DataGrid. The event handler then catches the sorting criteria where it is used to sort the appropriate DataGrid column.

	<asp:datagrid id=Datagrid1 runat="server" ... AllowSorting="true" OnSortCommand="SortGrid">
		
		
	private void SortGridData(TabularReportCollection list, string sortField, bool asc)
	{
		TabularReportCollection.TabularReportFields sortCol = 
			TabularReportCollection.TabularReportFields.InitValue;

		switch(sortField)
		{
			case "ProductName":
				sortCol = TabularReportCollection.TabularReportFields.ProductName;
				break;
			case "UnitPrice":
				sortCol = TabularReportCollection.TabularReportFields.UnitPrice;
				break;
			case "UnitsInStock":
				sortCol = TabularReportCollection.TabularReportFields.UnitsInStock;
				break;
		}

		list.Sort(sortCol, asc);
	}
		
		
	public void Sort(TabularReportFields sortField, bool isAscending)
	{
		switch (sortField) 
		{
			case TabularReportFields.ProductName:
				base.Sort(new ProductNameComparer());
				break;
			case TabularReportFields.UnitsInStock:
				base.Sort(new UnitsInStockComparer());
				break;
			case TabularReportFields.UnitPrice:
				base.Sort(new UnitPriceComparer());
				break;
		}

		if (!isAscending) base.Reverse();
	}
		

The color coding is done by checking the units in stock as the inner DataGrid databind and changing the style class for that line item.

	// if there are no units in stock, color the line item as red by changing the style class
	if (unitsInStock == 0)
		e.Item.CssClass = "OutOfStock";
		

The OutOfStock style used above is defined in the styles.css file.

	.OutOfStock 
	{
		color: red;
	}
		

To calculate the extended, an event handler is added to the OnItemDataBound event of the Datagrid. It retrieves the unit price and multiplies that to the units in stock to calculate the extended price of a product.

	protected void CalculateExtendedPrice(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
	{
		if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
		{
			double unitsInStock = Convert.ToDouble(e.Item.Cells[1].Text);
			double unitPrice =  Convert.ToDouble(e.Item.Cells[4].Text);
			e.Item.Cells[5].Text = string.Format("{0:c}", unitPrice * unitsInStock);

			// format unit price column as currency
			e.Item.Cells[4].Text = string.Format("{0:c}", unitPrice);
			
			...
		}
	}