Click here to Skip to main content
Email Password   helpLost your password?

Sample Image - DataGridTree.jpg

Introduction

The TreeGridDemo demonstrates a custom method for DataGrid paging. The DataGrid behaves much like a tree control, which collapses or expands sections of the grid rows. This way of paging is useful when the grid contains too many records and you have to render all the records in on page because ,for example, you need to display a summary underneath the grid like total or average or etc. By this way the user will be able to either see the summary or even open all or part of the sections of the grid.

Using the code

I use both server sdie and client side script to do this functionality.The client side script is responsible for showing and hiding a number of grid rows according to the number of rows per section.  Also, the script sets the current state of the section in hidden fields for server side state presesrvation. This is important beacause when the grid is bound to its datasource it loses its client state as a result of being rerendered. This is the key method of the client script:

//******************************************************************************************************** //CALLED EVERY TIME YOU COLLAPSE OR EXPAND THE GRID ITEMS //********************************************************************************************************

function ExpandCollapse(sender,gridClientID,rowIndex,numItemsPerPage) { var grid=document.getElementById(gridClientID); for(i=rowIndex+1;i<(rowIndex+numItemsPerPage+1);i++) { try { //SHOW OR HIDE THE ROW ACCORDING TO CURRENTSTATEgrid.rows[i].style.display=sender.value=='-'?'none':'block'; //SET THE HIDDEN VALUE FOR SERVER SIDE STATE PRSERVATION hiddenShowHideStatus=grid.rows[i].cells[0].children[0]; if(hiddenShowHideStatus&&hiddenShowHideStatus.type=='hidden') { hiddenShowHideStatus.value=sender.value=='-'?'none':'block'; } } catch(ex) { break; } } //CONVERT THE TEXT OF THE BUTTON CONTROL FROM + TO - ANDVICEVERSA sender.value=(sender.value=='-')?'+':'-'; }

In the server side I render separator rows in the PreRender event of the DataGrid. The separator rows contains two cells. One cell for rendering the button control that fires client side event for collapsing or expanding the grid sections, and the other cell is for section numbering ;"From x to y". The key method on the server side is below: 

private void treeGridToManage_PreRender(object sender, EventArgs e) { PopulateGridSeparators((DataGrid)sender); } private void PopulateGridSeparators(DataGrid treeGridToManage) { int pagerCellColSpan=0; int numItemsPPage=int.Parse(treeGridToManage.Attributes["NumItemsPPage"]); if(treeGridToManage.Items.Count>0) { pagerCellColSpan=treeGridToManage.Items[0].Cells.Count; int numSeparatorsCreated=0; for(int index=1;index<treeGridToManage.Items.Count+numSeparatorsCreated;index+=(numItemsPPage+1),numSeparatorsCreated++) { string clientText=""; clientText+="<INPUT TYPE =BUTTON ID=BTN"+index+" "; clientText+="VALUE=\"-\" "; clientText+="ONCLICK=\"javascript:ExpandCollapse(this,'"+treeGridToManage.ClientID+"',"+index+","+numItemsPPage+")\" "; clientText+="STYLE=\"BORDER-RIGHT: gray thin solid; BORDER-TOP: gray thin solid; BORDER-LEFT: gray thin solid; WIDTH: 25px; BORDER-BOTTOM: gray thin solid; HEIGHT: 28px\""; clientText+=">"; TableCell cell=new TableCell(); cell.Text=clientText; cell.Width=30; cell.BorderStyle=BorderStyle.Dotted; TableCell cell2=new TableCell(); cell2.ColumnSpan=pagerCellColSpan; cell2.HorizontalAlign=HorizontalAlign.Center; cell2.Text="From "+(index-numSeparatorsCreated)+" To "+(((index-numSeparatorsCreated)+numItemsPPage-1)); cell2.BorderStyle=BorderStyle.Dotted; DataGridItem item=new DataGridItem(0,0,ListItemType.Separator); item.BackColor=Color.LightGray; item.Cells.Add(cell); item.Cells.Add(cell2); treeGridToManage.Controls[0].Controls.AddAt(index,item); } } }

I have spoken about state preservation between the client and the server. I do this by holding hidden fields within the grid, these hidden fields holds the last state of every section on the client (wherther it is Collapsed or Expanded). I read the values of these hidden fields in an ArrayList before binding to the grid, after binding I set the formerly read values againg in the newly rendered hidden fields. When the  page is loaded on the client side I read the values of the hidden fields and set the state of the different sections of the grid according to hidden fields. In brief I do three action for state preservation:

  1. Dynamically add a template column to the datagrid  containing the hiden fields
    class DataGridTemplate : System.Web.UI.ITemplate
    {
        ListItemType templateType;
        string columnName;
        public DataGridTemplate(ListItemType type, string colname)
        {
            templateType    = type;
            columnName        = colname;
        }
        public void InstantiateIn(System.Web.UI.Control container)
        {
            // The Hidden Field Built Dynamically
    
            HtmlInputHidden hiddenShowHideStatus=new HtmlInputHidden();
            hiddenShowHideStatus.Value="none";
            hiddenShowHideStatus.ID="hiddenShowHideStatus";
            
            switch(templateType)
            {
                case ListItemType.Item:
                case ListItemType.EditItem:
                        container.Controls.Add(hiddenShowHideStatus);
                        break;
            }
        }
    }
    
            
  2. Read the hidden fields before calling grid.DataBind() and set the again in the newly rendered fields
    public void ManageGridBinding()
    {
        treeGridToManage.PreRender+=new EventHandler(treeGridToManage_PreRender);
    
        ArrayList stateList=new ArrayList();
        for(int i=0;i<treeGridToManage.Items.Count;i++)
        {
            HtmlInputHidden hiddenTreeGridState=(HtmlInputHidden)treeGridToManage.Items[i].Cells[0].FindControl("hiddenShowHideStatus");
            if(hiddenTreeGridState!=null)
            {
                stateList.Add(hiddenTreeGridState.Value);
            }    
        }
    
        treeGridToManage.DataBind();
           
        if(stateList.Count>0)
        {
            for(int i=0;i<stateList.Count;i++)
            {
                HtmlInputHidden hiddenTreeGridState=(HtmlInputHidden)treeGridToManage.Items[i].Cells[0].FindControl("hiddenShowHideStatus");
                if(hiddenTreeGridState!=null)
                {
                    hiddenTreeGridState.Value=stateList[i].ToString();
                }
            }
        }
    
    }
            
  3. Create a client side function executed on page load to set the current state of the grid
    this.onload=function()
    {
        grids=new Array('DataGrid1','DataGrid2');
        for(j=0;j<grids.length;j++) 
        {
            var grid=document.getElementById(grids[j]); 
            if(grid) 
            { 
                for(i=0;i<grid.rows.length;i++)
                {
                    hiddenShowHideStatus=grid.rows[i].cells[0].children[0];
                    if(hiddenShowHideStatus)
                    {
                        if(hiddenShowHideStatus.type=='hidden')
                        {
                            grid.rows[i].style.display=hiddenShowHideStatus.value=='none'?'none':'block';
                        }
                    }
                }
                SetButtonState(grid);
            }
        }
    };
            
            
            

Points of Interest

You donot have to worry about all of that code, all you have to do to get a grid behaving this way is just include TreeGridUIManager.cs in your project and this file contains all code required for the following tasks:

  1. Manages DataGrid Binding
  2. Handles Pre_Render event of the DataGrid to render separator rows

The DataGridTemplate class is Responsible for

  1. Dynamically creating template column
  2. Adding hidden fields to that column

The TreeGridScriptRenderer class is responsible for creating dynamic client side script 

To get one or more DataGrids behaving this way on your page do the following: 

  1. Include TreeGridUIManager.cs in your project
  2. when biding donot call dataGrid.DataBind but do the following

        
sqlDataAdapter1.Fill(dataSet11);
sqlDataAdapter2.Fill(dataSet11);

//Don't call datagrid.DataBind do the following:


//Create Instance

TreeGridUIManager _TreeGridUIManager=new TreeGridUIManager();

//Define Grids To Behave Like Tree passing section rowcount for every grid

_TreeGridUIManager.AddGridToManage(DataGrid1,5);
_TreeGridUIManager.AddGridToManage(DataGrid2,7);

//Call 

_TreeGridUIManager.DataBind();
        
        

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralTemplate column Value
FrancoisB
3:27 22 Jun '06  
I am having difficulty in retrieving an updated template column value when "looping" through the grid. A textbox is added to the ItemTemplate of the template column.

This is my code:
foreach(DataGridItem dgi in dbgInfoDetail.Items)
               {
               string txtAdjustment = ((TextBox)dgi.Cells[0].FindControl("txtAdjustment")).Text;


I tried different cell positions. It does not fail it just doen not return the updated value in the txtAdjustment textbox. The orginal value is retrieved before the update.

Any ideas.

Thanks

Francois
GeneralRe: Template column Value
FrancoisB
4:20 22 Jun '06  
I can see the databind event fires before I get the value. So how do I get the value before this happens?

Francois
GeneralRe: Template column Value
FrancoisB
4:57 22 Jun '06  
Please ignore stupid mistake!
GeneralRe: Template column Value
Ahmed Abdelgawad
0:36 24 Jun '06  
I am sorry for replying late.
For sorry you w'll not be able to get the updates!!!!
This happens because in every (PageLoad), I bind the grids again and again.
So when you update, before the update event fires, the <b>Page_Load </b>is executed and binds again, and overrides the changes you have recently done with the original value that is supposed to be changed, so you lose the change that was done.

So??

Actually, the normal scenario is:

<code>if(!IsPostBack)
{
      UpdateUI();
}</code>

And the question is:Why donot you do that?????
When I tried to do that, I lose a record between post backs!!,
to tell the truth, I couldnot figure out why does that happen.
To give you more information, if you are willing to fix that bug, the whole staff is in the <b>PreRender </b>event.

I hope to have time to update this version

Sorry for not helping you.





GeneralRe: Template column Value
Ahmed Abdelgawad
0:37 24 Jun '06  
I am sorry for replying late.
For sorry you w'll not be able to get the updates!!!!
This happens because in every (PageLoad), I bind the grids again and again.
So when you update, before the update event fires, the Page_Load is executed and binds again, and overrides the changes you have recently done with the original value that is supposed to be changed, so you lose the change that was done.

So??

Actually, the normal scenario is:

if(!IsPostBack)
{
UpdateUI();
}

And the question is:Why donot you do that?????
When I tried to do that, I lose a record between post backs!!,
to tell the truth, I couldnot figure out why does that happen.
To give you more information, if you are willing to fix that bug, the whole staff is in the PreRender event.

I hope to have time to fix this.

Sorry for not helping you.


Ahmed abdelgawad

GeneralCan this be done on a TreeView control?
Mad M0nk
23:26 18 Apr '06  
Ahmed,

I'm just starting out on ASP.NET and C#...

Is it possible to have a TreeView automatically collapse or expand when you select a specific branch?

Regards,

Confused
GeneralRe: Can this be done on a TreeView control?
Ahmed Abdelgawad
23:52 18 Apr '06  
Dear,
Of course there is a TreeView control that expands and collapses when you select a specific branch, but this control is a new control that came with asp.net 2.0, you can use it if you have visual studio 2005. Atually asp.net 2.0 came along with more than 50 new control!!, you can go to asp.net to get started, you'll find great resources there.

But, to know, the grid contol and the tree control have different functionality, beahviour and appearance . The tree control is used mainly to represent hierarchical structures like directory browsing or categorized items and so on. But the Grid is used to represent tabular data like a table that contains row with the same structure.

So when I created this control I thought about creating a mixture of both controls by making use of the tree collapsibility that makes the control use little or smaller space on my form.

I hope I could have helped you.



QuestionI am not able to select Datagrid Row
imnexensys
20:02 18 Apr '06  
I am not abe to select row from the grid and also i want to capture cell values from the datagrid is that possible if possible reply me how to do..WTF

imran
AnswerRe: I am not able to select Datagrid Row
Ahmed Abdelgawad
20:34 18 Apr '06  
Alsalamo 3lekom

Dear imran

Thankyou for being interested in my article,
Regarding Selectig a row:All you have to do is using the property builder to add a button column to your grid, then create an event handler to handle your grid "SelectedIndexChanged" event.

Regarding capturing cell values, I suppose you are willing to do that in the "SelectedIndexChanged" event handler. Just do the following:

If You are not using template columns use the following
string productID=DataGrid1.SelectedItem.Cells[3].Text;
If you are using template columns
do the following

string productID=((Label)DataGrid1.SelectedItem.Cells[3].FindControl("productLabel")).Text;




Ahmed Abdelgawad
QuestionRe: I am not able to select Datagrid Row
imnexensys
20:55 18 Apr '06  
Walaikum Assalaam

Thanks for quick response...
Is that not possible without inserting select button. i want to select row where ever in the grid i click...

Jazakallah Smile

Imran
AnswerRe: I am not able to select Datagrid Row
Ahmed Abdelgawad
21:02 18 Apr '06  
I cannot get your question, do you mean selecting a row like selecting in your email via a check box, or sth?
GeneralRe: I am not able to select Datagrid Row
imnexensys
21:14 18 Apr '06  
Yes like email selection. Previouly u have suggested me to add select button from the property builder but without inserting select button from the property builder i should select entire row as well capture cell values from the grid.
GeneralRe: I am not able to select Datagrid Row
Ahmed Abdelgawad
22:17 18 Apr '06  
Dear Imran

I have sent you an email containg a solution that contains the functionality you need , in the demo i have added check box control that you can use in your code to s[ecify the selected rows

If this is waht you need and if you liked it, plz tell me


GeneralRe: I am not able to select Datagrid Row
imnexensys
0:57 19 Apr '06  
Hey
thats a briliant piece of work
thanks for sharing

5 globes from me Smile

Jazakallah
imran
Generalyou can add expand allll / collapse all
eng_asem78
21:39 10 Apr '06  
you can add option to expand all or collapse all to the DataGrid items.

asem


Last Updated 10 Apr 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010