GridView RowDataBound event handler does not find GridView cells content - asp.net

I have the following GridView, which has as DataSource a List<T>:
<asp:GridView ID="gvDownloads" UseAccessibleHeader="False"
AutoGenerateColumns="False" runat="server" PageSize="10" AllowPaging="true"
CellPadding="4" CellSpacing="1" GridLines="None" DataKeyNames="productid">
<EmptyDataTemplate>
No licenses found
</EmptyDataTemplate>
<Columns>
<asp:TemplateField HeaderText="Id" >
<ItemTemplate>
<%# Eval("ProductId")%>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Product Name">
<ItemTemplate>
<%# Eval("ProductName")%>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Stock Code">
<ItemTemplate>
<%# Eval("StockCode")%>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Which renders correctly and with the proper values.
Now, I would like to modify on the fly the field StockCode and in order to do so I have in my code behind:
Sub gvDownloads_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs) Handles gvDownlads.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
e.Row.Cells(2).Text = StockCodeConverter.Convert(e.Row.Cells(2).Text)
End If
End Sub
But the data cells corresponding to StockCode are empty. Now I tried to debug and for some reason the code finds just the value of the header row. The values of the other rows are string.Empty or &nsbp. Might it depend on the List as DataSource?

Use ASP.NET controls instead, for example Labels:
If e.Row.RowType = DataControlRowType.DataRow Then
Dim lblStockCode = DirectCast(e.Row.FindControl("lblStockCode"), Label)
lblStockCode.Text = StockCodeConverter.Convert(lblStockCode.Text)
End If
on aspx:
<asp:TemplateField HeaderText="Stock Code">
<ItemTemplate>
<asp:Label Id="LblStockCode" runat="server" Text='<%# Eval("StockCode") %>'></asp:label>
</ItemTemplate>
</asp:TemplateField>
You can even omit the Eval on aspx and set the Text property completely in codebehind:
If e.Row.RowType = DataControlRowType.DataRow Then
Dim row = DirectCast(e.Row.DataItem, DataRowView)
Dim lblStockCode = DirectCast(e.Row.FindControl("lblStockCode"), Label)
lblStockCode.Text = StockCodeConverter.Convert(row["StockCode"].ToString)
End If
Edit: If you want to stay with your text and also with the TemplateField you could cast the first control in the cell which is a autogenerated DataBoundLiteralControl when there's only text and use it's Text property.
Dim StockCode = DirectCast(e.Row.Cells(2).Controls(0), DataBoundLiteralControl).Text
But that makes your code less readable in my opinion.

I think in GridView RowDataBound Event as the Binding is still in process you are not getting any Value...I would suggest you to use "DataRowView"
DataRowView drv = (DataRowView)e.Row.DataItem;
e.Row.Cells(2).Text = drv["StockCode"].ToString();

Related

How to code a button click event in a GridView that returns a value from its row in the GridView

