DataGrid is a powerful component and utilizing the sorting and the paging features of it is quite simple which requires adding only a few lines of code. However as a class library builder, I want to have a
DataGrid class that has all the built-in features of sorting and paging so that the programmers don't have to even write those few lines of code. The updated version of the article provides the sorting feature for HyperLink Column and Template Column besides the Bound Column from the original version.
Using the code
The DLL contains the following classes:
DataGridNavigationBar and the
OridinalProvider. After adding the reference to the DLL and adding the DLL to the ToolBox, you can drag and drop the latter two controls to your Web page in Design mode. The HTML code should contain a reference to the DLL as given in the code below:
<%@ Register TagPrefix="ntm" Namespace="NTM.Controls" Assembly="SPDataGrid" %>
Your web page should be inherited from the
CmdButtonForm class like this:
public class WebForm1 : NTM.Controls.CmdButtonForm
You can utilize either both the sorting and the paging features of the
SPDataGrid class or just one of them. Firstly, the
ConnectionStr property (which of course specifies the connection string) should be set correctly. To fill up the
DataGrid, call the
Populate public method. It has only one parameter that is the
SQLSelectStr which contains the
SELECT statement. Properties used with the sorting feature are:
ListOfSortableColumns: A comma separated string containing zero-based indexes of the columns you want to make as sortable (e.g. "0,1,3,4"). If you want to make all the columns in the
DataGrid sortable, set
ListOfSortableColumns = "all".
InitialSortingColumnIndex: The zero-based index of the column you initially want to sort by. Default value of -1 means the
DataGrid content will not be sorted at the first time while calling the
Populate method. Note that the initial sorting column may not be in the list of sortable columns.
DESC (ending) or any other value means ascending.
myOrdinalProvider: The cooperating ordinal provider object. If you don't specify this, the default
OrdinalProvider will step into action. Check the details under the Sorting section.
You should set
AllowPaging = "true" if you want to use the paging feature. Properties used with the paging feature are:
NavigationBarControl: The cooperating navigation bar. For those who are using the original version, please note that the
NavigationBarControlID was used and now replaced by this property.
InitialPageSize: The initial size of each page. Default value is 20.
You can add as many
SPDataGrid to your page as you want. You can also check the demo project to see how to use the components. It works with the NorthWind database of MS SQL Server.
Points of interest
What is a CmdButtonForm class?
You may wonder what the
CmdButtonForm class is doing here when all you want is to work with
DataGrids. Actually, that is the trick I usually use while working with pages having multiple submit buttons at the class library level. The idea is that the page will add some hidden fields and each Submit button will modify their values before submitting, so by checking their values, we will know which Submit button was pressed. In this article I have used two hidden fields to store the command and the command parameter of Submit. The
function __SetCmd(cmd, param)
var CmdTag = document.getElementById('__Cmd');
var ParamTag = document.getElementById('__CmdParam');
if (CmdTag != null)
if (ParamTag != null)
OnClick event handler of each Submit button on the page will call the
__SetCmd function above. For instance, the first button in the navigation bar attached to the
When the page is submitted, on the server side, the
DataGrid will check the values of the hidden fields and "execute" the command if necessary, as shown in the following code:
protected override void OnLoad(EventArgs e)
string Command = GetInputHiddenValue("__Cmd");
string CommandParam = GetInputHiddenValue("__CmdParam");
string ControlID = "";
int nOff = CommandParam.IndexOf("$");
ControlID = CommandParam.Substring(0,nOff);
CommandParam = CommandParam.Substring(nOff+1);
ControlID = CommandParam;
CommandParam = "";
As you can see, the
DataGrid will check the
Command parameter against its ID to see if the command was intended to be executed by it. If the command has more than one parameter, then the parameters will be concatenated into a string with a pre-defined separator (in our case it is the dollar sign ($)). Talking about real object-oriented encapsulation, it could be better if the
DataGrid class can add those hidden fields by itself instead of the container page class. However, .NET will throw an exception saying: "A control cannot modify its parents' control collections" if you try to do so.
The SPDataGrid class
SPDataGrid class is using the ViewState to store some data which should be persisted over the requests. One of these persistent data is the SQL
SELECT statement. It is set in the
Populate method and will last until the next
Populate is called.
Unlike the other .NET sorting samples, I have used the sorting feature of the database engine instead of utilizing the sorting feature of the .NET platform provided by the
DataView.Sort method. I believe that this sorting method can provide much better performance while working with large datasets, especially if you create proper indexes for the DB tables. However, I didn't have time to test and measure it and I will be very glad if you share your experience related to this matter with me. The
SPDataGrid class needs the information about the ordinal of each (sortable) column in the SQL
SELECT statement in order to sort its content and the
OrdinalProvider class (newly introduced in the updated 1.1 version) is providing that info. The
OnClick event handler of each Sort image (button) in the column's header, so when the image button is clicked, the class will know of which column the user wants to get the content sorted by. Now let's see how the
OrdinalProvider class can provide this info by default. In the case of
BoundColumn, it takes the
DataField property of each column and checks it against the SQL
SELECT statement to get the ordinal of the column in the
SELECT statement. With
DataTextField property will do the same:
if (dg.Columns[ColumnIndex] is BoundColumn)
else if (dg.Columns[ColumnIndex] is HyperLinkColumn)
However, if you are using Template columns or you are not happy with this "default service" you can have your own Ordinal provider. In the example web page, I use a Template column with a checkbox in the Customers
SPDataGrid1), which shows that if the customer is an international (non-USA) customer. Here is the entire
select CustomerID, CompanyName, ContactName, ContactTitle, Address,
City, Country,(case country when 'USA' then 0 else 1 end) as Int from customers
The Template column looks like this:
<asp:checkbox id=CheckBox1 Enabled="False"
"Int"))%>' Text="" runat="server">
To make this Template column sortable, I override the default
OrdinalProvider class and provide the ordinal of this column in the SQL
public class SPDataGrid1_OrdinalProvider : OrdinalProvider
public override int GetColumnOrdinal(SPDataGrid dg,
SqlDataReader myReader, int ColumnIndex)
if (ColumnIndex == 7)
return base.GetColumnOrdinal(dg, myReader, ColumnIndex);
The only one thing more to do is to tell the
DataGrid to use this
OrdinalProvider instead of the default one:
private void Page_Load(object sender, System.EventArgs e)
SPDataGrid1.myOrdinalProvider = new SPDataGrid1_OrdinalProvider();
DataGridNavigationBar is a composite control inherited from the
Panel class. It contains several labels, textboxes and buttons. You can customize it to your taste by using adjust/add/remove elements. Just remember to add the proper
OnClick event handler to the buttons as I mentioned. The class doesn't have any
public methods but there are two internal methods that are called from the cooperating
internal void Initialize(string AttachedTo)
internal void AdjustNavigationBar(int NumberOfRows, int CurrentPageIndex,
int PageCount, int PageSize)
The first method "attaches" the navigation bar to the
DataGrid and the second method is called to enable/disable the navigation buttons depending on the
CurrentPageIndex property of the
DataGrid class presented here (together with the "escorting" classes) provide a reusable mechanism of adding sorting and paging features to ASP.NET
DataGrids. The class works only with the
DataGrids displaying contents populated from a relational database engine (RDBMS). However, with some modifications, you can build your own
DataGrid class that works with any other data source. To give you something to start with, think of using the
DataView.Sort method instead of building the SQL
SELECT statement with the
ORDER BY part as I did in this article...
- April 26th, 2005 - Initial version.
- July 14th, 2005 - Updated version 1.1:
NavigationBarControlID was replaced by
NavigationBarControl for better performance and clearer code.
- Introducing the
SortingMgr class to provide sorting feature for
ResetInputHiddenValue method was created and called inside the
OnLoad event to clear
__CmdParam (bug fix).