Introduction
Displaying data from various datasources like Database, XML etc. has never been easier since the advent of ASP.NET's Server Control DataGrid
. The DataGrid
control is used to display data from the datasource defined by its DataSource
property. There are tonnes of features provided by DataGrid
in displaying data. Sorting each column when clicked on the name of the column in the DataGrid
is one of the best features. We will concentrate on sorting XML.
Displaying XML data using DataGrid
and achieving the same tasks is a little bit different. The reason is, as such an XML file when loaded by a DataGrid
will only display the XML content as "text". On the other hand, data from a database table will have all the constraints (like what type of data - int, float, double,...) embedded into the DataGrid
instance. So you dont have to emphasize in your code that "Col1 of my data is of type integer(and not text)".
But if you display XML you have to specify somehow or other that what type of data each of your column contains. Otherwise, all values are considered as type "text". Remember, you have to do this only if you want to get certain type-specific features of DataGrid
. If you dont need these features, but simply want to display your XML in a DataGrid
you dont have to worry at all. You can proceed in the same way as you do for displaying your SQL data.
Assumptions
Before I proceed I assume my readers are familiar with the following. Please do not worry if you dont know any of the listed. You may still proceed as I have made the article so simple and not added complex issues.
- XML and Schemas
- ASP.NET code-behind files
- Basic HTML Authoring
Contents
This article is broken down into pieces of subsections named as follows. This will walk you through the entire steps to do Sorting of XML data.
DataGrid
and properties - Design time and Run time
AllowSorting
property and OnSortCommand
method
- The
BindGrid
function
- Sample XML and XSD files explained
There are two sample files that I have included in this article. XMLDataGrid.ASPX (the template file) and XMLData.CS (the code-behind file). My data files are books.XML and books.XSD. Down the road we will learn how to design all the sample files. Lets get ourselves wet.
DataGrid and properties
First lets see how to write our XMLDataGrid.ASPX file. This file acts like a template to generate the DataGrid
table. Attaching the data (technically called DataBinding) to the grid and assigning other properties are done in the code behind file. To add a DataGrid
to an ASP.NET web form we use the <asp:DataGrid>
tag. So we add the following code to our ASPX page which already contains the <html>
tags.
<body>
<form runat="server">
<span class="normaltext">Find the difference between sorting of
Item Id column and Amount column</span>
<br>
<asp:datagrid runat="server">
</asp:datagrid>
</form>
</body>
A point to ponder:- Note in the file the Page
attribute has Inherits
property set to our class name (XMLDataMain
) in our code-behind file and Src
property set to .CS file.
AllowSorting property and OnSortCommand method
Following are the design time properties that we are setting for our DataGrid
control in our ASPX file.
CssClass=asptable
id="XMLDataGrid"
CellSpacing="1"
OnSortCommand="SortGrid"
AllowSorting="True"
The CssClass
property allows you to set any custom styles you want to apply to the grid. The id
property is the unique identifier for our DataGrid
control. This identifier is used in our code-behind file to handle our DataGrid
programmatically. The OnSortCommand
is used to specify our custom function that should be called when the column names of the DataGrid
is clicked. This will work only when the AllowSorting
property is set to "true". If the AllowSorting
property is true you will automatically get hyperlinks to your column names. Easy, isnt it!! when you hear something is done AUTOMATICALLY. This is pretty much it for our .ASPX file. Now lets move on to our code-behind file. The XMLData.CS.
The BindGrid function
To better understand our code-behind file, lets discuss what do we need to do further to achieve our goal. We have our DataGrid
ready to receive any data and its also ready to sort the columns based on the implementation of SortGrid
function. So all we need is a function which binds the XML data with the DataGrid
. Also we are making it so general that our SortGrid
function is going to make use of that function and just tells what column needs to be sorted. Of course, Sorting is binding same data to the Grid but the rows are shuffled based on our Sort field.
Lets call our function as BindGrid()
. Now BindGrid
has to read the XML and bind it to the DataGrid
. The following code snippet would simply do this.
using System;
using System.XML;
using System.Data;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
public class XMLDataMain : Page
{
public DataGrid XMLDataGrid;
protected void Page_Load(Object src, EventArgs e)
{
if(!IsPostBack)
{
BindGrid();
}
}
protected void BindGrid()
{
string dataUrl = "books.XML";
DataSet ds = new DataSet();
ds.ReadXML(Server.MapPath(dataUrl));
DataView dv = new DataView(ds.Tables[0]);
XMLDataGrid.DataSource = dv;
XMLDataGrid.DataBind();
}
}
A point to ponder:- Refer MSDN documentation for more details on DataBinding. I have used Server.MapPath()
function. So, for the demo code to work, you should unzip all the files under the same folder. Or if you want to copy the XML data file to a different folder, please change the argument of the ReadXML()
in the above code.
The ReadXML method of the DataSet
class understands the specified XML data and converts it into a relational format (meaning, tables, columns, rows, constraints etc.). Then a DataView
is created using the first and only table in the DataSet ds
. This view becomes our DataSource
and once we call the DataBind
function of our grid, the XML data is rendered in a table format. So we call our BindGrid
function from Page_Load
. But we are not completed yet.
Sorting the columns
We are going to use a special state management feature ViewState
provided by ASP.NET to achieve our sorting. We can achieve it in a much simpler way, but this will also help you to learn this cool new feature. For more information on ViewState
refer to Taking a Bite Out of ASP.NET ViewState. We are going to use two custom-defined properties to handle our Sorting. They are
SortField
, name of the field which should be sorted on a particular request.
SortAscending
, a boolean value to toggle our sorting between Ascending and Descending when the user clicks the same column many times.
Add the following code to our XMLDataMain
class
string SortField {
get {
object o = ViewState["SortField"];
if (o == null) {
return String.Empty;
}
return (string)o;
}
set {
if (value == SortField) {
SortAscending = !SortAscending;
}
ViewState["SortField"] = value;
}
}
bool SortAscending {
get {
object o = ViewState["SortAscending"];
if (o == null) {
return true;
}
return (bool)o;
}
set {
ViewState["SortAscending"] = value;
}
}
The only complication that you find in these get/set methods are the usage of ViewState
(if you were not aware of it before). If its too much to understand, in order to proceed with the article, just think of the way you assign a Session variable in traditional ASP. ViewState
is something like that. I dont want to get too much into ViewState
functionality. For more details, refer to the above specified link. Also, Note that we toggle the SortAscending
property, if its true then sort by Ascending order otherwise by Descending.
Lets go back to our BindGrid
function and make certain changes to handle sorting. We already know that BindGrid
is the only function we rely one, for any rendering changes. So here are our changes.
protected void BindGrid()
{
string dataUrl = "books.XML";
DataSet ds = new DataSet();
ds.ReadXML(Server.MapPath(dataUrl));
DataView dv = new DataView(ds.Tables[0]);
DataTable dt = dv.Table;
dv.Sort = SortField;
if (!SortAscending)
{
dv.Sort += " DESC";
}
XMLDataGrid.DataSource = dv;
XMLDataGrid.DataBind();
}
We use the Sort
property of the DataView
and specify which field needs to be sorted. We also append the string Desc
with the field name if we want descending order sorting. By default a column will be sorted in the ascending order. We are almost there. Now the only final thing that we have to finish is to hook up this function to our SortGrid
command function. Because this is the function which will be called when the user requests Sorting of a column by clicking the column name.
Guess what we have to write in our SortGrid
command function?? Change the SortField
property to which the user requested and call the BindGrid
function and you are all set. Here is the code.
protected void SortGrid(Object src, DataGridSortCommandEventArgs e)
{
XMLDataGrid.CurrentPageIndex = 0;
SortField = e.SortExpression;
BindGrid();
}
Since we would be having only one page to display we set the pageindex
to 0. If you have custom Paging
feature enabled you set the appropriate page index to sort. Then we set our SortField
property to the one the user requested. The user requested field name is stored in the DataGridSortCommandEventArgs
object's SortExpression
property. Finally we call our BindGrid()
function.
Sorting Integers - Sample XML and XSD files
So far everything will work fine. But if you try to click on the columns (<iid>
and <iamount>
) where numbers appear the sorting behaves differently. Kinda disappointing. The numbers are sorted as if they are text. So 8 becomes > 120. This is not what we want from our DataGrid
. The only way is to tell the DataGrid
that the XML tags which bears numbers are real numbers and not text. To do this we define our own Schema and load the schema along with our XML into the DataGrid
.
So lucky we are, that we have this ReadXMLSchema
method in DataSet
class, to load our Schema and validate our XML against this Schema. This will also caution the DataSet
while preparing the relational information of whatever data that is loaded. If it is loaded with an XML data it checks whether a Schema is loaded along with that. If so it uses the schema to analyze the XML data and prepares itself based on what is said in the Schema about the XML. If you see the sample Schema (books.XSD) you may notice that
<ElementType name="iid" content="textOnly" dt:type="ui4"/>
The iid
element is defined as unsigned integer. I have also deliberately left the iamount
element as string to find the difference between sorting of two number columns. Here is the code change that the BindGrid
needs to undergo.
protected void BindGrid()
{
string dataUrl = "books.XML";
string schemaUrl = "books.XSD";
DataSet ds = new DataSet();
ds.ReadXMLSchema(Server.MapPath(schemaUrl));
ds.ReadXML(Server.MapPath(dataUrl));
DataView dv = new DataView(ds.Tables[0]);
DataTable dt = dv.Table;
dt.Columns[0].ColumnName = "Item Description";
dt.Columns[1].ColumnName = "Item Id";
dt.Columns[2].ColumnName = "Amount";
dv.Sort = SortField;
if (!SortAscending)
{
dv.Sort += " DESC";
}
XMLDataGrid.DataSource = dv;
XMLDataGrid.DataBind();
}
A point to ponder:- You should call the ReadXMLSchema
method before you call ReadXML
method.
Didnt you find any difference in the snippet that I added just above. Anything new!! Anything interesting!! Here it is. Since the DataSet
reads the XML it also gives column names as it was found in the original data. I dont like it nor do you, I guess. I dont want to change my XML tag names just for the sake of it. So I changed the column names by using the ColumnName
property of each column in the DataTable
.
Conclusion
There are a lot of interesting properties and methods in the DataGrid
class, like editing the XML data from the grid and saving them back and many more. Explore them per your wish and enjoy .NETing!!!
References
Fun exercises
Wanna mess with our DataGrid
and find out whatelse can it do? Try these.
- Convert the
iamount
tag into a float datatype and try to display it in the form US$... Now try to sort it. You should sort only the amount not the entire string US$...
- Find out how sorting works if you have case sensitive strings
- Change one of the
iid
to a string like, abc or something and see how its rendered. Try to handle the exception if you get any...