<html>
<head>
<title>ASP.NET Reports Starter Kit Documentation</title><link href="style.css" type="text/css" rel="stylesheet"></head>
<body class="NormalIndent">
<h1>HierarchicalReport
</h1>
<p><b>Description:</b> The hierarchical report displays 3
different grids. The first grid displays sales totals, grouped by
territory, and filtered by year. The second grid displays sales totals grouped
by employee, within a specific territory - namely, the territory selected
in the first grid. The third grid shows information on a
particular employee - namely, the employee selected in the second grid.
</p>
<p><strong>Overview:</strong> Three DataGrids are set up on
this page, each with their own data source, retrieved from three separate
stored procedures. Only the data source for the first grid, "Sales
By Territory" is bound at Page Load and does not change; the other 2 grids are
hidden until the user selects the Territory, and then the Employee. When
the user chooses which Territory to view, the second grid, "Sales for [<em>Territory</em>]"
becomes visible, and is bound to the results of the stored procedure
"GetEmployeeSalesByTerritory", which accepts the Territory Name as a
parameter. Similarly, when the user chooses which Employee to view, the
third grid, "Employee Info for [<em>Employee</em> ]" becomes visible, and is
bound to the results of the stored procedure "GetEmployeeByID", which accepts
the Employee's ID as a parameter. Should the user click on the Territory
grid when the Employee grid is visible, the Employee grid will hide,
and the Employee Sales by Territory grid will refresh with the new data.
</p>
<p>Additionally, this report demonstrates how to implement interactive sorting and
paging using ASP.NET.
</p>
<p>
Sorting is enabled for each column in the first two DataGrids. 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.
</p>
<p><img src="./images/1x1.gif" width="25"><img src="images/HierarchicalSorting.gif"></p>
<p>Paging is also enabled in the first two DataGrids, so that a limited number
of records can be displayed on each page, with the ability to navigate through
the pages of data. Links to previous and next pages are
displayed just above the footer section of the DataGrids. In the
footer section, below the navigation links, a text field shows how many
pages total there are, as well as which page the user is on. </p>
<p><img src="./images/1x1.gif" width="25"><img src="images/HierarchicalPaging.gif">
<br>
</p>
<p><strong>Implementation Notes: </strong>
</p>
<p>In order for the user to be able to make a selection from one of the grids, and
to be able to pass that information to the codebehind, a Template column is
used as the first column in the top two DataGrids. A Template
column is used to create a column with a customized control layout,
including custom appearances for the heading section, the footer section, and
the items section of the column. In this example, a LinkButton within
the DataGrid Template column captures the user input - namely, the
Territory or Employee selected.</p>
<pre> <<font color=blue>asp:templatecolumn</font> sortexpression="Territory" headertext="Territory Name">
<itemtemplate>
<asp:linkbutton <font color=blue>oncommand="TerritoryGrid_Click" commandname="TerritoryDrill"
commandargument='<%# DataBinder.Eval(Container, "DataItem.TerritoryDescription") %>'</font>
cssclass="hier" runat="server" id="Territorylnk"
text='<%# DataBinder.Eval(Container, "DataItem.TerritoryDescription") %>'
</asp:linkbutton>
</itemtemplate>
<itemstyle width="200px"></itemstyle>
</asp:templatecolumn>
</pre>
<p>There are three main parts to the implementation of the LinkButton within the
Template column: the <em>OnCommand</em> attribute, the <em>CommandName</em> attribute,
and the <em>CommandArgument</em> attribute. The OnCommand attribute
specifies which method should be run when the LinkButton's text is
pressed. The CommandName and CommandArgument attributes hold data that
can be passed as parameters to the method. In the above code snippet, the
CommandArgument receives the value of the "TerritoryDescription" field of the
item selected by the user. The TerritoryGrid_Click method receives this
value and passes it to the BindList_EmpTerr method, which is a method that
binds the data for the second grid.</p>
<pre>
Protected Sub TerritoryGrid_Click(ByVal sender As [Object], ByVal e As CommandEventArgs)
Dim territoryName As String = e.CommandArgument.ToString().Trim()
' Put the second grid back to the first page
EmployeeTerritoryGrid.CurrentPageIndex = 0
' Show the table with the second grid, and update the headers
ETTableColumn.Visible = True
ETHeader.Text = territoryName
BindList_EmpTerr(territoryName)
' Hide the 3rd table if it was visible
EmpTableColumn.Visible = False
End Sub 'TerritoryGrid_Click
</pre>
<p>In the second grid, both the EmployeeID and the EmployeeName are passed into the
function ETGrid_Click. The EmployeeName field is used for display in the
third grid's Header section, and the EmployeeID field is used to pass in to the
BindList_Emp method, which is a method that binds the data for the third grid.</p>
<pre> <asp:templatecolumn sortexpression="EmployeeName" headertext="Employee Name">
<itemtemplate>
<asp:linkbutton oncommand="ETGrid_Click"
<font color="blue">commandargument='<%# (String) DataBinder.Eval(Container, "DataItem.EmployeeName") +
"," + DataBinder.Eval(Container, "DataItem.EmployeeID").ToString() %>'</font>
cssclass="hier" runat="server"
id= "lnkEmployeeTerritory" text='<%#
DataBinder.Eval(Container, "DataItem.EmployeeName") %>'> </asp:linkbutton>
</itemtemplate>
<itemstyle
width= "150px"></itemstyle>
</asp:templatecolumn>
</pre>
<pre>
Protected Sub ETGrid_Click(ByVal sender As [Object], ByVal e As CommandEventArgs)
Dim cmdArgs As String() = e.CommandArgument.ToString().Split(","c)
Dim emploName As String = cmdArgs(0)
Dim employeeID As Integer = Convert.ToInt32(cmdArgs(1))
' Show the table with the 3rd grid, and update the headers
EmpTableColumn.Visible = True
EmployeeName.Text = emploName
BindList_Emp(employeeID)
End Sub 'ETGrid_Click
</pre>
<p>
Binding the data to the top two DataGrids includes four
easy steps to account for paging and sorting - retrieving the data,
sorting the data, binding the data to the DataGrid's DataSource, and updating
the paging text labels.</p>
<pre>
Private Sub BindList_EmpTerr(ByVal territoryName As String)
Dim empTerritoryList As HierarchicalReportCollection = HierarchicalReport.GetEmployeeSalesByTerritory(territoryName, Convert.ToInt32(ddlYear.SelectedItem.Value))
' do the sorting if there are data returned
If empTerritoryList.Count > 0 Then
SortGridData(empTerritoryList, SortField_EmpTerr, SortAscending_EmpTerr)
EmployeeTerritoryGrid.DataSource = empTerritoryList
EmployeeTerritoryGrid.DataBind()
End If
' Update paging labels
CurrPage_EmpTerr.Text = Convert.ToString((EmployeeTerritoryGrid.CurrentPageIndex + 1))
TotPages_EmpTerr.Text = EmployeeTerritoryGrid.PageCount.ToString()
End Sub 'BindList_EmpTerr
</pre>
<p>Sorting is a simple task to set up and 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 then used to sort the appropriate DataGrid's column.
</p>
<pre> <asp:datagrid id="TerritoryGrid" runat="server" ... <FONT color=blue> AllowSorting="True" OnSortCommand="SortGrid_Terr"</FONT> ...>
</pre>
<pre>
Private Sub SortGridData(ByVal list As HierarchicalReportCollection, ByVal sortField As String, ByVal asc As Boolean)
Dim sortCol As HierarchicalReportCollection.HierarchicalReportFields = HierarchicalReportCollection.HierarchicalReportFields.InitValue
Select Case sortField
Case "Territory"
sortCol = HierarchicalReportCollection.HierarchicalReportFields.Territory
Case "SalesTotals"
sortCol = HierarchicalReportCollection.HierarchicalReportFields.SalesTotals
Case "EmployeeName"
sortCol = HierarchicalReportCollection.HierarchicalReportFields.EmployeeName
End Select
list.Sort(sortCol, asc)
End Sub 'SortGridData
</pre>
<p>The SortGridData method in the codebehind determines which field to sort
on, and passes that value to the Sort method of the
HierarchicalReportCollection object. The Sort method in the
HierarchicalReportCollection component calls the appropriate IComparer method,
which simply compares items in order to sort the list.
</p>
<pre>
Public Overloads Sub Sort(ByVal sortField As HierarchicalReportFields, ByVal isAscending As Boolean)
Select Case sortField
Case HierarchicalReportFields.Territory
MyBase.Sort(New TerritoryNameComparer())
Case HierarchicalReportFields.SalesTotals
MyBase.Sort(New SalesTotalsComparer())
Case HierarchicalReportFields.EmployeeName
MyBase.Sort(New EmployeeNameComparer())
End Select
If Not isAscending Then
MyBase.Reverse()
End If
End Sub 'Sort
</pre>
<p>
Implementing ASP.NET paging is even simpler. The AllowPaging property is
set to True, a PageSize is set, and a short event handler is
defined. The only thing that typically has to be done in the
event handler is to set the DataGrid's CurrentPageIndex to the
DataGridPageChangedEventArgs.NewPageIndex, and to re-bind. Here, in
addition, the second and third table are hidden when the page is changed. </p>
<pre> <asp:datagrid id="TerritoryGrid" runat="server" ... <font color=blue>AllowPaging="True" PageSize="5" OnPageIndexChanged="Page_Terr"</font> ... />
</pre>
<pre>
Protected Sub Page_Terr(ByVal sender As [Object], ByVal e As DataGridPageChangedEventArgs)
' Hide the 2nd and 3rd table if they were visible
ETTableColumn.Visible = False
EmpTableColumn.Visible = False
TerritoryGrid.CurrentPageIndex = e.NewPageIndex
BindList_Terr()
End Sub 'Page_Terr
</pre>
<p>Finally, a style can be set for the Previous and Next text labels.</p>
<pre> <pagerstyle CssClass="PagerHier" PrevPageText="&lt; Prev" NextPageText="Next &gt;"
Height="30px" VerticalAlign="middle"></pagerstyle>
</pre>
</body>
</html>