Blog

posts from 2003

Save your UserSettings in an Isolated Store

0 Comments
By Fons Sonnemans, 01-feb-2003

Download UserSettings.zip

Introduction

I like applications who remember my settings the next time I use it. This is an easy feature which is often forgotten. Most of the time because it is quite some work. With this article I want to help you with this and saving you a lot off work.

Then there is always the question 'where do we store the settings?'. I see a lot of applications using the Registry, ini-files or xml-files. All these solutions are causing a security risk. Especially when you want your application to be downloaded from the web using the 'no touch' deployment features of .NET.

Microsoft has solved this problem for me by introducing an isolated stores. With these stores, you can read and write data that less trusted code cannot access and prevent the exposure of sensitive information that can be saved elsewhere on the file system. Data is stored in compartments that are isolated by the current user and by the assembly in which the code exists.

The downloadable zipfile contains a UserSettingLibrary which can be used to store user settings into an isolated store.

IsolatedUserSettings class

The IsolatedUserSettings class can be used to store and retrieve user settings in and from an IsolatedStorageFile. The RegisterForm() method can be used to register a form. The location, size and windowstate of the form will be saved and restored. The SaveSetting() method saves an serializable object which can be restored using the GetSetting() method.


using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;

namespace ReflectionIT.UserSettings
{
    /// <summary>
    /// The IsolatedUserSettings class can be used to store and retrieve user settings
    /// in and from an IsolatedStorageFile.
    /// </summary>
    public class IsolatedUserSettings : IDisposable
    {
        private Hashtable _settings;
        privatebool_closed =false;
        privatestring_fileName;

        /// <summary>
        /// Constructor
        /// </summary>
        public IsolatedUserSettings(stringfileName)
        {
            _fileName=fileName;
            IsolatedStorageFile isoFile= GetStore();
            string[] files=isoFile.GetFileNames(fileName);
            if(files.Length == 1){
                // file exists -> Deserialize
                using(IsolatedStorageFileStream stream= new IsolatedStorageFileStream(
                         fileName, FileMode.Open, isoFile)){
                    BinaryFormatter formatter=new BinaryFormatter();
                    try{
                        _settings= (Hashtable)formatter.Deserialize(stream);
                    }
                    catch(SerializationException){
                        _settings= new Hashtable();
                    }
                }
            }else{
                // start fresh
                _settings= new Hashtable();
            }
        }

        /// <summary>
        /// ND Destructor
        /// </summary>
        ~IsolatedUserSettings() {
            Dispose(false);
        }

