Download as doc or pdf
Download as doc or pdf
You are on page 1of 14

Introduction

In many situations we want to bind a fully, in-place operational GridView with a


dynamically changing data source. This article shows how GridView can be templated
programmatically at run time and how is it possible to do edit, insert and delete
operations on it. Although just binding the GridView with some dynamically changing
datatsource is a simple and straight job which does not need templated GridView, it
cannot facilitate us with the aforementioned operations. To make the GridView
simultaneously an operational one, we need to template it dynamically which also
requires dynamically generated queries for corresponding operations according to the
GridView's data source.

It is useful in many applications especially those that require data manipulation


functionality of Enterprise Manager on client side i.e. displaying any table of any
database with options for all possible operations on client side. This solution provided in
this article will be the best and probably the only way to make it all possible. This article
shows how ITemplate is implemented effectively to achieve all of this.

Figure 1

How it works
First, we should know what a template is. Generally, a template is description of how a
particular item will be rendered at run time. It determines the layout and binding of
ASP.NET server control(s) contained by it. We may put this description at design time or
at run time according to our need. At design time we can define templates declaratively
using inline tags in aspx source of GridView (the following listing shows this). Since we
are going for a case where we don’t know the number of fields of a table and their
description (Name, Data Type etc) in advance therefore we need to create the templates
dynamically according to fields of the particular table. This is where the concept of
dynamically templated GridView comes. This is a GridView that can be bound to any
table of any database of any server, providing Insert, Edit and Delete option
simultaneously. As long as we know the name of fields in advance we don’t need ,even,
to know about ITemplate but to create templates dynamically we have to implement
ITemplate interface. We need to generate ItemTemplate and EditItemTemplate
dynamically for each field of the table, plus a field for buttons required for each
command. The former lets to specify how an item will look like in normal mode (usually
displayed in labels) while the later lets to specify how an item will change when it is put
into Edit mode (displayed in text boxes).

ITemplate has one method named InstantiateIn, its explanation will come later. The class
DynamicallyTemplatedGridViewHandler which implements this interface, exposes any
template property of GridView as its own property, hence the default layout of a template
is overridden.

The following listing shows how to create template fields at design time. It shows that we
must know number of fields and their name in advance to create their templates. This is
NOT required in our case.

Listing 1
<asp:GridView ID="TableGridView"
runat="server" AutoGenerateColumns="False" >
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:Label id="lblName" Runat="Server"
Text='<%# Eval("pub_name") %>'/>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox id="tbName" Runat="Server"
Text='<%# Bind("pub_name") %>'/>
</EditItemTemplate>
</asp:TemplateField>
....

</Columns
</asp:GridView>

I have implemented ITemplate in a separate class,


DynamicallyTemplatedGridViewHandler, for reusability and better understanding. I will
explain working and usage of this class since they play a key role in achieving all of this.

We can divide the whole working in two parts:


• Writing a class which implements ITemplate

• Using the class which implements ITemplate

Writing a class which implements ITemplate

Here is the skeleton of class DynamicallyTemplatedGridViewHandler implementing


ITemplate with a list of data members and methods.

Listing 2
public class DynamicallyTemplatedGridViewHandler : ITemplate
{
ListItemType ItemType;
string FieldName;
string InfoType;

public DynamicallyTemplatedGridViewHandler(ListItemType Item_type,


string field_name, string control_type);
public void InstantiateIn(System.Web.UI.Control Container);
private void OnDataBinding(object sender, EventArgs e);
}

This class has three data members:


ListItemType ItemType;
string FieldName;
string InfoType;

ItemType keeps the type of a list item type: Item, EditItem, Header, Footer,
AlternatingItem, Pager, SelecetdItem or Separator. In this demo version we need only
three of these; we need Header (For heading of each column), Item (for showing fields
when GridView will be in normal mode) and EditItem (for showing fields when
GridView will be in Edit mode).

FieldName keeps the name of each template field that will be displayed in the header.