Apologies for the slightly convoluted title.
Basically, I have a project where I have a database connected to it. I'm displaying some of the contents of the database using a GridView, which is all up and running, but the way I'm designing the page I need to be able to click a button in each row that in essence exports the value of one of the cells in the row to a subroutine.
I've tried googling it quite a bit and trying related things I found here, but nothing I could find would function like I would like it to.
The relevant code is below.
Markup:
<asp:GridView ID="Staffgv" runat="server" AutoGenerateColumns="false" OnRowCommand="Staffgv_RowCommand" AllowPaging="true" PageSize="20" OnPageIndexChanging="Staffgv_PageIndexChanging" BackColor="#f9f9f9" CssClass="gvStyle" >
<Columns>
<asp:TemplateField HeaderText="Start" InsertVisible="False" SortExpression="DateTimeStart">
<HeaderStyle Width="70px" CssClass="hdrGvStart"/>
<ItemTemplate>
<asp:Label ID="lblDateTimeStart" runat="server" Text='<%# Bind("DateTimeStart", "{0:t}") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Finish" SortExpression="DateTimeEnd">
<HeaderStyle Width="70px" CssClass="hdrGvFinish"/>
<ItemTemplate>
<asp:Label ID="lblDateTimeEnd" runat="server" Text='<%# Bind("DateTimeEnd", "{0:t}") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Forename" SortExpression="Forename">
<HeaderStyle Width="140px" CssClass="hdrGvForename"/>
<ItemTemplate>
<asp:Label ID="lblForename" runat="server" Text='<%# Bind("Forename") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Surname" SortExpression="Surname">
<HeaderStyle Width="140px" CssClass="hdrGvSurname"/>
<ItemTemplate>
<asp:Label ID="lblSurname" runat="server" Text='<%# Bind("Surname") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<HeaderStyle CssClass="gvHeaderEdit" />
<ItemTemplate>
<asp:Button ID="Btnapptid" runat="server" Text="" CssClass="btnGVEdit" CommandName="FillStaffTables" CommandArgument="<%# CType(Container,GridViewRow).RowIndex %>"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And the relevant VB code:
Private Sub Staffgv_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles Staffgv.RowDataBound
Select Case e.Row.RowType
Case DataControlRowType.DataRow
Dim row = DirectCast(e.Row.DataItem, DataRowView)
Dim apptid As Integer = Integer.Parse(row("AppointmentID").ToString)
Dim Btnapptid = DirectCast(e.Row.FindControl("Btnapptid"), Button)
'Btnapptid.Text = apptid
Btnapptid.ToolTip = apptid
End Select
End Sub
Protected Sub Staffgv_RowCommand(ByVal sender As Object, e As GridViewCommandEventArgs)
If (e.CommandName = "FillStaffTables") Then
Dim index As Integer = Convert.ToInt32(e.CommandArgument)
lblTxtTester.Text = "AppointmentID"
TextboxTester.Text = index
End If
End Sub
If anyone would like more code like the code used to fill the GridView I'll post it, just didn't want to post too much irrelevant code.
Thanks
Solution A
When you only want 1 value from the gridview row when the button is clicked.
You could simply utilize the CommandArgument of your button (Btnapptid). This assumes that you don't need the gridview row index for when the button is clicked. If you do need this, then please see Solution B, otherwise continue here.
First, you'd need to modify your button's CommandArgument in the aspx page
ASPX
<asp:Button ID="Btnapptid" runat="server" Text="" CssClass="btnGVEdit"
CommandName="FillStaffTables" CommandArgument='<%# Bind("AppointmentID") %>'/>
Then you should be able to grab the AppointmentID like this
VB (inside Staffgv_RowCommand())
If (e.CommandName = "FillStaffTables") Then
txtAppointmentID.Text = e.CommandArgument
End If
Solution B
When you need more than 1 value from the gridview row when the button is clicked
Please note that this solution requires a couple changes on your end
Create an additional control (in the gridview) which should hold the value that you want to get when the button is clicked.
Fill said control with the value you want (via Bind() in the UI or in RowDataBound in the codebehind).
Next, in your RowCommand you'd be able to grab the newly created control with the help of the index variable (from your example) like so
Staffgv.Rows(index).FindControl("YOUR_CONTROLS_ID")
Example
Say that you decide to create a HiddenField control in which to store the value from your database
ASPX (hidden field should be somewhere inside the gridview - right under Btnapptid should be fine)
<asp:HiddenField ID="hfMyControl" runat="server" Visible="False"
Value='<%# Bind("SOME_DB_COLUMN") %>'/>
VB (inside Staffgv_RowCommand())
Dim hfMyControl As HiddenField = _
DirectCast(Staffgv.Rows(index).FindControl("hfMyControl"), HiddenField)
Then simply use the value of hfMyControl
hfMyControl.Value
You could also repeat all this using multiple controls in order to potentially access multiple DB values stored in controls in the gridview.

How to read a dataitem from a parent repeater?