        /// <summary>
        /// Close the IsolatedUserSettings
        /// </summary>
        publicvoid Dispose(){
            Dispose(true);
            System.GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Close the IsolatedUserSettings
        /// </summary>
        /// <param key="disposing"></param>
        protectedvirtualvoid Dispose(bool disposing){
            this.Close();
        }

        /// <summary>
        /// Write the UserSettings to the IsolatedStorage
        /// </summary>
        publicvirtualvoid Close() {
            if(!_closed){
                IsolatedStorageFile isoFile= GetStore();

                using(IsolatedStorageFileStream stream= new IsolatedStorageFileStream(
                         FileName, FileMode.Create,isoFile)){
                    BinaryFormatter formatter=new BinaryFormatter();
                    formatter.Serialize(stream,_settings);
                }
                _closed= true;
            }
        }

        /// <summary>
        /// Get the fileName
        /// </summary>
        publicstring FileName {
            get{return _fileName;}
        }

        /// <summary>
        /// Return the Store
        /// </summary>
        /// <returns></returns>
        private IsolatedStorageFile GetStore(){
            return IsolatedStorageFile.GetStore(IsolatedStorageScope.Assembly |
                IsolatedStorageScope.User | IsolatedStorageScope.Domain |
                IsolatedStorageScope.Roaming, null,null);
        }

        /// <summary>
        /// Register a Form
        /// </summary>
        /// <param key="key"></param>
        /// <param key="form"></param>
        publicvirtualvoid RegisterForm(string key, Form form){
            FormSetting f;
            if(_settings.ContainsKey(key)) {
                f =(FormSetting)_settings[key];
                f.Restore(form);
            }else{
                f =new FormSetting(key);
                f.Backup(form);
                _settings.Add(key, f);
            }
            form.SizeChanged +=new EventHandler(f.FormMovedOrResized);
            form.LocationChanged +=new EventHandler(f.FormMovedOrResized);
        }

        /// <summary>
        /// UnRegister a Form
        /// </summary>
        /// <param key="key">Name of the Form. A forms with an existing key will
        /// automatically get the stored settings.</param>
        /// <param key="form"></param>
        publicvirtualvoid UnRegisterForm(string key, Form form){
            if(_settings.ContainsKey(key)) {
                FormSetting f =(FormSetting)_settings[key];
                f.Backup(form);
                form.SizeChanged -= new EventHandler(f.FormMovedOrResized);
                form.LocationChanged -= new EventHandler(f.FormMovedOrResized);
            }
        }

        /// <summary>
        /// Save a setting with a given key
        /// </summary>
        /// <param key="key">Name of the setting by which it is saved an can be
        /// restored using GetSetting().</param>
        /// <param key="setting"></param>
        publicvoid SaveSetting(stringkey,objectsetting){

            // Check Serializable attribute
            TypeAttributes attributes=setting.GetType().Attributes;
            if((attributes& TypeAttributes.Serializable)==0) {
                thrownew ArgumentException("Setting argument must be marked Serializable.");
            }

            // Add or Overwrite setting
            if(_settings.ContainsKey(key)) {
                _settings[key]= setting;
            }else{
                _settings.Add(key, setting);
            }
        }

        /// <summary>
        /// Return the setting with the given key
        /// </summary>
        /// <param key="key"></param>
        /// <returns>
        /// The setting object or null when the the given key does not exists
        /// </returns>
        publicobject GetSetting(stringkey){
            return GetSetting(key,null);
        }

        /// <summary>
        /// Return the setting with the given key
        /// </summary>
        /// <param key="key"></param>
        /// <returns></returns>
        publicobject GetSetting(stringkey,objectdefaultValue){
            if(_settings.ContainsKey(key)) {
                return_settings[key];
            }
            returndefaultValue;
        }
    }
}

The FormSetting class is used to store a form's location, size and windowstate in a serializable object. The next time you register a form with the same key the location, size and windowstate is restored to the saved values. So if you resize your form, close it and open it again the size will be the same as it was when you last closed it.

using System;
using System.Drawing;
using System.Windows.Forms;

namespace ReflectionIT.UserSettings
{
    /// <summary>
    /// Summary description for FormSetting.
    /// </summary>
    [Serializable]
    publicclass FormSetting
    {
        private Point _location;
        privatestring_name;
        private Size _size;
        private FormWindowState _windowState;

        public FormSetting(stringname)
        {
            this._name =name;
        }

        /// <summary>
        /// Get the name used for this FormSetting
        /// </summary>
        publicstring Name {
            get{return (this._name);}
        }

        /// <summary>
        /// Backup the Size, Location and WindowState (only when not Minimized)
        /// </summary>
        /// <param name="form"></param>
        publicvirtualvoid Backup(Form form){
            if(form.WindowState != FormWindowState.Minimized) {
                // Only when not Maximized
                if(form.WindowState != FormWindowState.Maximized){
                    this._location=form.Location;
                    this._size=form.Size;
                }
                this._windowState=form.WindowState;
            }
        }

        /// <summary>
        /// Eventhandler
        /// </summary>
        /// <param name="sender">Form which is Moved or Resized</param>
        /// <param name="e"></param>
        publicvirtualvoid FormMovedOrResized(object sender, EventArgs e){
            Backup((Form)sender);
        }

        /// <summary>
        /// Restore the Size, Location and WindowState for the given form
        /// </summary>
        /// <param name="form"></param>
        publicvirtualvoid Restore(Form form){
            form.Location = this._location;
            form.Size = this._size;
            form.WindowState =this._windowState;
        }
    }
}

Sample

The included sample uses the IsolatedUserSettings class to store and restore the user settings. In this case the Form (Location, Size and WindowState), the Checked property of a CheckBox and a MostRecentlyUsedList object. The settings are restored in the constructor and saved in the Closing event handler, the Form properties are saved automatically by the internally used FormSetting class. You must set the StartPosition property of the form to 'Manual', otherwise the location will not be restored.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using ReflectionIT.UserSettings;

namespace Test
{
    /// <summary>
    /// Summary description for Form1.
    /// </summary>
    public class Form1: System.Windows.Forms.Form
    {
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.Button buttonMRUAdd;
        private System.Windows.Forms.TextBox textBoxMRU;
        private System.Windows.Forms.ListBox listBoxMRU;

        private MostRecentlyUsedList _mru;
        private IsolatedUserSettings _settings =new IsolatedUserSettings("settings.dat");
        private System.Windows.Forms.CheckBox checkBox1;

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components=null;

        public Form1()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();