InfoType keeps an indicator in string form for a type of information within a template
field i.e. whether a template field has information of "Command" or "String" so that later
data retrieval and data binding of that particular child control will be made accordingly. A
"Command" (Edit, Delete, and Insert) requires instantiation in the Button control while
he "String" requires a Label or TextBox.

Now, coming to the member methods listed above, there is a constructer which simply
sets the aforementioned data members with those passed as parameters.

Listing 3
public DynamicallyTemplatedGridViewHandler(ListItemType item_type, string field_name,
string control_type)
{
ItemType = item_type;
FieldName = field_name;
InfoType = info_type;
}
Here is the explanation of InstantiateIn the only method of ITemplate being implemented
by our class DynamicallyTemplatedGridViewHandler.

InstantiateIn

InstantiateIn ensures that the list item type of each template is created in its appropriate
control. For better understanding of functionality of this method see its name
"InstantiateIn." It means "Instantiate Item In Literal/Label/TextBox/Button/." The choice
will be according to the requirement. Like in the case of a Header, it is instantiated in
Literal control as shown in this part of the implementation of InstantiateIn.

InstantiateIn takes a "Container," a Control type object as a parameter. Container's control


collection is filled with all controls in which items of each type are instantiated. Its
implementation in the current scenario is a little lengthy, yet it is quite easy as we have to
do a similar type of job with each control; instantiate it, set its text property with
FieldName and add it into Container's control collection.

For example, below the code of InstantiateIn shows that if the ItemType is a Header, then
it creates a literal object called header_literal. After making it bold, set the text property
of header_literal with FieldName. Finally, add this control to the control collection of the
Container control passed as parameter to InstantiateIn method.

Similarly, we have to write instantiation code for ItemType if it is "Item" and "EditItem."
In case the ItemType is "Item" (fields look when GridView is in normal mode), we need
one more check inside it to see that InfoType tells whether the Item will be instantiated
with a Button (Edit, Insert, and Delete) or Label. If InfoType is a Button then it creates
three buttons for the aforementioned tasks. It is simple to do; create a button object, set its
all properties accordingly, also add the button's click event handler and finally, add it into
the control collection of the control (Container) passed as an argument.

Listing 4
public void InstantiateIn(System.Web.UI.Control Container)
{
switch (ItemType)
{
case ListItemType.Header:
Literal header_ltrl = new Literal();
header_ltrl.Text = "<b>" + FieldName + "</b>";
Container.Controls.Add(header_ltrl);
break;
case ListItemType.Item:
switch (InfoType)
{
case "Button":
ImageButton edit_button = new ImageButton();
edit_button.ID = "edit_button";
edit_button.ImageUrl = "~/images/edit.gif";
edit_button.CommandName = "Edit";
edit_button.Click += new ImageClickEventHandler(edit_button_Click);
edit_button.ToolTip = "Edit";
Container.Controls.Add(edit_button);
/*Similarly, add button for delete just set its
command to equal to "Delete." It is important to know when
"insert" button is added, its CommandName is set to "Edit" like
that of the "edi" button because we want the GridView to enter into
Edit mode and this time we also want the text boxes for corresponding fields
empty*/
ImageButton insert_button = new ImageButton();
insert_button.ID = "insert_button";
insert_button.ImageUrl = "~/images/insert.bmp";
insert_button.CommandName = "Edit";
insert_button.ToolTip = "Insert";
insert_button.Click += new ImageClickEventHandler(insert_button_Click);
Container.Controls.Add(insert_button);
default:
Label field_lbl = new Label();
field_lbl.ID = FieldName;
field_lbl.Text = String.Empty;
field_lbl.DataBinding += new EventHandler(OnDataBinding);
Container.Controls.Add(field_lbl);
break;
}
break;
case ListItemType.EditItem:
if (InfoType == "Button")
{
ImageButton update_button = new ImageButton();
update_button.ID = "update_button";
update_button.CommandName = "Update";
update_button.ImageUrl = "~/images/update.gif";
update_button.ToolTip = "Update";
update_button.OnClientClick =
"return confirm('Are you sure to update the record?')";
Container.Controls.Add(update_button);

// Similarly, add a button for Cancel

}
else
// if other key and non key fields then bind textboxes with texts
{
TextBox field_txtbox = new TextBox();
field_txtbox.ID = FieldName;
field_txtbox.Text = String.Empty;
// if to update then bind the textboxes with coressponding field texts
//otherwise for insert no need to bind it with text

if ((int)new Page().Session["InsertFlag"] == 0)
field_txtbox.DataBinding += new EventHandler(OnDataBinding);
Container.Controls.Add(field_txtbox);

}
break;
}
}