How to read a dataitem from a parent repeater?
<asp:Repeater ID="rpt" runat="Server">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "MyRepeaterDataItem")%>
<asp:GridView ID="gv" runat="Server">
<Columns>
<asp:TemplateField>
<HeaderTemplate>
<%# DataBinder.Eval(Container.DataItem, "MyRepeaterDataItem")%>
</HeaderTemplate>
<ItemTemplate>
TEXT
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</ItemTemplate>
</asp:Repeater>
I am trying to get the dataitem MyRepeaterDataItem belonging to the repeater to appear in the nested gridviews header.
I have tried using .Parent and .NamingContainer but cannot get the correct syntax using VB.NET
Assuming you're binding a Datasource to the GridView in the RepeaterItem's ItemDataBound Event Handler you can wire up a Handler for the RowCreated (or DataBound) Events of the GridView using AddHandler:
Private Sub rpt_ItemDataBound(sender As Object, e As RepeaterItemEventArgs) Handles rpt.ItemDataBound
If e.Item.ItemType = ListItemType.AlternatingItem OrElse e.Item.ItemType = ListItemType.Item Then
AddHandler gvTarget.RowCreated, AddressOf GridViewRowCreated
'Bind your Datasource to the GridView AFTER you wire it up:
'Dim gvTarget As GridView = CType(e.Item.FindControl("gv"), GridView)
'gvTarget.DataSource = lstYourDataSource
'gvTager.DataBind()
End If
End Sub
Then your Event Handler for gv's RowCreated can get the value from the parent RepeaterItem:
Public Sub GridViewRowCreated(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
Dim lblHeader As Label = CType(e.Row.FindControl("lblGridViewHeader"), Label)
'Check due to Header/Footer.
If Not lblHeader Is Nothing Then
lblHeader.Text = DataBinder.Eval(CType(sender.Parent, RepeaterItem).DataItem, "MyRepeaterDataItem")
End If
End Sub
The code mentioned above requires you to add a label to your GridView's HeaderTemplate:
<asp:GridView ID="gv" runat="Server">
<Columns>
<asp:TemplateField>
<HeaderTemplate>
<asp:Label ID="lblGridViewHeader" runat="server"></asp:Label>
</HeaderTemplate>
<ItemTemplate>
TEXT
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Or optionally you could just do this in GridViewRowCreated:
e.Row.Cells(0).Text = DataBinder.Eval(CType(sender.Parent, RepeaterItem).DataItem, "MyRepeaterDataItem")
Get Parent Data Item From Any Nested Control
After a little trial and error I worked out a simple solution that can work without any code behind.
VB.NET
<HeaderTemplate>
<%# DataBinder.Eval(Container, "NamingContainer.NamingContainer.DataItem.MyRepeaterDataItem")%>
</HeaderTemplate>
Where MyRepeaterDataItem is the data column.

How to find a control inside a TemplateField in Gridview

I have a gridView with a textBox inside a templateField. I wan to extract the text of the textBox if a checkbox is marked in the row.
I have the gridView defined as follows
<asp:GridView ID="GV_Comments" runat="server" AutoGenerateColumns="False" DataKeyNames="id"
DataSourceID="SQL_Comments">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox runat="server" ID="Comment_Select" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="ID" HeaderText="ID" ReadOnly="True" SortExpression="ID" />
<asp:TemplateField HeaderText="comment" SortExpression="comment">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Bind("comment") %>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="CommentForPeriod" runat="server" Text='<%# Bind("comment") %>'></asp:TextBox>
</EditItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:Button ID="B_Load" runat="server" Text="Transfer Selection" onclick="B_Load_Click" />
<br />
<asp:TextBox ID="CompiledText" runat="server" Width="662px" Rows="10"
TextMode="MultiLine"></asp:TextBox>
And the code as follows
Protected Sub B_Load_Click(ByVal sender As Object, ByVal e As EventArgs) '(sender As Object, e As System.EventArgs) Handles B_Load.Click
Dim FullText As String = ""
For Each row As GridViewRow In GV_Comments.Rows
Dim CB_Control As CheckBox = CType(row.FindControl("Comment_Select"), CheckBox)
Dim Txt_Control As TextBox = CType(row.FindControl("CommentForPeriod"), TextBox)
If CB_Control IsNot Nothing AndAlso CB_Control.Checked AndAlso Txt_Control IsNot Nothing Then
FullText = FullText & Txt_Control.Text & "<br/>"
End If
Next row
CompiledText.Text = FullText.ToString
End Sub
When I debug the code I can see that the Checkbox control is found but not the TextBox control. Would anyone see why?
You can't do like this. When you click the button: B_Load, then GridView is NOT in Edit mode. And this is why you can't get the TextBox, which is in EditItemTemplate.
You can only get the controls inside <ItemTemplate> in your button click as gridview is in Normal display Mode. <EditItemTemplate> controls are rendered only when GridView enters Edit mode.
So, you need to get the value of the Label: Label1 here actually, which has the same value and is inside <ItemTemplate> .
Dim Lbl_Control As Label= CType(row.FindControl("Label1"), Label)
// button click as usual, just get and check the value of Label control, rather than TextBox control.
Protected Sub B_Load_Click(ByVal sender As Object, ByVal e As EventArgs) '(sender As
Object, e As System.EventArgs) Handles B_Load.Click
Dim FullText As String = ""
For Each row As GridViewRow In GV_Comments.Rows
Dim CB_Control As CheckBox = CType(row.FindControl("Comment_Select"),
CheckBox)
Dim Lbl_Control As Label= CType(row.FindControl("Label1"), Label)
If CB_Control IsNot Nothing AndAlso CB_Control.Checked AndAlso Lbl_Control
IsNot Nothing Then
FullText = FullText & Lbl_Control.Text & "<br/>"
End If
Next row
CompiledText.Text = FullText.ToString
End Sub

