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.