When InfoType is not a Command it means we have to instantiate it with a label as in


GridView's normal mode when each cell text of GridView's rows is displayed in label.
Therefore, by default, control is instantiated with Label and is added into the Container.
Since we have no more info types (other than command and string) then no further
checks are required. We come to the outer check when ItemType is EditItem. Now, we
need an inner check for info type (command or string). The template field will be
instantiated in Button if the info type is Command; otherwise it requires TextBox for the
cell text of edit item.

It is important to know that the CommandName of insert_button is set to "Edit" just to


take advantage of the Edit mode that provides text boxes for all editable items. If the
these text boxes are emptied, they can be used for insertion instead of editing without
dedicating an extra row for it. This is easy and handy as it becomes a better approach
when it is not known in advance how many columns there are in GridView's data source.
I have taken a session variable InsertFlag that is set to 0 and 1 for Edit and Inert
operations respectively.

Since we have to bind the labels in Item template and text boxes in EditItem template
with corresponding cell values, the data binding event handler OnDataBinding of both
label and text box populates the fields with cell values accordingly.
And you might also want to know how text boxes for each field get emptied when Insert
button is clicked. The solution is simple; do not bind them with a database and apply a
check while adding the data binding event handler of text box. Do not call
OnDataBinding if the insert button is clicked.

Listing 5
if ((int)new Page().Session["InsertFlag"] == 0)
field_txtbox.DataBinding += new EventHandler(OnDataBinding);

DataBinding Event Handler

