The Problem
So many articles explain
all the various ways of getting your information into a DataGrid. This
article will explain how to get information out of your datagrid.
It is not exactly clear how
to get information out of your DataGrid once it is bound and filled. So,
how do I read the contents of a particular column when the user selects an item?
The Setup
My page has a DataGrid called dgAges, a Label
called lblName, and another Label called lblAge.
It has a Select column, a Bound column (Name), and a Template column (Age).
...
<asp:DataGrid
id="dgAges"
runat="server"
AutoGenerateColumns="False"
OnSelectedIndexChanged="
SelectionChanged">
<Columns>
<asp:ButtonColumn Text="Select"
HeaderText="" CommandName="Select" />
<asp:BoundColumn DataField="Name"
HeaderText="Name" />
<asp:TemplateColumn HeaderText="Age">
<ItemTemplate>
<%#
Container.DataItem("Age") %> yrs. old
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
<P> </P>
<P><STRONG><U>Current Selection:</U></STRONG></P>
<P><STRONG>Name:</STRONG> <asp:Label id="lblName"
runat="server"></asp:Label></P>
<P><STRONG>Age:</STRONG> <asp:Label id="lblAge"
runat="server"></asp:Label></P>
... |
What I want to do is set the
Text property of the Labels to the respective values when a row is selected.
The OnSelectedIndexChanged property of dgAges
specifies that the SelectionChanged sub routine
will run when an item is selected.
The BoundColumn vs. The TemplateColumn
The first thing you'd think to try is to use a cell's Text property to get
whatever text is there. This works only for the Bound Column:
...
Protected Sub SelectionChanged()
'Bound Column... This works
lblName.Text = dgAges.SelectedItem.Cells(1).Text
'Template Column... This doesn't work
lblAge.Text =
dgAges.SelectedItem.Cells(2).Text
End Sub
... |
That is because .NET sees the
contents of the BoundColumn as Text and the contents of the TemplateColumn as
a DataBoundLiteralControl. In fact, .NET
always sees the contents of Template columns as a collection of server controls.
To correctly set lblAge's Text, you'd have to
acess the DataBoundLiteralControl's Text property.
Each cell has a collection of Controls that we can access. Keep in mind
that this collection returns an object of type Control, so you will have to
cast it to the type you want to use; in this case a DataBoundLiteralControl.
Here's how it is done:
...
Protected Sub SelectionChanged()
'Bound Column... This works
lblName.Text = dgAges.SelectedItem.Cells(1).Text
'Template Column... This works
lblAge.Text =
CType(dgAges.SelectedItem.Cells(2).Controls(0), DataBoundLiteralControl).Text
End Sub
... |
Don't be discouraged if you're
thinking "I would never have guessed DataBoundLiteralControl."
The important thing is to know how it works. Now we know .NET puts the
contents of Template columns in the Controls collection of each cell, and we
know to cast the controls to the types we need to utilize all their methods
and properties.
Note that the Template column
isn't always going to have a DataBoundLiteralControl.
Obviously, if you have a control in your template (say a TextBox in an EditItemTemplate),
that particular control will be in the Controls collection of that cell.
The Better Setup
Knowing what I know now, I
would have set up the whole page a little differently. First of all, I
would have used a Label control in my Template column, so I know what I'm getting
instead of having to guess or otherwise figure out that it is a DataBoundLiteralControl:
...
<asp:DataGrid
id="dgAges"
runat="server"
AutoGenerateColumns="False"
OnSelectedIndexChanged="
SelectionChanged">
<Columns>
<asp:ButtonColumn Text="Select"
HeaderText="" CommandName="Select" />
<asp:BoundColumn DataField="Name"
HeaderText="Name" />
<asp:TemplateColumn HeaderText="Age">
<ItemTemplate>
<asp:Label
RunAt="server" ID="lblThisAge" Text='<%# Container.DataItem("Age")
%>' /> yrs. old
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
<P> </P>
<P><STRONG><U>Current Selection:</U></STRONG></P>
<P><STRONG>Name:</STRONG> <asp:Label id="lblName"
runat="server"></asp:Label></P>
<P><STRONG>Age:</STRONG> <asp:Label id="lblAge"
runat="server"></asp:Label></P>
... |
Please note the following:
- I know the type of the control in the Template column because I specified
it: <asp:Label ...
This means I no longer have to guess the type of the control in the Template
column.
- I know the ID of the information I want to retrieve: ID="lblThisAge"
...
- I can use the FindControl() method of the item to retrieve my Label based
on its ID.
This means that I don't have to guess which order the control appears in the
cell's Controls collection and I don't have to keep track of which column
number my Age column is.
...
Protected Sub SelectionChanged()
'Bound Column... This works
lblName.Text = dgAges.SelectedItem.Cells(1).Text
'Template Column... This works
lblAge.Text =
CType(dgAges.SelectedItem.FindControl("AgeText"), Label).Text
End Sub
... |
The Conclusion
The problem was that I assumed the Text property of the cell would always return
the text in the cell. If you think about it, you'll realize that with
all the complex things you can do in a Template column, and all the complex
controls you can have in a Template column, the Text property wouldn't suffice
as a means of retrieving the information you need. With this understanding,
you can then explore all the options you have in how to retrieve and/or manipulate
information in your DataGrid.