Thursday, July 24, 2008

How to build a Grid View dynamically ?

Today I will show you something pretty standard - how to build a asp:GridView when you don't know in advance how it will be formatted, what Columns should be bounded...
We have to build the GridView’s structure – it’s Columns. We can see that GridView.Columns.Add method takes a DataControlField as a parameter, so our attention should go to Controls inheriting DataControlField so that we can add them to the Columns collection.

There are two kinds of columns we can use - a BoundField and a TemplateField.

The first one – a BoundField , is easy to create and use. We can set properties like DataField,HeaderText, HeaderStyle, ReadOnly and so on. But we can’t set a template. This control should be used when adding a very simple row, which won’t be edited or no complex formatting will be applied to it. It’s the easiest way to add a new column, but it’s not flexible enough for all we can need. So, BoundField should be used when we don’t want anything complex from the Column.

The TemplateField on the other hand is powerful.

namespace System.Web.UI.WebControls
{
public class TemplateField : DataControlField
{
public TemplateField();

public virtual ITemplate AlternatingItemTemplate { get; set; }
public virtual bool ConvertEmptyStringToNull { get; set; }
public virtual ITemplate EditItemTemplate { get; set; }
public virtual ITemplate FooterTemplate { get; set; }
public virtual ITemplate HeaderTemplate { get; set; }
public virtual ITemplate InsertItemTemplate { get; set; }
public virtual ITemplate ItemTemplate { get; set; }
protected override DataControlField CreateField();
public override void ExtractValuesFromCell(IOrderedDictionary dictionary, DataControlFieldCell cell, DataControlRowState rowState, bool includeReadOnly);
public override void InitializeCell(DataControlFieldCell cell, DataControlCellType cellType, DataControlRowState rowState, int rowIndex);
public override void ValidateSupportsCallback();
}
}

We can see from it’s class that it contains Properties for all the possible Templates (EditItemTemplate, ItemTemplate, InsertItemTemplate and etc.) . All templates should implement the ITemplate Interface , which is simple enough – it contains only one method – InstantiateIn(Control container)

namespace System.Web.UI
{
public interface ITemplate
{
void InstantiateIn(Control container);
}
}

In this method we should add whatever we like in the container. This method is called for every cell of the GridView, so the container is actually a DataControlField (which is not so important for now, except the fact that should be noted – the DataControlField is not the NamingContainer, but the GridViewRow is).
Now, for example , let’s create a class for ItemTemplate. Here is a sample code for an ItemTemplate:
public class CustomTemplateField : ITemplate
{
public CustomTemplateField()
{
}

public void InstantiateIn(Control container)
{
Label label = new Label();
label.DataBinding += new EventHandler(label_DataBinding); // use the DataBinding event to
// set the data to the label

container.Controls.Add(label); // Simply add the control to the Controls
// collection of the container

}

void label_DataBinding(object sender, EventArgs e)
{
Label lbl = sender as Label; // Get the label
GridViewRow gvr = lbl.NamingContainer as GridViewRow; // And get it's naming container
string text = DataBinder.Eval(gvr.DataItem, "Name").ToString(); // We can now use the DataItem
// to bind some data to our
// Controls in the cell

lbl.Text = text; // we can of course here perform some formatting
}
}

Any other Template’s implementation is similar to the above’s one. If we want to provide edit functionality, we should make an EditItemTemplate, for example like this one:
public class EditItemTemplate : ITemplate
{
public EditItemTemplate()
{
}

public void InstantiateIn(Control container)
{
TextBox txt = new TextBox();
txt.ID = "TextBoxID";
txt.DataBinding += new EventHandler(txt_DataBinding);
}

void txt_DataBinding(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
GridViewRow gvr = sender as GridViewRow;
tb.Text = DataBinder.Eval(gvr.DataItem, "Name").ToString();
}
}

So it’s pretty easy to create dynamically a GridView with TemplateFields. And that’s all we needed to build dynamically a GridView !

What's next ?
In the next post, I will show you how we can handle the edit operation on such GridView and how to use it's ViewState.

5 comments:

Vesko Kolev said...

Welcome to the blogsphere!
The community needs guys like you blogging frequently!

Good luck, man!

Павелъ Дончевъ said...

Hey, Yosif! I am very happy that you finally decide to share your knowledge with us:) I am pretty sure you have a lot of things to share and we are waiting for them!

Good Luck from me too!

cypressx said...

Thanks guys!
Your comments are very encouraging !:)

Miranda said...

Hi, Yosif...
I'm using a grid view dynamically I have some TemplateFields, and after a postback, the TemplateField based columns lose their controls and data

cypressx said...

Hi, Miranda.

I have posted something about this problem, too: http://yyosifov.blogspot.com/2008/08/persisting-dynamically-built-gridviews.html

Basically, you should rebuild all your dynamic columns after page postback. It's not very convenient, but the only way.