The implementation of the data binding event handler "OnDataBindin"' is simple. First,
we get the "bound_value_object" that is returned by the static method Eval of DataBinder
class which takes two parameters. One is of type object called "data_item_container"
(containing the DataItem that is assigned with sender control's NamingConatiner) and
other is the string expression, FieldName. Once we get this bound_value_object, we
assign its value (string) to the Text property of Label (if ItemType is Item; for normal
mode) and TextBox (if ItemType is EditItem; for Edit mode).

Listing 6
private void OnDataBinding(object sender, EventArgs e)
{
object bound_value_obj = null;
Control ctrl = (Control)sender;
IDataItemContainer data_item_container =
(IDataItemContainer)ctrl.NamingContainer;
bound_value_obj = DataBinder.Eval(data_item_container.DataItem, FieldName);
switch (ItemType)
{
case ListItemType.Item:
Label field_ltrl = (Label)sender;
field_ltrl.Text = bound_value_obj.ToString();
break;
case ListItemType.EditItem:
TextBox field_txtbox = (TextBox)sender;
field_txtbox.Text = bound_value_obj.ToString();
break;
}
}

The implementation of ITemplate is complete, although I want to mention that there are
some event handlers, "insert_button_Click" and "edit_button_Click" for Insert and Edit
buttons respectively. They do nothing except the former sets the Session[InsertFlag] to 1
and the later sets it to 0.

Using the class which implements ITemplate

Now I will show you how to use the class DynamicallyTemplatedGridViewHandler


which implements ITemplate.

Add a new page to your project; in the demo it is default.aspx. Drop GridView control on
it. Name it TableGridView and set its AutoGenerateColumns property to false and add
the following event handlers to it.
• OnRowEditing

• OnRowCancelingEdit

• OnRowUpdating

• OnRowDeleting

The following aspx source shows this.

Listing 7
<asp:GridView ID="TableGridView"
OnRowEditing ="TableGridView_RowEditing"
OnRowCancelingEdit="TableGridView_RowCancelingEdit"
OnRowUpdating="TableGridView_RowUpdating"
OnRowDeleting="TableGridView_RowDeleting"
runat="server" AutoGenerateColumns="False" >
</asp:GridView>

The default.cs has method PopulateDataTable, CreateTemplatedGridView,


GenerateUpdateQuery, GenerateDeleteQuery, GenerateUpdateQuery and the
aforementioned event handlers.

Let us take a look at the most important method CreateTemplatedGridView. This method
is responsible for initializing the object of DynamicallyTemplatedGridViewHandler and
creating the templated GirdView. Before going into detail of this method I want to
mention the use of two global variables taken in _Default class

Public static DataTable Table- It stores the data source table that will be bound with the
dynamically templated GridView.

ArrayList ParameterArray- It keeps the new field values while editing or inserting the
fields of a particular row. It is to be used by the queries.

Defining CreateTemplatedGridView

This is an important method which shows how to use the class


DynamicallyTemplatedGridViewHandler. It first invokes the PopulateDataTable method,
initializes the Table data member and clears the column collection of the TableGridView
to avoid repetitive addition of columns on post backs. It initializes ServerName,
USerName, Password, DatabaseName and TableName with their corresponding Session
variables. Using the information on the aforementioned variables, it generates a
connection string and initializes the SqlConnection object "Connection." The
SQLDataAdapter object uses this connection object and select query for selecting all
rows from the desired table and populate Table using the Fill method of SqlDataAdapter.
The definition of PopulateDataTable is below.

Listing 8
void PopulateDataTable()
{
Table = new DataTable();
TableGridView.Columns.Clear();
string ServerName = (string)Session["Server"];
string UserName = (string)Session["UserName"];
string Password = (string)Session["Password"];
string DatabaseName = (string)Session["DatabaseSelected"];
string TableName = (string)Session["TableSelected"];
SqlConnection Connection = new System.Data.SqlClient.SqlConnection("
Data Source=" + ServerName + ";
Initial Catalog=" + DatabaseName + ";
User ID=" + UserName + ";
Password=" + Password + ";
Connect Timeout=120");
SqlDataAdapter adapter = new SqlDataAdapter("Select * from " + TableName,
Connection);
try
{
adapter.Fill(Table);
}
catch (Exception ex)
{
msg_lbl.Text = ex.ToString();
}
}

We have, so far, populated the data source which will be bound by the TableGridView
through invoking PopulateDataTable in CreateTemplatedGridView. Now, coming to the
definition of CreateTemplatedGridView, we have to template the GridView. It is
important to know that a TemplateField object has the following template properties that
should be set on need basis.

• HeaderTemplate

• ItemTemplate

• EditItemTemplate

• FooterTemplate

• InsertItemTemplate

• AlternatingItemTemplate

We need only the first three template properties as we implemented the ITemplate for
them only.

The first column of the TableGridView is basically command column. It only has buttons
for different operations on a row, which is the same for every table bound to the
TableGridView. Take the TemplateField object BtnTmpField and initialize it. Then set the
template properties (HeaderTemplate,ItemTemplate and EditItemTemplate) as the
following code shows.

Listing 9
void CreateTemplatedGridView()
{
//fill the table which is to be bound with the GridView
PopulateDataTable();
TemplateField BtnTmpField = new TemplateField();
BtnTmpField.ItemTemplate =
new DynamicallyTemplatedGridViewHandler(ListItemType.Item, "...", "Command");
BtnTmpField.HeaderTemplate =
new DynamicallyTemplatedGridViewHandler(ListItemType.Header, "...",
"Command");
BtnTmpField.EditItemTemplate =
new DynamicallyTemplatedGridViewHandler(ListItemType.EditItem, "...",
"Command");
TableGridView.Columns.Add(BtnTmpField);

....

These three template properties are initialized using the constructor of the
DynamicallyTemplatedGridViewHandler class, which sets the data members
ItemType, FieldName and InfoType with the arguments provided. You might have
noted that I have given no title for this field (it is just "'…") and you may give a common
title or whatever you want. I have set the InfoType with "Command" (that will be an
indicator for the InstantiateIn method to inform it about the type of information the
template field contains) which will definitely be instantiated in buttons i.e. Edit, Insert
and Delete in normal mode while Save and Cancel in Edit mode. Finally, add
BtnTemplateField in the column collection of the GridView as the last line of the code
shows. Now, we are finished with the first field of the dynamically templated GridView
which is common to any table being bound to the GridView.

Next, we have to traverse the table to get the name and type of each column (which we
do not know in advance) so it is passed to the constructor (second and third arguments for
column name and type respectively) for initializing the three template properties of each
field.

Listing 10

....
for (int i = 0; i < Table.Columns.Count; i++)
{
TemplateField ItemTmpField = new TemplateField();
// create the header
ItemTmpField.HeaderTemplate =
new DynamicallyTemplatedGridViewHandler(ListItemType.Header,
Table.Columns[i].ColumnName,
Table.Columns[i].DataType.Name);
// create ItemTemplate
ItemTmpField.ItemTemplate =
new DynamicallyTemplatedGridViewHandler(ListItemType.Item,
Table.Columns[i].ColumnName,
Table.Columns[i].DataType.Name);
// create EditItemTemplate
ItemTmpField.EditItemTemplate =
new DynamicallyTemplatedGridViewHandler(ListItemType.EditItem,
Table.Columns[i].ColumnName,
Table.Columns[i].DataType.Name);
// add to the GridView
TableGridView.Columns.Add(ItemTmpField);
}

....

After initializing the Header,ItemTemplate and EditItemTemplate for a field, we have to


add it to the column collection of the GridView. Now, all fields of the GridView have
been templated successfully and we have to assign the Table to the GridView's
DataSource property and bind it. Simply add the following two lines to the code.

Listing 11

....
TableGridView.DataSource = Table;
TableGridView.DataBind();
}

This finishes the definition of CreateTemplatedGridView and also completes the creation
of the templated GridView dynamically. This method has been invoked in Page_Load
only for post backs! This may seem a bit strange. Why is it done for all post backs and
not just the first time? Actually, at each post back a dynamically templated GridView
cannot be presented from ViewState so we have to create and bind it explicitly using this
method on all post backs.

Working with GridView Operations

Now let us take a look at the three operations (Edit, Insert and Delete) and their
corresponding event handlers one by one, which will help you better understand how this
demo works.

How Edit Operation works

When a user clicks on the pencil image button used for editing, the GridView enters into
Edit mode and the OnRowEditing event is fired that has been handled just to set the
EditIndex of the GridView to new EditIndex explicitly, bind it and then save the
NewEditIndex in session so that it can be used later when needed.

Listing 12
public void TableGridView_RowEditing(object sender, GridViewEditEventArgs e)
{
TableGridView.EditIndex = e.NewEditIndex;
TableGridView.DataBind();
Session["SelecetdRowIndex"] = e.NewEditIndex;
}

Figure 2
The figure above shows how it looks when the GridView enters into Edit mode. For
every field, data bound textboxes are displayed. The row now contains two buttons, an
Update (one with check image) and Cancel (one with cross image). When the user makes
desired changes and clicks the update button, OnRowUpdating event is fired. First, we
get all information needed to generate the connection string that includes the server name,
user name, and password and database name that are gotten from their corresponding
Session variable. Next, get the particular GridView row which is being edited so that the
new values of all fields of that row could be added in ArrayList ParameterArray; one we
have already taken for this purpose.

Listing 13
GridViewRow row = TableGridView.Rows[e.RowIndex];
for (int i = 0; i < Table.Columns.Count; i++)
{
string field = ((TextBox)row.FindControl(Table.Columns[i].ColumnName)).Text;
ParameterArray.Add(field);

Later we need these field values from ParameterArray to generate Update and Insert
Query accordingly. Since Insert Operation has been implemented through edit mode of
the GridView, it definitely uses the OnRowUpdating event. So, here we have to check
which operation is intended and whether the Insert or Edit button has been clicked. This
check is made using the flag value from the Session variable Session["InsertFlag"] as
shown in the following part of OnRowUpdating.

Listing 14

....
string Query = "";
if ((int)Session["InsertFlag"] == 1)
Query = GenerateInsertQuery();
else
Query = GenerateUpdateQuery();
SqlCommand Command = new System.Data.SqlClient.SqlCommand(Query, Connection);
try
{
Connection.Open();
Command.ExecuteNonQuery();
Session["InsertFlag"] = (int)Session["InsertFlag"] == 1 ? 0 : 1;
}
catch (SqlException se)
{
msg_button.Visible = true;
msg_lbl.Text = se.ToString();
}
TableGridView.EditIndex = -1;
CreateTemplatedGridView();

After the command runs successfully, Session[InserFlag] is reset. You may have noticed
that I have cancelled the GridView's Edit mode by setting its Edit Index equal to -1 which
brings the GridView back to the normal mode so that the changes could be viewable. We
have to call CreateTemplatedGridView after any manipulation on table through the
GridView as we again want the templated GridView to be created with updated
information. Simply setting the DataSource of the GridView and calling DataBind
method cannot work in this scenario.

It is important to know how the cancel button works. When clicked, the event
TableGridView_RowCancelingEdit gets fired which sets the EditIndex to -1, binds the
GridView and resets the index of the selected row in the session variable as shown below.

Listing 15
public void TableGridView_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
{
TableGridView.EditIndex = -1;
TableGridView.DataBind();
Session["SelecetdRowIndex"] = -1;

How Insert Operation works

When the Insert button is clicked, the GridView enters into Edit mode. However, this
time it has empty text boxes for all corresponding fields. It has been shown in the
definition of InstantiateIn. None of the event handlers specific to insert operation solely
have been implemented! It is just the utilization of the event handlers for edit operation,
but with common sense, which, as elders say, is not common! There is another point to be
noted here, it may be a small drawback, but it is negligible; the row for insertion replaces
the exiting row just visually and after insertion it reappears along with the newly inserted
one.

Figure 3
How Delete Operation works

When the Delete button is clicked a confirmation dialogue box appears (one also added
for Update button). It is a small piece of JavaScript code added in the button's
OnClientClick event. When user select "Ok" the OnRowDeleting event gets fired and the
delete query runs for that particular row.

Listing 16

....
string Query = GenerateDeleteQuery(e.RowIndex);
SqlCommand Command = new System.Data.SqlClient.SqlCommand(Query, Connection);
try
{
if(Connection.State==ConnectionState.Closed)
Connection.Open();
Command.ExecuteNonQuery();
}
catch (SqlException se)
{
msg_button.Visible = true;
msg_lbl.Text = se.ToString();
Connection.Close();
}
CreateTemplatedGridView();

First, the delete query is generated by invoking the GenerateDeleteQuery method which
takes the index of the row to be deleted as parameter. This index is used to find the
particular row's first field (being assumed that the Primary Key lies in first column) for
the parameter in WHERE clause for delete query. GenerateDeleteQuery uses this index as
shown below.
Listing 17

string query = "";


if (Table.Columns[0].DataType.Name == "String" || Table.Columns[0].DataType.Name == "DateTime")
query = "Delete from " + TableName + " where " + Table.Columns[0].ColumnName +
"='" + <span class=Bold>Table.Rows[index][0].</span>ToString() + "'";

Again, after any manipulation on the database through GridView, we need to call
CreateTemplatedGridView. We also have to do this after the deletion completes
successfully.

You might also like