            // Get UserSettings
            _settings.RegisterForm(this.Name,this);
            _mru=(MostRecentlyUsedList)_settings.GetSetting("mru",new MostRecentlyUsedList(5));
            checkBox1.Checked = (bool)_settings.GetSetting("checked",true);
            listBoxMRU.DataSource = _mru;
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protectedoverridevoid Dispose(bool disposing)
        {
            if(disposing )
            {
                if(components!= null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing);
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        staticvoid Main()
        {
            Application.Run(new Form1());
        }

        /// <summary>
        /// Add the Text to the MRU and bind it to the datasource
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        privatevoidbuttonMRUAdd_Click(object sender, System.EventArgs e){
            _mru.Add(textBoxMRU.Text);
            textBoxMRU.Clear();
            textBoxMRU.Focus();

            listBoxMRU.DataSource = null;// force update
            listBoxMRU.DataSource =_mru;
        }

        /// <summary>
        /// Save the MRU and checkBox1.Checked to the UserSettings
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        privatevoid Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e){
            _settings.SaveSetting("mru",_mru);
            _settings.SaveSetting("checked",checkBox1.Checked);
            _settings.Close();
        }
    }
}

The MostRecentlyUsedList class has a Serializable attribute which makes it possible to store it in an IsolatedUserSetting.

 

using System;
using System.Collections;

namespace Test
{
   &nbsp/// <summary>
   &nbsp/// Summary description for MostRecentlyUsedList.
   &nbsp/// </summary>
   &nbsp[Serializable]
   &nbsppublicclass MostRecentlyUsedList : IList
   &nbsp{
   &nbsp   &nbspprivateint_capacity;
   &nbsp   &nbspprivate ArrayList _list;

   &nbsp   &nbsp/// <summary>
   &nbsp   &nbsp/// Initializes a new instance of the MostRecentlyUsedList class
   &nbsp   &nbsp/// that is empty and has the specified capacity
   &nbsp   &nbsp/// </summary>
   &nbsp   &nbsp/// <param name="capacity">The number of elements that the new
   &nbsp   &nbsp/// MostRecentlyUsedList is capable of storing. </param>
   &nbsp   &nbsppublic MostRecentlyUsedList(intcapacity)
   &nbsp   &nbsp{
   &nbsp   &nbsp   &nbsp_capacity=capacity;
   &nbsp   &nbsp   &nbsp_list=new ArrayList(capacity);
   &nbsp   &nbsp}

   &nbsp   &nbsp/// <summary>
   &nbsp   &nbsp/// Add a object to the beginning of the list. If the value already
   &nbsp   &nbsp/// exists it is removed first. The length of the list will never
   &nbsp   &nbsp/// exceed the capacity.
   &nbsp   &nbsp/// </summary>
   &nbsp   &nbsp/// <param name="value">The Object to be added to the beginning
   &nbsp   &nbsp/// of the MostRecentlyUsedList</param>
   &nbsp   &nbsppublicvoid Add(objectvalue)
   &nbsp   &nbsp{
   &nbsp   &nbsp   &nbspif(_list.Contains(value)){
   &nbsp   &nbsp   &nbsp   &nbsp_list.Remove(value);
   &nbsp   &nbsp   &nbsp}else{
   &nbsp   &nbsp   &nbsp   &nbspif(this.Count ==this.Capacity){
   &nbsp   &nbsp   &nbsp   &nbsp   &nbsp_list.RemoveAt(this.Count -1);
   &nbsp   &nbsp   &nbsp   &nbsp}
   &nbsp   &nbsp   &nbsp}
   &nbsp   &nbsp   &nbsp_list.Insert(0,value);
   &nbsp   &nbsp}

   &nbsp   &nbsp/// <summary>
   &nbsp   &nbsp/// Get or sets the number of elements that the MostRecentlyUsedList
   &nbsp   &nbsp/// is capable of storing.
   &nbsp   &nbsp/// </summary>
   &nbsp   &nbsppublicint Capacity {
   &nbsp   &nbsp   &nbspget{return (this._capacity);   &nbsp}
   &nbsp   &nbsp   &nbspset{
   &nbsp   &nbsp   &nbsp   &nbspthis._capacity=value;
   &nbsp   &nbsp   &nbsp   &nbspwhile(this.Count >=this.Capacity){
   &nbsp   &nbsp   &nbsp   &nbsp   &nbsp_list.RemoveAt(this.Count -1);
   &nbsp   &nbsp   &nbsp   &nbsp}
   &nbsp   &nbsp   &nbsp}
   &nbsp   &nbsp}
   &nbsp   &nbsp....

Conclusion

The IsolatedUserSettings is an simple class which uses an isolated store to serialize and deserialize objects. With it you can enhance your applications without much effort.

Any suggestions and feedback for improving this article is most welcome. Send your suggestions and feedback to Fons.Sonnemans@reflectionit.nl

READ MORE

NEW C# LANGUAGE FEATURES

0 Comments
By Fons Sonnemans, 10-jan-2003

On November 7th, at the OOPSLA Conference in Seattle, WA, C# creator Anders Hejlsberg unveiled several potential language features for the next major release of Visual C# .NET. The four primary features Anders spoke about were:

  • Generics, a form of C++ templates that makes reusing existing code easier
  • Iterators, a construct that makes traversing collections of data significantly faster and easier
  • Anonymous methods, an easier way to perform simple tasks using delegates
  • Partial types, a means for programmers to split code across multiple files
Read more

READ MORE

ASP.NET DataIslandGrid Control

0 Comments
By Fons Sonnemans, 01-jan-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.

ID Firstname Lastname Address City
<< < > >>
172-32-1176
Johnson
White
abc
Stein
213-46-8915
Marjorie
Green
abc
Oakland
238-95-7766
Rony
Carson
abc
Berkeley
267-41-2394
Michael
O'Leary
abc
San Jose
274-80-9391
Dean
Straight
abc
Oakland
341-22-1782
Meander
Smith
abc
Lawrence
409-56-7008
Abrahamfdfs
Bennett
abc
Berkeley
427-17-2319
Ann
Dull
abc
Palo Alto
472-27-2349
Burt
Gringlesby
abc
Covelo
486-29-1786
Charlene
Locksley
abc
San Francisco

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;

    privatevoid Page_Load(objectsender, System.EventArgs e)
    {
        stringconnectString=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);

                // Bind the DataGrid
                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")]
publicclass 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.
/// <summary>
/// Add a JavaScript to the Page to sort a column
/// </summary>
/// <param name="e"></param>
override protected void OnPreRender(EventArgs e){

     if(this.Page.Request.Browser.JavaScript == true){
         // Build JavaScript        
         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");

         // Add the Script to the Page
         this.Page.RegisterClientScriptBlock("SortDataIslandGrid", s.ToString());
     }
}

/// <summary>
/// Sends server control content to a provided HtmlTextWriter object, which writes
/// the content to be rendered on the client.
/// </summary>
/// <param name="writer">The HtmlTextWriter object that receives the server control content.</param>
protectedoverridevoid Render(HtmlTextWriter writer) {

     if(_dataSet!=null) {


         // XML
         stringoldNS= DataSet.Namespace;
         DataSet.Namespace ="";

         writer.AddAttribute("id","xml" +this.ClientID);
         writer.RenderBeginTag("XML");

         DataSet.WriteXml(writer, XmlWriteMode.IgnoreSchema);
         writer.RenderEndTag();//XML

         DataSet.Namespace =oldNS ;

         // XSL (sorting)
         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 colin 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(); //xsl:for-each"
             writer.RenderEndTag();

             writer.RenderEndTag(); //XML
         }
     }

     bool design=(this.Site !=null&&this.Site.DesignMode);
     if(_dataSet!=null| design){
         // Table
         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");

         // Header
         writer.RenderBeginTag("thead");
         writer.RenderBeginTag("tr");

         foreach(ReflectionIT.Web.UI.WebControls.BoundColumn bcin_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();// tr
         writer.RenderEndTag();// thead

         // body
         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 bcin_columns){
                 bc.Render(writer);
             }
             writer.RenderEndTag(); //tr
         }
         writer.RenderEndTag();// tbody

         // Footer
         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(); //th
             writer.RenderEndTag();//tr
             writer.RenderEndTag();//tfoot
         }

         writer.RenderEndTag();// table
     }
}

protectedvirtualvoid WritePaging(HtmlTextWriter writer, stringfunction,stringtext){
     writer.AddAttribute("href","javascript:"+this.ClientID +function);
     writer.RenderBeginTag("a");
     writer.Write(HttpUtility.HtmlEncode(text));
     writer.RenderEndTag();//a
     writer.Write(HtmlTextWriter.SpaceChar);
}

protectedvirtualvoid WriteSort(HtmlTextWriter writer, stringdataField,stringheaderText){
     writer.AddAttribute("href","javascript:sortColumn(xml"+this.ClientID +
         ".XMLDocument, xsl"+this.ClientID +".XMLDocument, '+"+dataField+"');");
     writer.RenderBeginTag("a");
     writer.Write(HttpUtility.HtmlEncode(headerText));
     writer.RenderEndTag();//a
     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

Tags: Web, ASP.NET

READ MORE

<12

All postings/content on this blog are provided "AS IS" with no warranties, and confer no rights. All entries in this blog are my opinion and don't necessarily reflect the opinion of my employer or sponsors. The content on this site is licensed under a Creative Commons Attribution By license.