How to display confirmation message with GridView ShowDeleteButton CommandField

I'm trying to attach a Javascript function to confirm a delete of a record in a GridView. I know this can be done more easily using an asp:LinkButton in an ItemTemplate but I am trying to attach it to a CommandField - ShowDeleteButton button.
I've tried to follow this tutorial: display-confirmation-message-on-gridview-deleting
I am new to GridView and my implementation is in VB.Net not C#. Here is the code where I try to inject the Javascript function and I cannot figure out how/why my row.cell references are wrong:
Protected Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs) _
Handles GridView1.RowDataBound
If e.Row.RowState <> DataControlRowState.Edit Then
If e.Row.RowType = DataControlRowType.DataRow Then 'check for RowType DataRow
Dim id As String = e.Row.Cells(0).Text 'get the position to be deleted
Try
'cast the ShowDeleteButton link to linkbutton
Dim lb As LinkButton
Dim i As Integer = 4 'cell we want in the row (relative to 0 not 1)
lb = DirectCast(e.Row.Cells(i).Controls(2), LinkButton)
If lb IsNot Nothing Then 'attach the JavaScript function with the ID as the parameter
lb.Attributes.Add("onclick", "return ConfirmOnDelete('" & id & "');")
End If
Catch ex As Exception
End Try
End If
End If
Here is my GridView markup snippet (a bit more busy than all bound columns; I count 3 TemplateField items after the first and only BoundField to arrive at the 5th column hence i = 4 above):
<Columns>
<asp:BoundField DataField="PositionID" HeaderText="ID" ReadOnly="true" />
<asp:TemplateField HeaderText="PositionTitle">
<ItemTemplate>
<%# Eval("PositionTitle")%>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox runat="server" ID="txtPositionTitle" Text='<%# Eval("PositionTitle")%>' />
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Incumbent">
<ItemTemplate>
<asp:Label ID="lblUser" runat="server" Text='<%# Eval("Incumbent")%>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:Label ID="lblUser" runat="server" Text='<%# Eval("Incumbent")%>' Visible = "false"></asp:Label>
<asp:DropDownList Width="100%" runat="server"
id="ddlUsers" AutoPostBack="true"
DataTextField="FullName" DataValueField="UserID"
OnSelectedIndexChanged="ddlUsers_SelectedIndexChanged">
</asp:DropDownList>
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Backup">
<ItemTemplate>
<%#Eval("Backup")%>
</ItemTemplate>
<EditItemTemplate>
<asp:Label ID="lblBackup" runat="server" Text='<%# Eval("Backup")%>' Visible = "false"></asp:Label>
<asp:DropDownList Width="100%" runat="server"
id="ddlUsersBackup" AutoPostBack="true"
DataTextField="FullName" DataValueField="UserID"
OnSelectedIndexChanged="ddlUsersBackup_SelectedIndexChanged">
</asp:DropDownList>
</EditItemTemplate>
</asp:TemplateField>
<asp:CommandField ButtonType="Button" ControlStyle-CssClass="coolbutton"
ShowEditButton="true"
ShowDeleteButton="true"
ShowCancelButton="true" />
When I ignore the error, the Command buttons are indeed in the 5th column:
The error I get without the Try/Catch is:
If you haven't figured out yet, your problem is in ButtonType="Button" here:
<asp:CommandField ButtonType="Button" ControlStyle-CssClass="coolbutton"
ShowEditButton="true"
ShowDeleteButton="true"
ShowCancelButton="true" />
You can't cast a command button to link button. You have to define it as Link button (please refer to MSDN for details of CommandField class). This is working:
<asp:CommandField ButtonType="Link" ControlStyle-CssClass="coolbutton"
ShowEditButton="true"
ShowDeleteButton="true"
ShowCancelButton="true" />
Just call a javascript function on onclientclick event and ask for confirmation. If it returns true then you can call the server side code to delete.
Below is the code for explanation
<asp:LinkButton ID="lbDelete" runat="server" OnClick="lbDelete_Click" OnClientClick="return fnConfirm();"> Delete</asp:LinkButton>
And below is the javascript function:
<script type="text/javascript">
function fnConfirm() {
if (confirm("The item will be deleted. Are you sure want to continue?") == true)
return true;
else
return false;
}
You can check the detailed article with source code in the below link
http://www.dotnetpickles.com/2013/03/how-to-show-confirm-message-while.html
Thanks
I found lots of C# answers.
aspx page:
<asp:CommandField DeleteText="Delete" ShowDeleteButton="true"
ButtonType="Button" />
aspx.vb page:
Protected Sub GridView1_RowDataBound(sender As Object, _
e As GridViewRowEventArgs) _
Handles GridView1.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
If (e.Row.RowState <> DataControlRowState.Edit) Then
Dim oButton As Button
oButton = CType(e.Row.Cells(14).Controls(0), Button)
oButton.OnClientClick = _
"if (confirm(""Are you sure you wish to Delete?"") == false) return;"
End If
End If
End Sub

