Nested GridView Row Event Pandemonium
I have come across odd behavior involving nested gridview controls and row events. Basically the nested gridview row event will not fire unless it is in the last row of the outer grid.
If you explicitly add the row event property to the markup (e.g. OnRowDeleted="gvInner_RowDeleted") then the row event fires for all nested gridviews however it fires twice for the nested gridview in the last row of the outer gridview
EDIT: I should note that I am using the SqlDataSource for the outer gridview as it is also editable. This isn't clear in the example.
Markup:
<asp:Grid开发者_如何学GoView ID="gvOuter" DataSourceID="sdsOuter" runat="server" OnRowDatabound="DynamicControlAdder" AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="Id" ReadOnly="true" />
<asp:TemplateField>
<ItemTemplate>
<asp:UpdatePanel ID="upInner" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:GridView ID="gvInner" DataSourceId="sdsInner" runat="server" DataKeyNames="Id" AutoGenerateColumns="false">
<Columns>
<asp:CommandField ShowDeleteButton="true" />
<asp:BoundField DataField="Id" />
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="sdsInner" runat="server"
ProviderName="<%$ ConnectionStrings:YourString.ProviderName %>"
ConnectionString="<%$ ConnectionStrings:YourString %>"
SelectCommand="SELECT * FROM tblInner WHERE OuterTableKey = @OuterTableKey"
DeleteCommand="DELETE FROM tblInner WHERE Id = @Id">
<SelectParameters>
<asp:Parameter Name="OuterTableKey" DefaultValue="0" Type="Int32" />
</SelectParameters>
<DeleteParameters>
<asp:Parameter Name="Id" DefaultValue="0" Type="Int32" />
</DeleteParameters>
</asp:SqlDataSource>
</ContentTemplate>
</asp:UpdatePanel>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="sdsOuter" runat="server"
ProviderName="<%$ ConnectionStrings:YourString.ProviderName %>"
ConnectionString="<%$ ConnectionStrings:YourString %>"
SelectCommand="SELECT * FROM tblOuter">
</asp:SqlDataSource>
Code:
Partial Class Testing
Inherits System.Web.UI.UserControl
Protected Sub DynamicControlAdder(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
If e.Row.RowType <> DataControlRowType.DataRow Then
Exit Sub
End If
Dim s As SqlDataSource = CType(e.Row.FindControl("sdsInner"), SqlDataSource)
s.SelectParameters("OuterTableKey").DefaultValue = e.Row.DataItem("Id").ToString()
End Sub
Protected Sub gvInner_RowDeleted(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeletedEventArgs) Handles gvInner.RowDeleted
//'This never fires unless it is the last row in the outer gridview
//'also, if this event is declared in the OnRowDeleted property of the inner gridview, this event fires twice if it is in the last row of the outer grid
Dim strFoo As String = "Foo"
End Sub
End Class
Can anybody reproduce this, and how do you work around it?
I don't think you should doing what you're doing the way you're doing it.
Here's how I would do what you're attempting:
- Use a repeater for your outer databound control as it renders more efficiently and it will have the same effect you're trying to achieve with the outer gridview control.
- In your code behind, retrieve your data for your outer gridview (which is now a repeater) and load it into a datatable and add it to a dataset, do the same for data in the inner gridview.
- Then create a relation between the datatables within the dataset using DataSet.Relations.Add
- Once you create the relationship between both datatables in the dataset, you can access the fields (columns) in your second datatable which will be used to populate the data for the nested (inner) gridview control.
Markup
<asp:Repeater ID="rpt1" runat="server">
<ItemTemplate>
<asp:GridView ID="gv1" runat="server" DataSource='<%# Container.DataItem.Row.GetChildRows("relation1") %>' GridLines="None" AutoGenerateColumns="false">
<columns>
<asp:TemplateField>
<ItemTemplate>
<asp:TextBox ID="txt1" runat="server" Text='<%# Container.DataItem("<column>") %>'></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
</columns>
</asp:GridView>
</ItemTemplate>
</asp:Repeater>
Code Behind
Dim ds As New DataSet
Dim dt1 As DataTable = obj.<method> 'Outer data
Dim dt2 As DataTable = obj.<method> 'Inner data
ds.Tables.Add(dt1)
ds.Tables.Add(dt2)
ds.Relations.Add("relation1", ds.Tables(0).Columns("<unique identifier column name>"), ds.Tables(1).Columns("<unique id column name>"), False)
rpt1.DataSource = dt1
rpt1.DataBind()
And the answer is......
Remove the handles clause from the RowDeleted Event and set the OnRowDeleted Property to the gvInner_RowDeleted event.
Updated code below. I just wish i had the last 4 hours of my life back.
Markup:
<asp:GridView ID="gvOuter" DataSourceID="sdsOuter" runat="server" OnRowDatabound="DynamicControlAdder" AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="Id" ReadOnly="true" />
<asp:TemplateField>
<ItemTemplate>
<asp:UpdatePanel ID="upInner" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:GridView ID="gvInner" DataSourceId="sdsInner" runat="server" DataKeyNames="Id" OnRowDeleted="gvInner_RowDeleted" AutoGenerateColumns="false">
<Columns>
<asp:CommandField ShowDeleteButton="true" />
<asp:BoundField DataField="Id" />
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="sdsInner" runat="server"
ProviderName="<%$ ConnectionStrings:YourString.ProviderName %>"
ConnectionString="<%$ ConnectionStrings:YourString %>"
SelectCommand="SELECT * FROM tblInner WHERE OuterTableKey = @OuterTableKey"
DeleteCommand="DELETE FROM tblInner WHERE Id = @Id">
<SelectParameters>
<asp:Parameter Name="OuterTableKey" DefaultValue="0" Type="Int32" />
</SelectParameters>
<DeleteParameters>
<asp:Parameter Name="Id" DefaultValue="0" Type="Int32" />
</DeleteParameters>
</asp:SqlDataSource>
</ContentTemplate>
</asp:UpdatePanel>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="sdsOuter" runat="server"
ProviderName="<%$ ConnectionStrings:YourString.ProviderName %>"
ConnectionString="<%$ ConnectionStrings:YourString %>"
SelectCommand="SELECT * FROM tblOuter">
</asp:SqlDataSource>
Code:
Partial Class Testing
Inherits System.Web.UI.UserControl
Protected Sub DynamicControlAdder(ByVal sender As Object, ByVal e As GridViewRowEventArgs)
If e.Row.RowType <> DataControlRowType.DataRow Then
Exit Sub
End If
Dim s As SqlDataSource = CType(e.Row.FindControl("sdsInner"), SqlDataSource)
s.SelectParameters("OuterTableKey").DefaultValue = e.Row.DataItem("Id").ToString()
End Sub
Protected Sub gvInner_RowDeleted(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeletedEventArgs)
//' Now we're talking
Dim strFoo As String = "Foo"
End Sub
End Class
精彩评论