ASP.NET DataIslandGrid Control
By Fons Sonnemans (January 2003)
Download
DataIslandGrid.zip
Introduction
The standard ASP.NET DataGrid control is a great control. You can use it for many
things, it even supports Paging and Column Sorting. Those last two option although
work using postbacks to the server.
Internet Explorer 5.0 (and higher) support XML Client-side Data-Binding. This is
a powerful DHTML feature which is not used in the .NET Framework. It allows.
The DataIslandGrid control is an ASP.NET grid which is bound to a DataTable in a
DataSet. The DataSet is serialized and rendered to an Xml DataIsland. The Grid uses Tabular
Data-Binding to the Xml DataIsland. This makes it possible to support client-side
Column Sorting and Paging. The Column Sorting is implemented using a JavaScript
and a StyleSheet in a second Xml DataIsland.
Example
The following grid is a DataIslandGrid which is bound to the Authors table of the
SQLServer pubs database.
172-32-1176
White
Johnson
408 496-7223
10932 Bigge Rd.
Stein
CA
94025
true
213-46-8915
Green
Marjorie
415 986-7020
309 63rd St. #411
Oakland
CA
94618
true
238-95-7766
Carson
Rony
415 548-7723
589 Darwin Ln.
Berkeley
CA
94705
true
267-41-2394
O'Leary
Michael
408 286-2428
22 Cleveland Av. #14
San Jose
CA
95128
true
274-80-9391
Straight
Dean
415 834-2919
5420 College Av.
Oakland
CA
94609
true
341-22-1782
Smith
Meander
913 843-0462
10 Mississippi Dr.
Lawrence
KS
66044
false
409-56-7008
Bennett
Abrahamfdfs
415 658-9932
6223 Bateman
Berkeley
CL
94705
true
427-17-2319
Dull
Ann
415 836-7128
3410 Blonde St.
Palo Alto
CA
94301
true
472-27-2349
Gringlesby
Burt
707 938-6445
PO Box 792
Covelo
CA
95428
true
486-29-1786
Locksley
Charlene
415 585-4620
18 Broadway Av.
San Francisco
CA
94130
true
527-72-3246
Greene
Morningstar
615 297-2723
22 Graybar House Rd.
Nashville
TN
37215
false
648-92-1872
Blotchet-Halls
Reginald
503 745-6402
55 Hillsdale Bl..
Corvallis
OR
97334
true
672-71-3249
Yokomoto
Pete
415 935-4228
3 Silver Ct.
Walnut Creek
CA
94595
true
712-45-1867
del Castillo
Innes2
615 996-8275
2286 Cram Pl. #86
Ann Arbor
MI
48105
true
722-51-5454
DeFrance
Michel
219 547-9982
3 Balding Pl.
Gary
IN
46403
true
724-08-9931
Stringer
Dirk
415 843-2991
5420 Telegraph Av.
Oakland
CA
94609
true
724-80-9391
MacFeather
Stearns
415 354-7128
44 Upland Hts.
Oakland
CA
94612
true
756-30-7391
Karsens
Livia
415 534-9219
5720 McAuley St.
Oakland
CA
94609
true
807-91-6654
Panteley
Sylvia
301 946-8853
1956 Arlington Pl.
Rockville
MD
20853
true
846-92-7186
Hunter
Sheryl
415 836-7128
3410 Blonde St.
Palo Alto
CA
94301
true
893-72-1158
McBadden
Heather
707 448-4982
301 Putnam
Vacaville
CA
95688
false
899-46-2035
Ringer
Anne
801 826-0752
67 Seventh Av.
Salt Lake City
UT
84152
true
998-72-3567
Ringer
Albert
801 826-0752
67 Seventh Av.
Salt Lake City
UT
84152
true
The Html of this page must have redirective to the DataIslandGridProject. Then you
can add the DataIslandGrid control to the Page. The best way to do this is by adding
the assembly to your Toolbox. You then can drag&drop the DataIslandGrid from
your Toolbox onto your Page.
<%@ Register
TagPrefix="rit" Namespace="ReflectionIT.Web.UI.WebControls"
Assembly="DataIslandGridProject"
%>
.
.
<rit:DataIslandGrid
id="DataIslandGrid1"
runat="server"
CellSpacing="0" BorderWidth="1px"
AllowSorting="true"
CssClass="clsTest"
width="80%"
BorderColor="WhiteSmoke">
<rit:BoundColumn
CssClass="clsColumnID"
Width="120px" DataField="au_id" HeaderText="ID" />
<rit:BoundColumn
DataField="au_fname"
HeaderText="Firstname"></rit:BoundColumn>
<rit:BoundColumn
DataField="au_lname"
HeaderText="Lastname"></rit:BoundColumn>
<rit:BoundColumn
DataField="address"
HeaderText="Address"></rit:BoundColumn>
<rit:BoundColumn
DataField="city" HeaderText="City"></rit:BoundColumn>
</rit:DataIslandGrid>
.
.
You add Columns to the datagrid using the BoundColumn Collection Editor.
The following code binds the DataGrid to the Authors table. I have converted the
SQLServer table to an MS Access database to make testing easier.
public class WebForm1 : System.Web.UI.Page
{
protected ReflectionIT.Web.UI.WebControls.DataIslandGrid DataIslandGrid1;
private void
Page_Load(object sender, System.EventArgs
e)
{
string
connectString =
string.Format(
@"Provider=Microsoft.Jet.OLEDB.4.0;Password=;User
ID=Admin;Data Source={0};",
Server.MapPath("pubs.mdb"));
using
(OleDbConnection con
= new OleDbConnection(connectString)) {
con.Open();
using
(OleDbCommand cmd
= new OleDbCommand("select * from Authors", con)) {
OleDbDataAdapter
da =
new OleDbDataAdapter(cmd);
DataSet
ds =
new DataSet();
da.Fill(ds);
DataIslandGrid1.DataSet
= ds;
}
}
}
}
DataIslandGrid Control
The DataIslandGrid class is a WebControl with a lot of attributes. The ParseChilderenAttribute
is set to the Columns property. It enables a control to specify how the page parser
interprets nested elements within the control's tags when the control is used declaratively
on an ASP.NET page. Columns is a property of type BoundColumnsCollection
and holds items of the BoundColumn type
[ToolboxData("<{0}:DataIslandGrid runat=server></{0}:DataIslandGrid>")]
[ParseChildren(true, "Columns")]
[PersistChildren(false)]
[Designer(typeof(ReflectionIT.Web.UI.WebControls.Design.DataIslandGridDesigner))]
[DefaultProperty("Columns")]
public class DataIslandGrid
: System.Web.UI.WebControls.WebControl
The class has a set of public properties which allows you to configure it.
The real magic is done in the Render() and OnPreRender() methods:
- JavaScript code for client-side sorting is added to the Page.
- The DataTable from the DataSet is rendered to an Xml DataIsland using the WriteXml()
method of the DataSet.
- An Xml StyleSheet is rendered in a second Xml DataIsland. This StyleSheet is used
by the JavaScript to do the sorting. It uses a xsl:for-each loop with a
order-by attribute. The value of this attribute is set in the JavaScript.
- A <table> tag is rendered with a datasrc attribute which is set to
the ID of first XmlDataIsland
- For each Column a table cell (<td>) is rendered within a <thead> section.
In this Cell a hyperlink is rendered with a NavigateUrl property to the JavaScript
which does the client-side sorting. The Text of the hyperlink is set to the HeaderText
property.
- For each Column a table cell (<td>) is rendered within a <tbody> section.
In this Cell a <div> tag is rendered with a datafld attribute which
is set to the DataField property.
- A <tfoot> section is rendered with four hyperlinks for paging: MoveFirst,
MovePrevious, MoveNext and MoveLast.
- All tags are closed.
override protected
void OnPreRender(EventArgs
e) {
if (this.Page.Request.Browser.JavaScript
==
true) {
System.Text.StringBuilder s =
new System.Text.StringBuilder();
s.Append("\n<script type='text/javascript'
language='JavaScript'>\n");
s.Append("<!--\n");
s.Append("function sortColumn(xmldoc, xsldoc,
sortcol) {\n");
s.Append(" xsldoc.selectSingleNode(\"//xsl:for-each\").setAttribute(\"order-by\",
sortcol);\n");
s.Append(" xmldoc.documentElement.transformNodeToObject(xsldoc.documentElement,xmldoc);\n");
s.Append("}\n");
s.Append("// -->\n");
s.Append("</script>\n");
this.Page.RegisterClientScriptBlock("SortDataIslandGrid", s.ToString());
}
}
protected override
void Render(HtmlTextWriter
writer)
{
if (_dataSet !=
null)
{
string
oldNS = DataSet.Namespace;
DataSet.Namespace
= "";
writer.AddAttribute("id", "xml"
+ this.ClientID);
writer.RenderBeginTag("XML");
DataSet.WriteXml(writer,
XmlWriteMode.IgnoreSchema);
writer.RenderEndTag();
DataSet.Namespace
= oldNS
;
if
(this.AllowSorting) {
writer.AddAttribute("id", "xsl"
+ this.ClientID);
writer.RenderBeginTag("XML");
writer.RenderBeginTag(DataSet.DataSetName);
writer.AddAttribute("order-by",
"?");
writer.AddAttribute("select", DataTable.TableName);
writer.AddAttribute("xmlns:xsl",
"http://www.w3.org/TR/WD-xsl");
writer.RenderBeginTag("xsl:for-each");
writer.WriteBeginTag(DataTable.TableName);
writer.Write(HtmlTextWriter.TagRightChar);
foreach (DataColumn
col in DataTable.Columns) {
writer.WriteBeginTag(col.ColumnName);
writer.Write(HtmlTextWriter.TagRightChar);
writer.WriteBeginTag("xsl:value-of");
writer.WriteAttribute("select",
col.ColumnName);
writer.Write(HtmlTextWriter.SlashChar);
writer.Write(HtmlTextWriter.TagRightChar);
writer.WriteEndTag(col.ColumnName);
}
writer.WriteEndTag(DataTable.TableName);
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderEndTag(); }
}
bool
design = (this.Site
!= null
&& this.Site.DesignMode);
if (_dataSet !=
null |
design) {
writer.AddAttribute("datasrc", "#xml"
+ this.ClientID);
writer.AddAttribute("id", this.ClientID);
writer.AddAttribute("CellPadding", this.CellPadding.ToString());
writer.AddAttribute("CellSpacing", this.CellSpacing.ToString());
if
(this.AllowPaging) {
writer.AddAttribute("dataPageSize", PageSize.ToString());
}
if
(!this.BorderWidth.IsEmpty) {
writer.AddAttribute("border", BorderWidth.ToString());
}
if
(this.CssClass
!=
string.Empty)
{
writer.AddAttribute("class", CssClass);
}
if
(this.ControlStyleCreated
&& this.ControlStyle
!= null) {
ControlStyle.AddAttributesToRender(writer);
}
writer.RenderBeginTag("table");
writer.RenderBeginTag("thead");
writer.RenderBeginTag("tr");
foreach
(ReflectionIT.Web.UI.WebControls.BoundColumn bc
in _columns)
{
if (!bc.Width.IsEmpty) {
writer.AddAttribute("width",
bc.Width.ToString());
}
writer.RenderBeginTag("th");
if (this.AllowSorting &
bc.Sortable) {
this.WriteSort(writer,
bc.DataField, bc.HeaderText);
} else {
writer.Write(bc.HeaderText);
}
writer.RenderEndTag();
}
writer.RenderEndTag(); writer.RenderEndTag();
writer.RenderBeginTag("tbody");
int
t = design ? (AllowPaging ? PageSize :
4)
: 1;
for
(int x
= 0; x <
t; x++) {
writer.RenderBeginTag("tr");
foreach (ReflectionIT.Web.UI.WebControls.BoundColumn bc
in _columns)
{
bc.Render(writer);
}
writer.RenderEndTag(); }
writer.RenderEndTag();
if
(AllowPaging)
{
writer.RenderBeginTag("tfoot");
writer.RenderBeginTag("tr");
writer.AddAttribute("colspan",
_columns.Count.ToString());
writer.RenderBeginTag("th");
this.WritePaging(writer,
".firstPage();", "<<");
this.WritePaging(writer,
".previousPage();", "<");
this.WritePaging(writer,
".nextPage();", ">");
this.WritePaging(writer,
".lastPage();", ">>");
writer.RenderEndTag();
writer.RenderEndTag();
writer.RenderEndTag();
}
writer.RenderEndTag(); }
}
protected virtual void WritePaging(HtmlTextWriter
writer,
string function,
string text)
{
writer.AddAttribute("href",
"javascript:" + this.ClientID
+ function);
writer.RenderBeginTag("a");
writer.Write(HttpUtility.HtmlEncode(text));
writer.RenderEndTag();
writer.Write(HtmlTextWriter.SpaceChar);
}
protected virtual void WriteSort(HtmlTextWriter
writer,
string dataField,
string headerText) {
writer.AddAttribute("href",
"javascript:sortColumn(xml" +
this.ClientID +
".XMLDocument,
xsl" + this.ClientID +
".XMLDocument, '+" +
dataField + "');");
writer.RenderBeginTag("a");
writer.Write(HttpUtility.HtmlEncode(headerText));
writer.RenderEndTag();
writer.Write(HtmlTextWriter.SpaceChar);
}
You can configure the appearance of the DataGrid easily by assigning a stylesheet
classname to the CssClass property of the DataGrid and its BoundColumns. I have
also included a DataIslandGridDesigner class which renders the design-time html.
Links:
I have used the following articles to create this control and to write this article:
Conclusion
The Internet Explorer features for Data-Binding to Xml DataIslands are very powerful.
The DataIslandGrid control makes it easy to use them in a ASP.NET application. It
demonstrates the power of .NET controls.
Any suggestions and feedback for improving this article is most welcome. Send your
suggestions and feedback to Fons.Sonnemans@reflectionit.nl