Need variable number of drop-down lists in a gridview template field

I have the following gridview defined which is populating perfectly:
<asp:GridView runat="server" ID="chargeDetail" AutoGenerateColumns="false" DataKeyNames="LineItemNumber,DetailId,IsParking" CellSpacing="0" CellPadding="6"
HorizontalAlign="Center" EnableViewState="true" ShowFooter="true">
<Columns>
<asp:BoundField DataField="StudentID" HeaderText="Student ID" ReadOnly="True" ItemStyle-HorizontalAlign="Center" />
<asp:BoundField DataField="StudentName" HeaderText="Name" ReadOnly="true" />
<asp:BoundField DataField="CampusName" HeaderText="Campus" ReadOnly="true" />
<asp:TemplateField HeaderText="Description" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:Label runat="server" ID="LineItemDetailLabel" Text='<%# IIf(Eval("Mandatory") = "1", Eval("LineItemDetail") + " *", Eval("LineItemDetail")) %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Item Cost" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
$<asp:Label runat="server" ID="LineItemCostLabel" Text='<%# Eval("LineItemCost") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Options" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:Label runat="server" ID="lblOptionsAvailable"
Text='<%# IIf(Eval("AttributesAvailable") = "1", "Options will be available to select on the next screen.", "No options available.") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Pay Now" ItemStyle-HorizontalAlign="Center" FooterText="Total:">
<ItemTemplate>
<asp:Checkbox runat="server" ID="cbLineItemSelected" Checked='<%# IIf(Eval("Selected") = "1", "True", "False") %>'
Enabled='<%# IIf(Eval("Mandatory") = "1", "False", "True") %>' AutoPostBack="True" OnCheckedChanged="cbLineItemSelected_CheckedChanged" />
</ItemTemplate>
<FooterStyle Font-Bold="True" HorizontalAlign="Right" />
</asp:TemplateField>
<asp:TemplateField HeaderText="Total">
<ItemTemplate>
$<asp:Label runat="server" ID="lblLineTotalCost" Text='<%# IIf(Eval("Selected") = "1", Eval("LineItemCost"), "0.00") %>' />
</ItemTemplate>
<FooterTemplate>
$<asp:Label runat="server" ID="lblTotal" />
</FooterTemplate>
<FooterStyle Font-Bold="True" />
</asp:TemplateField>
</Columns>
</asp:GridView>
In the TemplateField where HeaderText="Options", I need to be able to create a variable number of drop-down lists. For each fee the school may be charging, they could have any number of attributes associated. For example, a TShirt fee may need to collect Size and Color and each attribute may have multiple options.
Right now, you can see I have a label that is populating correctly but I need to be able to create the two drop-down lists, in this case for Size and Color, so the parent can specify them when they check the box to include this fee in their charges. You'll notice that in the Pay Now column, the checkbox has an OnCheckedChanged event which currently updates the total column and overall total in the footer row.
I would ideally like that event to populate the drop-downs when it is checked so the parent can make their selections. I'll also need to know how to handle the post back from those drop-downs so I can get the selections recorded correctly.
From what I can find, I'm guessing I need to create a nested gridview where I can put the drop-downlists but I can't find any examples to point me in the right direction of how to make that work. I can easily populate the drop-downs from the code behind and if pointed in the right direction, access the data as the parents enter it. Just need a bit of direction or an easier way.
Thanks in advance!
OK, I came up with a solution that seems to work. Inside the gridview definition, I nested another gridview with the drop-down list inside of it as follows:
<asp:TemplateField HeaderText="Options" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:GridView runat="server" ID="gvOptions" AutoGenerateColumns="False" DataKeyNames="AttributeId" HorizontalAlign="center" EnableViewState="true"
GridLines="None" EmptyDataText="No available options." ShowHeader="False">
<Columns>
<asp:BoundField DataField="Description" ReadOnly="true" ItemStyle-HorizontalAlign="Center" />
<asp:TemplateField>
<ItemTemplate>
<asp:DropDownList runat="server" ID="ddlOptionSelection" DataTextField="Description" DataValueField="OptionId"
EnableViewState="true" AutoPostBack="true" OnSelectedIndexChanged="ddlOptionSelection_SelectedIndexChanged">
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</ItemTemplate>
</asp:TemplateField>
The challenge was figuring out how to tie everything together since data in the nested gridview relied on data keys from the parent gridview. The RowDataBound event for the parent finds the child gridview and populates the dropdowns as follows (gvOptions is the child and chargeDetail is the parent):
Protected Sub chargeDetail_RowDataBound(sender As Object, e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles chargeDetail.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
Dim gv As GridView = DirectCast(e.Row.FindControl("gvOptions"), GridView)
Dim cb As CheckBox = DirectCast(e.Row.FindControl("cbLineItemSelected"), CheckBox)
Dim lineItem As String = chargeDetail.DataKeys(e.Row.RowIndex).Values("LineItemNumber")
gv.DataSource = GetOptions(chargeDetail.DataKeys(e.Row.RowIndex).Values("DetailId"))
gv.DataBind()
Dim gridLineCount As Integer = gv.Rows.Count()
Dim gridWorkingRow As Integer
For gridWorkingRow = 0 To gridLineCount - 1
Dim ddl As DropDownList = gv.Rows(gridWorkingRow).FindControl("ddlOptionSelection")
Dim AttribId As Integer = gv.DataKeys(gridWorkingRow).Values("AttributeId")
ddl.DataSource = PopulateOptions(AttribId)
ddl.DataBind()
ddl.SelectedValue = GetSelectedValue(AttribId, lineItem)
If cb.Checked Then
ddl.Enabled = True
Else
ddl.Enabled = False
End If
Next gridWorkingRow
End If
End Sub
When one of the dropdowns change, the following is fired. I found it easier, based on how the data is stored in the database, to re-send all of the options since the attribute:option pairs are concatenated to be stored in a single field:
Protected Sub ddlOptionSelection_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim ddlSender As DropDownList = DirectCast(sender, DropDownList)
Dim row As GridViewRow = DirectCast(ddlSender.NamingContainer, GridViewRow)
Dim gvOption As GridView = DirectCast(row.NamingContainer, GridView)
Dim chargeDetailRow As GridViewRow = DirectCast(gvOption.Parent.Parent, GridViewRow)
Dim LineItemNumber As Integer = CInt(chargeDetail.DataKeys(chargeDetailRow.RowIndex).Values("LineItemNumber"))
Dim gridLineCount As Integer = gvOption.Rows.Count()
Dim gridWorkingRow As Integer
Dim OptionString As String = ""
For gridWorkingRow = 0 To gridLineCount - 1
Dim ddl As DropDownList = gvOption.Rows(gridWorkingRow).FindControl("ddlOptionSelection")
Dim AttribId As String = gvOption.DataKeys(gridWorkingRow).Values("AttributeId")
If gridWorkingRow = gridLineCount - 1 Then
OptionString = OptionString + AttribId + ":" + ddl.SelectedValue
Else
OptionString = OptionString + AttribId + ":" + ddl.SelectedValue + ","
End If
Next gridWorkingRow
Dim PortalConn As New SqlConnection
PortalConn.ConnectionString = ConfigurationManager.ConnectionStrings("PortalConnStr").ConnectionString
PortalConn.Open()
Dim WriteBackSQL As New SqlCommand("sp_schedulePickup_UpdateOption", PortalConn)
WriteBackSQL.CommandType = Data.CommandType.StoredProcedure
WriteBackSQL.Parameters.Add(New SqlParameter("#SessionID", Session.SessionID.ToString()))
WriteBackSQL.Parameters.Add(New SqlParameter("#LineItemNumber", LineItemNumber))
WriteBackSQL.Parameters.Add(New SqlParameter("#Option", OptionString))
Dim returnStatus As Integer
Dim rsReturn As SqlDataReader = WriteBackSQL.ExecuteReader()
Do While rsReturn.Read()
returnStatus = rsReturn("ReturnStatus")
Loop
rsReturn.Close()
rsReturn = Nothing
PortalConn.Close()
PortalConn = Nothing
chargeDetail.DataSource = GetChargeDetails()
chargeDetail.DataBind()
getTotal()
End Sub

Resources