| Introduction
The DataGrid Web server control is a powerful
tool for displaying information from a data source. It is
easy to use; you can display editable data in a professional-looking
grid by setting only a few properties. At the same time, the
grid has a sophisticated object model that provides you with
great flexibility in how you display the data.
This article addresses some of the questions
about customizing grid display that are commonly asked in
newsgroups, on Web sites, and in other developer forums. The
techniques described here are sometimes quite simple and at
other times somewhat involved. In each case, however, they
address a question of how to go beyond the basic functionality
of the DataGrid control.
This articles assumes that you are already
familiar with the control — how to add it to a form and configure
it to display data. You should also understand how to put
a row in the grid into edit mode and other basic tasks. For
more details on DataGrid Web Server Control,
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbconDataGridWebControl.asp
The Web Forms DataGrid control is not the
same as the Windows Forms equivalent. It is a common (and
not unreasonable) assumption that they are the same control,
or at least have identical functionality. However, the entire
programming paradigm for Web Forms is quite different from
that for Windows Forms. For example, Web Forms pages perform
a round trip to the server for any processing; they must manage
state; they feature a very different data-binding model; and
so on. This articles addresses only the issues with
Web Forms DataGrid Control.
The Top Four Questions that are addressed
in this articles are,
1. Editing and Inserting Multiple Rows at Once.
2. Manipulating DataSource Values while binding to DataGrid
3. DropDownList in Editable DataGrid
4. Showing and Hiding Columns dynamically
Editing And Inserting Multiple
Rows at Once
The standard way to edit rows in the DataGrid
control — by adding an "Edit, Update, Cancel" button
to the grid's columns — only allows users to edit one row
at a time. If users want to edit multiple rows, they must
click the Edit button, make their changes, and then click
the Update button for each row.
In some cases, a useful alternative is to
configure the grid so that it is in edit mode by default.
In this scenario, the grid always displays editable data in
text boxes or other controls; users do not explicitly have
to put the grid into edit mode. Typically, users make whatever
changes they want and then click a button (not a button in
the grid) to submit all changes at once. The page might look
something like the following:

To configure the grid for multiple-row edit,
add the columns as you normally would and convert all editable
columns to template columns. In the Columns tab of the grid's
Property Builder, select the column and at the bottom of the
window, choose Convert this column into a Template column.
To edit the templates, right-click the grid and choose Edit
Template.
Add the edit controls to the ItemTemplate.
Note that you are not adding them to the EditItemTemplate,
as you normally would, because the rows will not be displayed
in edit mode. That is, the ItemTemplate will contain editable
controls.
Set up data binding for the grid normally.
You will need to bind each editable control individually.
A typical data binding expression will look like this:
|
Text='<%# Container.DataItem("Product")
%>' |
| |
Code
for DataGrid will be like this, |
| |
|
<asp:DataGrid id="DataGrid1"
style="Z-INDEX: 101; LEFT: 336px;" runat="server">
<Columns>
<asp:TemplateColumn
HeaderText="Product Name">
<ItemTemplate>
<asp:TextBox id="ProductName" runat="server"
Text =
'<%#Container.DataItem("ProductName") %>'>
</asp:TextBox>
</ItemTemplate>
....
....
</asp:TemplateColumn>
</Columns>
</asp:DataGrid> |
In this example, it is assumed that DataSource
for DataGrid is DataSet. DataSet is created from the XML File.
So after doing all the operations, it is saved back to Database.
During Save operation with the help of DataKeyField property,
we are able to identify whether this record has to be inserted
or updated. Since DataKeyField is binded to Primary Key, if
it is null then that record has to inserted otherwise we need
to update that record.
|
Private Sub Save_Click(ByVal sender As
System.Object, ByVal e As System.EventArgs) Handles
Save.Click
Dim di As DataGridItem
Dim oDataRowcol(1)
As DataRow
Dim dr As DataRow
'Reading the data from dataSource
oDataSet.ReadXml("d:\product.xml")
' Loop through the items in the datagrid.
For Each di In DataGrid1.Items
' Make sure this is an item and not the header or footer.
If di.ItemType = ListItemType.Item OrElse di.ItemType
=
If di.ItemType = ListItemType.Item OrElse di.ItemType
=
ListItemType.AlternatingItem Then
' Get the current row for update or insert operations
later.
If DataGrid1.DataKeys(di.ItemIndex).ToString = ""
Then
dr = oDataSet.Tables("Product").NewRow
' Insert the row instead.
dr("Name") = CType(di.FindControl("ProductName"),
TextBox).Text
dr("Price") = CType(di.FindControl("ProductPrice"),
TextBox).Text
dr("Quantity") = CType(di.FindControl("Quantity"),
TextBox).Text
dr("ProductID") = CType(di.FindControl("ProductID"),
TextBox).Text
oDataSet.Tables("Product").Rows.Add(dr)
Else
oDataRowcol = oDataSet.Tables("Product").Select("ProductID
=" &
DataGrid1.DataKeys(di.ItemIndex))
' Update the row instead.
dr = oDataRowcol(0)
dr("Name") = CType(di.FindControl("ProductName"),
TextBox).Text
dr("Price") = CType(di.FindControl("ProductPrice"),
TextBox).Text
dr("Quantity") = CType(di.FindControl("Quantity"),
TextBox).Text
dr("ProductID") = CType(di.FindControl("ProductID"),
TextBox).Text
End If
End If
Next
' Save the changes if
there is to database or to any datasource.
'In this example i am storing it back to XML File
oDataSet.WriteXml("d:\Product.xml")
End Sub |
Checking for Changed Items
One drawback of the above method of updating
and inserting records is that it is very inefficient
to send updates to the DataSet or database for each grid row
if there have been only few changes. If you are working with
a DataSet, you can add logic to check for changes between
the controls in the grid and the corresponding columns in
DataSet rows. If you are not using a DataSet — as in the example
above — you cannot easily make this comparison, since it would
involve a round trip to the database.
A strategy that works for both types of data
sources is to establish a way to determine whether rows are
"dirty" so you can check that before making an update.
The definitive way to determine whether a row has been dirtied
is to handle the changed event for the controls in a row.
For example, if your grid row contains a TextBox control,
you can respond to the control's TextChanged event. Similarly,
for check boxes, you can respond to a CheckedChanged event.
If we have one hidden field for each
row in the DataGrid. Then in the handler of these changed
events of controls, we can mark that row has changed by changing
the value of hidden item. For example if you wants to implement
this method, Then create a handler to do this stuff. The following
code shows a handler that can be invoked when a TextBox control
raises its TextChanged event or when a CheckBox control raises
its CheckedChanged event:
|
Sub DGRowChanged(ByVal
sender As Object, ByVal e As System.EventArgs)
Dim oControl As Control
Dim oGenControl As System.Web.UI.HtmlControls.HtmlInputHidden
oControl = CType(sender, Control)
oGenControl = oControl.Parent.FindControl("RecordState")
oGenControl.Value = "Changed"
End Sub |
It is helpful to understand that change events
do not, by default, post the page back to the server. Instead,
the event is raised only when the page is posted some other
way (usually via a Click event). During page processing, the
page and its controls are initialized, and then all change
events are raised. Only when the change event's handlers have
finished is the Click event raised for the control that caused
the post.
On to the RowChanged method illustrated above.
The code will update that hidden item (RecordState) to “Changed”
. This indicates that this record has been changed.
Now the code for save operation will be like
this and this method only updates the changed records only.
Basically in this method while looping through DataGrid Items
we are checking whether that record is changed by checking
that hidden field. Then if that record is changed, then do
insert or update accordingly.
|
Private Sub Save_Click(ByVal sender As
System.Object, ByVal e As System.EventArgs) Handles Save.Click
Dim di As DataGridItem
Dim oDataRowcol(1) As DataRow
Dim dr As DataRow
'Reading the data from dataSource
oDataSet.ReadXml("d:\product.xml")
' Loop through the items in the datagrid.
For Each di In DataGrid1.Items
' Make sure this is an item and not the
header or footer.
If di.ItemType = ListItemType.Item
OrElse di.ItemType =
ListItemType.AlternatingItem Then
' Get the current
row for update or insert operations later.
If CType(di.FindControl("RecordState"),
HtmlInputHidden).Value = "Changed" Then
If DataGrid1.DataKeys(di.ItemIndex).ToString = ""
Then
'Your code for insert goes here
Else
'Your code for update goes here
End If
End If
End If
Next
' Save the changes if there is
to database or to any datasource.
'In this example we are storing
it back to XML File
oDataSet.WriteXml("d:\Product.xml")
End Sub |
One task is left: binding the handlers to
the control events. In Visual Studio, you can only do this
in HTML view. The controls are not explicitly instantiated
in the code-behind file, so they are not supported by the
code tools. Switch the .aspx file to HTML view and in the
declarative elements for each of the controls, add the following
highlighted syntax:
|
<asp:TemplateColumn
headertext="Product Name">
<ItemTemplate>
<asp:TextBox id="ProductName" runat="server"
OnTextChanged="DGRowChanged"
Text='<%# Container.DataItem("Name") %>'>
</asp:TextBox>
</ItemTemplate>
</asp:TemplateColumn>
|
All the controls can call the same method
from their respective change methods, because the signature
for both event handlers is the same. That would be true also
if you had a list box or drop-down list control, whose SelectedIndexChanged
events likewise pass the same arguments
Manipulating DataSource Values while binding
to DataGrid
In many situations it is required for us to
manipulate the DataSource values while binding to the DataGrid.
For example you might store ‘Y’ and ‘N’ as Boolean values
in database. But in DataGrid for the properties like Enabled
or Checked, it requires Boolean value to be in “True” or “False”
format. In this case while binding to DataGrid, you need to
change these database values to DataGrid values accordingly.
You can do this type of manipulation in two ways.
One way is to write handler for ItemDataBound.
The main drawback in this approach is, ItemDataBound
event handler is common to all the DataGridItems. So you need
to find out exactly the DataGrid Item in which you needs to
do manipulation. Then in that DataGrid Item you need to find
out the columns for which you needs to do manipulation. This
drawback can be overcome in the other method.
Other way to do this is by writing a function
in code behind file, then calling that function while binding
in datagrid column by passing the DataSource value as its
parameter. Assume that, you wants to enable or disable a “Order”
radiobutton in DataGrid based on “Orders Date” column in database.
If orders date is not null then you need to enable this radio
button otherwise you need to disable this button.
|
<asp:TemplateColumn
headertext="Select Orders">
<ItemTemplate>
<asp:RadioButton id="RadioButton1" runat="server"
Enabled = '<%#
ReturnOrders(DataBinder.Eval(Container.DataItem, "OrdersDate"))
%> '>
</asp:RadioButton>
</ItemTemplate>
</asp:TemplateColumn> |
In the above DataGrid Code, While binding
the value of Enabled property of radio button a function
named “ReturnOrder” is called by passing the OrdersDate as
one of its parameter. This function will return Boolean value
depending upon the OrdersDate. Depending upon this Boolean
value this radio button will be enabled or disabled for that
record.
|
Public
Function returnSelect(ByVal OrdersDate As String) As
Boolean
If OrdersDate Is DBNull.Value Then
Return True
End If
Return False
End
Function |
This is just an simple example on how to manipulate
the DataSource values while binding using this method. But
you can do use this method in lots of place.
DropDownList in Editable DataGrid
A common request is to present users with
a drop-down list when a row is in edit mode. For example,
the grid might show a list of orders, including Product Name.
When users edit a book record, they might want to assign a
different Product; ideally, they can select from a drop-down
list that shows possible Products values such as "Onida
TV," "LG TV" or "X TV."
Displaying a drop-down list requires a template
column in the grid. Typically, the ItemTemplate contains a
control such as a data-bound Label control to show the current
value of a field in the record. You then add a drop-down list
to the EditItemTemplate. In Visual Studio, you can add a template
column in the Property builder for the grid, and then use
standard template editing to remove the default TextBox control
from the EditItemTemplate and drag a DropDownList control
into it instead. Alternatively, you can add the template column
in HTML view.
After you have created the template column
with the drop-down list in it, there are two tasks. The first
is to populate the list. The second is to preselect the appropriate
item in the list — for example, if the product name is set
to X TV," in a order record. when the drop-down list
displays, you often want "X TV" to be preselected.
There are many ways to populate the drop-down
list like using static items; using records from a dataset;
or by using a data reader to read information directly from
a database. In this article we are going to see only using
records from a Dataset
Populating DropDownList Using DataSet
If the data you want to display in the DropDownList
is a DataSet, you can use simple binding method. The
following shows the declarative syntax. The DropDownList is
bound to Product table in DataSet called oDataSet and
ProductId and ProductName are columns in Product Table.
|
<asp:TemplateColumn
HeaderText="Product Name">
<ItemTemplate>
<asp:Label id=Label3 runat="server"
Text='<%# DataBinder.Eval(Container, "DataItem.ProductName")
%>'>
</asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList id=DropDownList4 runat="server"
DataSource="<%# oDataSet %>" DataMember="Product"
DataTextField="ProductName" DataValueField="ProductID"
>
</asp:DropDownList>
</EditItemTemplate>
</asp:TemplateColumn> |
Preselecting an Item in the Drop-Down List
You often want to set the selected item in
the drop-down list to match a specific value, usually the
value displayed in the cell in display mode. You can do this
by setting the SelectedIndex property of the drop-down list
to the index of the value to display.
The following example shows a reliable way
to do this in a handler for the DataGrid item's ItemDataBound
event. This is the correct event to use, because it guarantees
that the drop-down list has already been populated, no matter
what data source the drop-down list is using.
The trick is in knowing what value to set
the drop-down list to. Typically, the value is already available
to you either in the current item (being displayed) or in
the DataItem property of the current item, which returns a
DataRowView object containing the current record. Once you
have the value, you can use the DropDownList control's FindByText
or FindByValue method to locate the correct item in the list;
Then set its selected property to True.
|
Private
Sub DataGrid1_ItemDataBound(ByVal sender As Object,
_
ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs)
_
Handles DataGrid1.ItemDataBound
If e.Item.ItemType = ListItemType.EditItem Then
Dim drv As DataRowView = CType(e.Item.DataItem, DataRowView)
Dim currentProduct As String = CType(drv("ProductName"),
String)
Dim ddl As DropDownList
ddl = CType(e.Item.FindControl("DropDownList1"),
DropDownList)
ddl.Items.FindByText(currentgenre).Selected = True
End If
End
Sub |
Showing and Hiding Columns Dynamically
One way to have columns appear dynamically
is to create them at design time, and then to hide or show
them as needed. You can do this by setting a column's Visible
property. The following example shows how to toggle the visibility
of the second column (index 1) of the grid:
|
DataGrid1.Columns(1).Visible
= Not (DataGrid1.Columns(1).Visible) |
|