ASP.NET Webforms嵌套Gridview点击父gridview行不显示子gridview

ASP.NET Webforms nested Gridview click on parent gridview row does not show child gridview

我正在尝试构建一个带有嵌套网格视图的 Web 表单应用程序。问题是当我在父 gridview 中的一行上单击“+”时,没有任何反应。见下文:

我有以下代码。

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Add Bill of Materials</h2>

    <script type="text/javascript">
        $(document).ready(function () {
            window.setTimeout(function () {
                $(".alert").fadeTo(1500, 0).slideUp(500, function () {
                    $(this).remove();
                });
            }, 3000);
        });

        function divexpandcollapse(divname) {
            var img = "img" + divname;
            if ($("#" + img).attr("src") == "../../Images/plus.png") {
                $("#" + img)
                    .closest("tr")
                    .after("<tr><td></td><td colspan = '100%' > " + $("#" + divname)
                        .html() + "</td></tr>")
                $("#" + img).attr("src", "../../Images/minus.png");
            } else {
                $("#" + img).closest("tr").next().remove();
                $("#" + img).attr("src", "../../Images/plus.png");
            }
        }
    </script>

    <div runat="server" visible="false" id="AlertDanger" class="alert alert-danger">
        <a href="#" class="close" data-dismiss="alert">&times;</a>BMDetails
        <strong>You must choose a date</strong>
    </div>

    <div runat="server" visible="false" id="AlertSuccess" class="alert alert-success">
        <a href="#" class="close" data-dismiss="alert">&times;</a>
        <strong>Purchase requisition saved successfully</strong>
    </div>
    <asp:Panel ID="BMDetails" runat="server">
        <div>
            <asp:UpdatePanel ID="UpdatePanelBM" runat="server">
                <ContentTemplate>
                    <fieldset class="form-horizontal">
                        <div class="row">
                            <div class="control-group col-sm-4">
                                <asp:Label runat="server" CssClass="col-sm-6 control-label" Style="text-align: left">Doc No.</asp:Label>
                                <div class="form-group col-sm-6">
                                    <asp:Label ID="lblDocNum" runat="server" CssClass="form-control" BackColor="#E6E6E6"></asp:Label>
                                </div>
                            </div>
                            <div class="control-group col-sm-8">
                                <asp:Label runat="server" CssClass="col-sm-6 control-label">Cust. PO No.</asp:Label>
                                <div class="form-group col-sm-6">
                                    <asp:TextBox ID="txtPurchaseDocNum" runat="server" AutoPostBack="true" CssClass="form-control" OnTextChanged="txtPurchaseDocNum_TextChanged"></asp:TextBox>
                                    <ajaxToolkit:AutoCompleteExtender
                                        ID="AutoCompleteExtender1"
                                        runat="server"
                                        TargetControlID="txtPurchaseDocNum"
                                        ServicePath="../../Web_Service/PurchaseOrders.asmx"
                                        ServiceMethod="GetPurchaseOrders"
                                        MinimumPrefixLength="2"
                                        EnableCaching="true"
                                        CompletionSetCount="10"
                                        CompletionInterval="10"
                                        DelimiterCharacters=";, :"
                                        ShowOnlyCurrentWordInCompletionListItem="true">
                                    </ajaxToolkit:AutoCompleteExtender>
                                    <asp:RequiredFieldValidator runat="server" ControlToValidate="txtPurchaseDocNum" Display="Dynamic"
                                        CssClass="text-danger" ErrorMessage="The Cust. Purchase Order No field is required." />
                                </div>
                            </div>
                        </div>
                    </fieldset>
                    <asp:GridView ID="bMGridView"
                        runat="server"
                        AutoGenerateColumns="False"
                        AllowPaging="True"
                        AllowSorting="True"
                        ShowFooter="True"
                        OnPageIndexChanging="bMParentGrid_PageIndexChanging"
                        OnRowDataBound="bMParentGrid_RowDataBound"
                        OnRowCommand="bMParentGrid_RowCommand"
                        PagerStyle-CssClass="bs-pagination"
                        ShowHeaderWhenEmpty="True"
                        EmptyDataText="Select Customer PO number"
                        CssClass="table table-striped table-bordered table-hover table-condensed">
                        <Columns>                           
                            <asp:TemplateField ItemStyle-Width="20px">
                                <ItemTemplate>
                                    <a href="JavaScript:divexpandcollapse
                                    ('div<%# Eval("Item") %>');">
                                        <img alt="Details" id="imgdiv<%# Eval
                                        ("Item") %>"
                                            src="../../Images/plus.png" />
                                    </a>                                    
                                    <div id="div<%# Eval("Item") %>" style="display: none;">                                       
                                        <asp:GridView ID="bMChildGrid"
                                            runat="server"
                                            AutoGenerateColumns="False"
                                            AllowPaging="True"
                                            AllowSorting="True"
                                            ShowFooter="True"
                                            OnRowEditing="bMChildGrid_RowEditing"
                                            OnRowCancelingEdit="bMChildGrid_RowCancelingEdit"
                                            OnRowUpdating="bMChildGrid_RowUpdating"
                                            PagerStyle-CssClass="bs-pagination"
                                            ShowHeaderWhenEmpty="True"
                                            EmptyDataText="No Records Found"
                                            CssClass="table table-striped table-bordered table-hover table-condensed">
                                            <Columns>
                                                <asp:TemplateField ItemStyle-Width="30px" HeaderText="#">
                                                    <ItemTemplate>
                                                        <asp:Label ID="lblLineNum" Text='<%# Container.DataItemIndex + 1 %>' runat="server" />
                                                    </ItemTemplate>
                                                </asp:TemplateField>
                                                <asp:TemplateField ItemStyle-Width="500px" HeaderText="Item">
                                                    <ItemTemplate>
                                                        <asp:Label ID="lblItem" runat="server"
                                                            Text='<%# Bind("Item")%>'></asp:Label>
                                                    </ItemTemplate>
                                                    <EditItemTemplate>
                                                        <asp:TextBox ID="txtItem" runat="server" Width="500px" Text='<%# Bind("Item")%>'></asp:TextBox>
                                                        <ajaxToolkit:AutoCompleteExtender
                                                            ID="AutoCompleteExtender3"
                                                            runat="server"
                                                            TargetControlID="txtItem"
                                                            ServicePath="../../Web_Service/Items.asmx"
                                                            ServiceMethod="GetItems"
                                                            MinimumPrefixLength="2"
                                                            EnableCaching="true"
                                                            CompletionSetCount="10"
                                                            CompletionInterval="10"
                                                            DelimiterCharacters=";, :"
                                                            ShowOnlyCurrentWordInCompletionListItem="true">
                                                        </ajaxToolkit:AutoCompleteExtender>
                                                        <asp:RequiredFieldValidator runat="server" ControlToValidate="txtItem" Display="Dynamic" ValidationGroup="Edit"
                                                            CssClass="text-danger" ErrorMessage="The Item field is required." />
                                                    </EditItemTemplate>
                                                    <FooterTemplate>
                                                        <asp:TextBox ID="txtItem" runat="server" Width="500px"></asp:TextBox>
                                                        <ajaxToolkit:AutoCompleteExtender
                                                            ID="AutoCompleteExtender4"
                                                            runat="server"
                                                            TargetControlID="txtItem"
                                                            ServicePath="../../Web_Service/Items.asmx"
                                                            ServiceMethod="GetItems"
                                                            MinimumPrefixLength="2"
                                                            EnableCaching="true"
                                                            CompletionSetCount="10"
                                                            CompletionInterval="10"
                                                            DelimiterCharacters=";, :"
                                                            ShowOnlyCurrentWordInCompletionListItem="true">
                                                        </ajaxToolkit:AutoCompleteExtender>
                                                        <asp:RequiredFieldValidator runat="server" ControlToValidate="txtItem" Display="Dynamic" ValidationGroup="Insert"
                                                            CssClass="text-danger" InitialValue="-1" ErrorMessage="The Item field is required." />
                                                    </FooterTemplate>
                                                </asp:TemplateField>
                                                <asp:TemplateField ItemStyle-Width="120px" HeaderText="Required Qty.">
                                                    <ItemTemplate>
                                                        <asp:Label ID="lblQuantity" runat="server"
                                                            Text='<%# Bind("Quantity")%>'></asp:Label>
                                                    </ItemTemplate>
                                                    <EditItemTemplate>
                                                        <asp:TextBox ID="txtQuantity" runat="server" Width="100px"
                                                            Text='<%# Bind("Quantity")%>'></asp:TextBox>
                                                        <asp:RequiredFieldValidator runat="server" ControlToValidate="txtQuantity" Display="Dynamic" ValidationGroup="Edit"
                                                            CssClass="text-danger" ErrorMessage="The Quantity field is required." />
                                                        <asp:RegularExpressionValidator ControlToValidate="txtQuantity" runat="server" CssClass="text-danger" Display="Dynamic"
                                                            ErrorMessage="Only integers allowed." ValidationExpression="^(0|[1-9]\d*)$"
                                                            ValidationGroup="Edit"></asp:RegularExpressionValidator>
                                                    </EditItemTemplate>
                                                    <FooterTemplate>
                                                        <asp:TextBox ID="txtQuantity" runat="server" Width="100px"></asp:TextBox>
                                                        <asp:RequiredFieldValidator runat="server" ControlToValidate="txtQuantity" Display="Dynamic" ValidationGroup="Insert"
                                                            CssClass="text-danger" ErrorMessage="The Quantity field is required." />
                                                        <asp:RegularExpressionValidator ControlToValidate="txtQuantity" runat="server" CssClass="text-danger" Display="Dynamic"
                                                            ErrorMessage="Only integers allowed." ValidationExpression="^(0|[1-9]\d*)$"
                                                            ValidationGroup="Insert"></asp:RegularExpressionValidator>
                                                    </FooterTemplate>
                                                </asp:TemplateField>
                                            </Columns>
                                        </asp:GridView>
                                    </div>
                                </ItemTemplate>
                            </asp:TemplateField>

                            <asp:TemplateField ItemStyle-Width="30px" HeaderText="#">
                                <ItemTemplate>
                                    <asp:Label ID="lblLineNum" Text='<%# Container.DataItemIndex + 1 %>' runat="server" />
                                </ItemTemplate>
                            </asp:TemplateField>

                            <asp:BoundField DataField="Item" HeaderText="Item" />
                            <asp:BoundField DataField="Quantity" HeaderText="Quantity" />

                        </Columns>
                    </asp:GridView>

                    <div class="form-group">
                        <div class="row">
                            <div class="col-sm-3">
                                <asp:Button runat="server" ID="InsertButton" OnClick="Insert" Text="Add" CssClass="btn btn-block btn-primary" />
                            </div>
                            <div class="col-sm-3">
                                <asp:Button runat="server" ID="CancelButton" OnClick="Cancel" Text="Cancel" CausesValidation="false" CssClass="btn btn-block btn-default" />
                            </div>
                        </div>
                    </div>
                </ContentTemplate>
            </asp:UpdatePanel>
        </div>
    </asp:Panel>

</asp:Content>

这是我在后面的代码中的内容:

protected void bMParentGrid_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType == DataControlRowType.DataRow)
            {
                string item = Convert.ToString(DataBinder.Eval(e.Row.DataItem, "Item"));

                string selectedItem = item.Substring(0, item.IndexOf(' ')).Trim();

                GridView bMChildGrid = e.Row.FindControl("bMChildGrid") as GridView;

                // Create Data Table
                DataTable dt = new DataTable();
                dt.Columns.Add("lineNum", typeof(int));
                dt.Columns.Add("Item", typeof(string));
                dt.Columns.Add("Quantity", typeof(int));

                dt.Rows.Add(dt.NewRow());
                bMChildGrid.DataSource = dt;
                bMChildGrid.DataBind();               
            }
        }

我做错了什么?单击父网格行不会显示嵌入其中的子网格。

嗯,我认为你不应该尝试在 gridview 中嵌套 gridview。

问题是 GridView 不像列表视图那样支持“行跨度”。

当您展开该 GridView 时,它会尝试适应一列。

因此网格可能如下所示:

所以我有 expand/show 嵌套 gridview 的 + 号,我会得到这个:

(在此示例中展开显示在酒店预订的人)

你得到这个效果:

现在您可能会用额外的锤子、锯子、钉子和锤子敲击它,使 gridview 展开并显示在该行下方。

但是,GridView 不能很好地支持(如果有的话)多行行的能力。

但是,listview 可以。因此,您可以很好地保留嵌套(子)gridview,并将其放入 ListView。

因此,列表视图 (should/can) 看起来非常相似。像这样说:

但是,我可以让嵌套控件 (gridview) 占据一整行,所以当我在列表视图中展开它时,我得到了这个:

如您所见,这看起来好多了。而且我不必与系统和 latyouts 作斗争 - 它工作得更好。

所以,这是我的 ListView 标记:

    <asp:ListView ID="ListView1" runat="server" DataKeyNames="ID" >

       <ItemTemplate>
          <tr style="">
            <td><asp:Button ID="cmdView" runat="server" Text="+" CommandName="Select" /></td>
            <td><asp:Label ID="HotelNameLabel" runat="server" Text='<%# Eval("HotelName") %>' /></td>
            <td><asp:Label ID="CityLabel" runat="server" Text='<%# Eval("City") %>' /></td>
            <td><asp:Label ID="ProvinceLabel" runat="server" Text='<%# Eval("Province") %>' /></td>
            <td><asp:Label ID="DescriptionLabel" runat="server" Text='<%# Eval("Description") %>' /></td>
          </tr>

          <tr>
            <td colspan="5">
               <asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="False" 
                 DataKeyNames="ID" CssClass="table table-hover" style="display:none"  >
                    <Columns>
                        <asp:BoundField DataField="Firstname" HeaderText="Firstname" SortExpression="Firstname" />
                        <asp:BoundField DataField="LastName" HeaderText="LastName" SortExpression="LastName" />
                        <asp:BoundField DataField="City" HeaderText="City" SortExpression="City" />
                    </Columns>
                </asp:GridView>

            </td>
           </tr>

           </ItemTemplate>

       <LayoutTemplate>
        <table id="itemPlaceholderContainer" runat="server" Class = "table table-hover" >
            <tr runat="server" style="">
                <th runat="server">View</th>
                <th runat="server">HotelName</th>
                <th runat="server">City</th>
                <th runat="server">Province</th>
                <th runat="server">Description</th>
            </tr>
            <tr id="itemPlaceholder" runat="server">
            </tr>
         </table>
       </LayoutTemplate>
    </asp:ListView>

如果仔细观察,我们会发现第一个“tr”(table 行)是重复的行。

然后我添加了额外的行 - 给它一个 col span = 5(所以它占用了一行

所以驱动这个的代码。包括 + 按钮。如果你再次点击 +,我就会崩溃(隐藏 gridview)。

因此,代码如下所示:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    If Not IsPostBack Then
        LoadGrid()
    End If

End Sub

Sub LoadGrid()

    Dim strSQL As String

    strSQL = "SELECT * FROM tblHotels ORDER BY HotelName"

    Using cmdSQL As New SqlCommand(strSQL, New SqlConnection(My.Settings.TEST4))

        cmdSQL.Connection.Open()

        ListView1.DataSource = cmdSQL.ExecuteReader
        ListView1.DataBind()

    End Using

End Sub

这样就显示了我们的网格。当然我们有“+”按钮,它只是加载给定行的网格,并将样式 (display) = normal.

代码是这样的:

Protected Sub ListView1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListView1.SelectedIndexChanged

    Dim gVR As ListViewDataItem = ListView1.Items(ListView1.SelectedIndex)
    Dim gChild As GridView = gVR.FindControl("GridView2")   ' pluck out the grid for this row

    If gChild.Style("display") = "normal" Then
        ' if grid is already display, then hide it, and exit
        gChild.Style("display") = "none"
        Exit Sub
    End If

    gChild.Style("display") = "normal"
    Dim HotelPK As String = ListView1.DataKeys(gVR.DataItemIndex).Item("ID")

    Dim strSQL As String
    strSQL = "SELECT * from People where hotel_id = " & HotelPK

    Using cmdSQL As New SqlCommand(strSQL, New SqlConnection(My.Settings.TEST4))

        cmdSQL.Connection.Open()
        gChild.DataSource = cmdSQL.ExecuteReader
        gChild.DataBind()

    End Using

End Sub

Protected Sub ListView1_SelectedIndexChanging(sender As Object, e As ListViewSelectEventArgs) Handles ListView1.SelectedIndexChanging

End Sub

代码不多,我们得到了一个非常漂亮的向下钻取,甚至可以切换显示、隐藏。

我真的建议您将其移至父级的 ListView。子项可以保留为 gridView,或任何其他内容。

请注意 listView 有多好 - 您只需拖放常规控件即可 - 您不需要像使用 GridView 那样一遍又一遍地使用模板化列。

对于大多数简单的网格 - 或者没有太多自定义的网格,GridView 非常好。

但是,当您有很多自定义控件并且需要很多花哨的东西和更多的灵活性时?然后你必须拿出大炮并开始使用 ListView。

listview 开始时往往是“一点点”更多的标记,但由于它具有近乎无限的灵活性,然后对于复杂的酷炫和惊人的网格控件,listView 更取决于挑战,并且在布局上有更多的灵活性。

ListView 的一大特点是能够为一条数据记录设置多行布局 - 这对于 GridView 来说真的很难。 ListView 使您可以为每个 reocrd 拥有“更多”一行的详细信息,并且具有有价值的 colspan(甚至还有 rowspan)。

因此,对于只有一行控件,GridView 就可以了,但如果您想要更多数据列,那么 ListView 就是合适的选择。

编辑:向该子网格添加行编辑

好的,所以我们有这段代码来加载我们的主列表视图

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    If Not IsPostBack Then
        LoadGrid()
    End If

End Sub

Sub LoadGrid()
    Dim strSQL As String

    strSQL = "SELECT * FROM tblHotels WHERE ID in (select hotel_Id from People)
             ORDER BY HotelName"

    ListView1.DataSource = MyRst(strSQL)
    ListView1.DataBind()

End Sub

并且我们将 expand + show child gv 作为此代码:

Protected Sub cmdView_Click(sender As Object, e As EventArgs)

    Dim cmd As Button = sender

    'Dim gVR As ListViewDataItem = ListView1.Items(ListView1.SelectedIndex)
    Dim gVR As ListViewDataItem = cmd.Parent
    Dim gChild As GridView = gVR.FindControl("GridView2")   ' pluck out the grid for this row

    If gChild.Style("display") = "normal" Then
        ' if grid is already display, then hide it, and exit
        gChild.Style("display") = "none"
        Exit Sub
    End If

    gChild.Style("display") = "normal"
    Dim HotelPK As String = ListView1.DataKeys(gVR.DataItemIndex).Item("ID")

    ' only re-load if never loaded (can't re-load else blow out check boxes

    If gChild.Rows.Count = 0 Then
        Dim strSQL As String
        strSQL = "SELECT * from People where hotel_id = " & HotelPK
        gChild.DataSource = MyRst(strSQL)
        gChild.DataBind()

    End If

End Sub

好的,现在我们需要向子网格添加一个编辑按钮。

我们可以在飞机上放下简asp.net按钮。但我打算放入一个 LinkBut​​ton - 因为它们可以轻松使用 bootstrap 图标 - 它们看起来更好。

因此,在我们的子 GV 标记中,我们添加:

                        <asp:BoundField DataField="City" HeaderText="City"           />
                        <asp:TemplateField HeaderText="Edit" HeaderStyle-Width="140px"
                            ItemStyle-HorizontalAlign="Center" >
                            <ItemTemplate>

                    <asp:LinkButton ID="cmdEdit" runat="server" CssClass="btn btn-default"
                        OnClick="cmdEdit_Click">
                    <span aria-hidden="true" class="glyphicon glyphicon-edit"></span>
                    </asp:LinkButton>

                    <asp:LinkButton ID="cmdDelete" runat="server" CssClass="btn btn-default"
                        style="margin-left:8px" >
                    <span aria-hidden="true" class="glyphicon glyphicon-trash"></span>
                        
                    </asp:LinkButton>

                </ItemTemplate>
            </asp:TemplateField>
           </Columns>
         </asp:GridView>

所以,我只是放入了两个按钮 - 我将它们都塞进了一个模板列中。所以,到目前为止我们所拥有的是:

(我展开一行)

所以,我们有一个编辑按钮和一个删除按钮。

因此,我们需要为弹出窗口构建一个“div”。这只是 div 中的一些简单标记,让我们可以编辑该行。

这个 div 可以放在列表视图下面 - 它在 LV 和嵌套 LV 之外 - 它只是 plane jane 100% 标记。

我有这个:

<div id="myedit" style="float:left;width:20%">
    <div style="text-align:right">
        <p>First Name :<asp:TextBox ID="txtFirstname" runat="server" Width="150"></asp:TextBox> </p>
        <p>Last Name :<asp:TextBox ID="txtLastname" runat="server" Width="150"></asp:TextBox> </p>
        <p>City  :<asp:TextBox ID="txtCity" runat="server" Width="150"></asp:TextBox> </p>
        <p>Active :<asp:CheckBox ID="Active" runat="server" Width="150"></asp:CheckBox> </p>
        <br />
    </div>
    <div style="text-align:right">
        Notes :
        <asp:TextBox ID="txtNotes" runat="server" Width="240px" TextMode="MultiLine" Height="71px"></asp:TextBox> <br />
        <br />
        <asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn" Width="90px"
             />
        <asp:Button ID="cmdCancel" runat="server" Text="Cancel" cssclass="btn" Width="90px"
          OnClientClick="$('#myedit').dialog('close');return false;" style="margin-left:8px"  />
    </div>
</div>

在页面上呈现时,它看起来像这样:

所以,第一个挑战?我们需要一个对话系统。有 BOATLOADS。但是,最常见的两个是 bootstrap 和 jQuery.UI.

我发现 bootstrap 对话框看起来非常好。但是和他们一起工作很痛苦。

所以,我们要硬着头皮在这里。毫无疑问,您正在使用并在您的网站中拥有 jQuery -(现在不都是吗???)。

因此,您必须添加并采用jQuery.UI。它是一个小型 js 库,但我强烈建议您采用它 - 它可以正常工作并允许您现在引入弹出对话框。这真的很棒 - 我们甚至使用删除按钮(确认对话框)。

因此,为一行编辑布置 div。

然后我们用 style="display:none" 隐藏 div。

我们有一个客户端脚本例程 - 这个例程的唯一工作是弹出对话框。

所以,我们现在有这个:

 <div id="myedit" style="float:left;width:20%;display:none">
    <div style="text-align:right">
        <p>First Name :<asp:TextBox ID="txtFirstname" runat="server" Width="150"></asp:TextBox> </p>
        <p>Last Name :<asp:TextBox ID="txtLastname" runat="server" Width="150"></asp:TextBox> </p>
        <p>City  :<asp:TextBox ID="txtCity" runat="server" Width="150"></asp:TextBox> </p>
        <p>Active :<asp:CheckBox ID="Active" runat="server" Width="150"></asp:CheckBox> </p>
        <br />
    </div>
    <div style="text-align:right">
        Notes :
        <asp:TextBox ID="txtNotes" runat="server" Width="240px" TextMode="MultiLine" Height="71px"></asp:TextBox> <br />
        <br />
        <asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn" Width="90px"
             />
        <asp:Button ID="cmdCancel" runat="server" Text="Cancel" cssclass="btn" Width="90px"
          OnClientClick="$('#myedit').dialog('close');return false;" style="margin-left:8px"  />
    </div>
</div>
        <script>
            function MyEdit() {
                // pop the div dialog to edit
                var myDialog = $("#myedit");
                myDialog.dialog({
                    modal: true,
                    width: "280px",
                    resizable: false,
                    appendTo: "form",
                    autoOpen: false
                });
                myDialog.dialog('open');
            }
        </script>

因此,我们将 jQuery.ui 添加到此页面。我的设置如下所示:

<head runat="server">
<title></title>

<script src="Scripts/jquery-1.12.4.js"></script>
<script src="Scripts/bootstrap.js"></script>
<link href="Content/bootstrap.css" rel="stylesheet" />

<link href="Content/themes/base/jquery-ui.css" rel="stylesheet" />
<script src="Scripts/jquery-ui-1.12.1.js"></script>

现在,我们可以采用一堆花哨的裤子客户端代码。但为了保持简单?

从现在开始,其他所有内容都是 CLEAN EASY 纯服务器端代码。我想随着时间的推移,您可以 ajax 对话框 - 引入一些 Web 方法调用 - 但它可能需要做很多额外的工作 - 我们不需要那样做。

好的,第一部分:

我们需要在 GV“编辑”按钮中连接事件点击按钮。 我们不能只是简单地双击按钮来创建点击事件。 (因为它嵌套在 lv 和 gv 中)。因此,从标记中,对于编辑按钮,我们转到标记,然后输入单击,然后注意非常非常酷,我们如何在单击事件后创建简单的代码。

例如:

因此,选择创建新事件 - 它似乎没有发生任何事情,但转到代码隐藏,我们的代码隐藏在点击存根之后。

在这个编辑按钮中?

我们得到 gv 行,从数据库中提取数据。将值推入我们的酷编辑 div,然后弹出对话框。

代码现在是这样的:

Protected Sub cmdEdit_Click(sender As Object, e As EventArgs)
    ' nested grid eit button.

    Dim cmdEdit As LinkButton = sender
    Dim gRow As GridViewRow = cmdEdit.NamingContainer
    Dim GV As GridView = gRow.NamingContainer
    Dim PkID As Integer = GV.DataKeys(gRow.RowIndex).Item("ID")

    Dim strSQL As String = "SELECT * from People WHERE ID = " & PkID

    Dim rstData As DataTable = MyRst(strSQL)

    ' load up our edit div
    With rstData.Rows(0)
        txtFirstname.Text = .Item("FirstName").ToString
        txtLastname.Text = .Item("LastName").ToString
        txtCity.Text = .Item("City").ToString
        If IsDBNull(.Item("Active")) Then
            Active.Checked = False
        Else
            Active.Checked = .Item("Active")
        End If
        txtNotes.Text = .Item("Notes").ToString
    End With

    Session("RowPK") = PkID
    ' save current list view row
    Dim LV As ListViewItem = GV.NamingContainer
    Session("LvRow") = LV.DataItemIndex

    ' now pop the dialog
    Page.ClientScript.RegisterStartupScript(Me.GetType(),
                                            "My pop", "MyEdit()", True)

End Sub

现在没有很多代码 - 但充满了巨大的提示和技巧!!!

请注意我是如何抓取 gv 行的。请注意我是如何获得 PK id 的。这些都是非常巧妙的技巧。

所以我们拉出一个数据行,推入 div,然后我们做的最后一件事是将页面设置为 运行 我们很酷的小 div 弹出对话框。

现在的结果是这样的:

现在,对于取消按钮(什么都不做),我们不需要任何实际代码 - 我们只需关闭对话框。

那个按钮是这样的:

 <asp:Button ID="cmdCancel" runat="server" Text="Cancel" cssclass="btn" Width="90px"
 OnClientClick="$('#myedit').dialog('close');return false;" style="margin-left:8px"  />

但是,我们的保存按钮?我们必须拿走文本框 - 推回数据库。

所以,保存按钮不仅要保存数据,而且当我们关闭对话框时,我们需要重新绘制(重新加载)gv - 事实上这花费了我们一些额外的代码 - 但我们需要它。

So, the save button will:
Pull text boxes back into table. 
Save send table back to database
re-load re-fresh gv

代码是这样的:

 Protected Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click

    Dim strSQL = "SELECT * FROM People where ID = " & Session("RowPK")
    Dim rstData As DataTable = MyRst(strSQL)

    With rstData.Rows(0)
        .Item("FirstName") = txtFirstname.Text
        .Item("LastName") = txtLastname.Text
        .Item("City") = txtCity.Text
        .Item("Active") = Active.Checked
        txtNotes.Text = .Item("Notes").ToString
    End With

    ' save to database
    Using conn As New SqlConnection(My.Settings.TEST4)
        Using cmdSQL As New SqlCommand(strSQL, conn)
            Dim da As New SqlDataAdapter(cmdSQL)
            Dim daU As New SqlCommandBuilder(da)
            conn.Open()
            da.Update(rstData)
        End Using
    End Using

    ' since save button does a post back? 
    ' then dialog will collpase - no need to close.

    ' we now need to re-load (refrsh) the GV to display
    ' changes
    Dim LVitem As ListViewItem = ListView1.Items(Session("LvRow"))
    ' get Gird from this row
    Dim GV As GridView = LVitem.FindControl("GridView2")

    Dim HOtelPk As Integer = rstData.Rows(0).Item("Hotel_id")
    strSQL = "SELECT * from People where hotel_id = " & HOtelPk
    Dim rstGV As DataTable = MyRst(strSQL)
    GV.DataSource = rstGV
    GV.DataBind()

End Sub

现在不会太长 - 但会突破代码屏幕的限制!!!

再次注意获取 GV 行的巧妙方法 - 我们在编辑时保存到会话中单击 LV 行 - 我们可以再次在页面中使用隐藏字段 - 所以您可以使用多种方法 - 取决于您的当前需求。

好的。就是这样!!!

现在,删除按钮?我们有这个:

同样,公平地说 - 必须重新绘制 Gv 成本代码。

但是,还不错:

Protected Sub cmdDelete_Click(sender As Object, e As EventArgs)
    ' delete this gv row

    Dim cmdDel As LinkButton = sender
    Dim gRow As GridViewRow = cmdDel.NamingContainer
    Dim GV As GridView = gRow.NamingContainer
    Dim PkID As Integer = GV.DataKeys(gRow.RowIndex).Item("ID")

    Dim LVrow As ListViewItem = GV.NamingContainer
    Dim ParentPK As Integer = ListView1.DataKeys(LVrow.DataItemIndex).Item("ID")

    Dim strSQL As String = "DELETE FROM People WHERE ID = " & PkID

    Using conn As New SqlConnection(My.Settings.TEST4)
        Using cmdSQL As New SqlCommand(strSQL, conn)
            conn.Open()
            cmdSQL.ExecuteNonQuery()
        End Using
    End Using

    ' refresh grid

    Dim rstData As DataTable =
        MyRst("SELECT * FROM People WHERE Hotel_id = " & ParentPK)
    GV.DataSource = rstData
    GV.DataBind()

End Sub

既然我们真的有那个很酷的对话系统?然后让我们弹出一个很酷的确认对话框。 (我们不必为此删除上面的代码。

Edit2:网格底部的添加按钮

好的,我们进行了编辑,但现在让我们设置一个添加按钮。

我们必须将此按钮放在网格下方,然后我们 hide/show 网格。

所以,我们修改 GV - 我们现在要 hide/show ROW!!!

所以,我们有这个:

          <td id="mygridview" colspan="5" runat="server" style="display:none">

               <asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="False" 
                 DataKeyNames="ID" CssClass="table table-hover" style="margin-left:3%;width:97%"  >
                    <Columns>
                        <asp:BoundField DataField="Firstname" HeaderText="Firstname" />
                        <asp:BoundField DataField="LastName" HeaderText="LastName"   />
                        <asp:BoundField DataField="City" HeaderText="City"           />
                        <asp:TemplateField HeaderText="Edit" HeaderStyle-Width="140px"
                            ItemStyle-HorizontalAlign="Center" >
                            <ItemTemplate>

                    <asp:LinkButton ID="cmdEdit" runat="server" CssClass="btn btn-default"
                        OnClick="cmdEdit_Click">
                    <span aria-hidden="true" class="glyphicon glyphicon-edit"></span>
                    </asp:LinkButton>

                    <asp:LinkButton ID="cmdDelete" runat="server" CssClass="btn btn-default"
                        OnClientClick="return delconfirm(this);"
                        OnClick="cmdDelete_Click"

                        style="margin-left:8px" >
                    <span aria-hidden="true" class="glyphicon glyphicon-trash"></span>
                        
                    </asp:LinkButton>

                </ItemTemplate>
            </asp:TemplateField>
           </Columns>
         </asp:GridView>
        
         <asp:Button ID="cmdAddRow" runat="server" Text="+"
             style="float:right;margin-right:5px" Width="40px"
             OnClick="cmdAddRow_Click"
             
             />
            </td>

所以,我们必须更新我们的展开按钮代码 - 抱歉 - 但我们有更多的 GV 来显示隐藏。所以代码变成这样:

Protected Sub cmdView_Click(sender As Object, e As EventArgs)

    Dim cmd As Button = sender

    'Dim gVR As ListViewDataItem = ListView1.Items(ListView1.SelectedIndex)
    Dim gVR As ListViewDataItem = cmd.Parent
    Dim gChild As GridView = gVR.FindControl("GridView2")   ' pluck out the grid for this row
    Dim MyDiv As HtmlTableCell = gVR.FindControl("mygridview")

    If MyDiv.Style("display") = "normal" Then
        ' if grid is already display, then hide it, and exit
        MyDiv.Style("display") = "none"
        Exit Sub
    End If

    MyDiv.Style("display") = "normal"

    Dim HotelPK As String = ListView1.DataKeys(gVR.DataItemIndex).Item("ID")

    ' only re-load if never loaded (can't re-load else blow out check boxes

    If gChild.Rows.Count = 0 Then
        Dim strSQL As String
        strSQL = "SELECT * from People where hotel_id = " & HotelPK
        gChild.DataSource = MyRst(strSQL)
        gChild.DataBind()

    End If

End Sub

变化不大 - 但我们隐藏/显示该行,因为我们现在有 GV 和按钮。

现在看起来像这样:

我们的添加按钮点击代码现在是这样的:

Protected Sub cmdAddRow_Click(sender As Object, e As EventArgs)

    Dim cmdAdd As Button = sender
    Dim LVrow As ListViewItem = cmdAdd.NamingContainer

    ' get PK of parent row
    Dim ParentPK As Integer = ListView1.DataKeys(LVrow.DataItemIndex).Item("ID")

    ' clear edit div
    txtFirstname.Text = ""
    txtLastname.Text = ""
    txtCity.Text = "Edmonton"
    Active.Checked = True
    txtNotes.Text = ""

    Session("LvRow") = LVrow.DataItemIndex
    Session("RowPK") = 0

    ' now pop the Edit dialog
    Page.ClientScript.RegisterStartupScript(Me.GetType(),
                                            "My pop", "MyEdit()", True)

End Sub

现在因为我们既有编辑又有添加? 好吧,那么我们的保存例程需要一点点改变。

(因此它可以同时处理 edit/save 和现在的新记录)

所以,我们稍微调整一下:

Protected Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click

    ' if new row - add it!!!
    Dim RowPK As Integer = Session("RowPK")

    Dim strSQL = "SELECT * FROM People where ID = " & Session("RowPK")
    Dim rstData As DataTable = MyRst(strSQL)

    Dim LVitem As ListViewItem = ListView1.Items(Session("LvRow"))
    ' get Gird from this row
    Dim GV As GridView = LVitem.FindControl("GridView2")

    Dim HOtelPk As Integer = ListView1.DataKeys(LVitem.DataItemIndex).Item("ID")

    Dim OneRow As DataRow

    If rstData.Rows.Count = 0 Then
        OneRow = rstData.NewRow
        OneRow("Hotel_ID") = HOtelPk
        rstData.Rows.Add(OneRow)
    Else
        OneRow = rstData.Rows(0)
    End If

    OneRow("FirstName") = txtFirstname.Text
    OneRow("LastName") = txtLastname.Text
    OneRow("City") = txtCity.Text
    OneRow("Active") = Active.Checked
    OneRow("Notes") = txtNotes.Text

    ' save to database
    Using conn As New SqlConnection(My.Settings.TEST4)
        Using cmdSQL As New SqlCommand(strSQL, conn)
            Dim da As New SqlDataAdapter(cmdSQL)
            Dim daU As New SqlCommandBuilder(da)
            conn.Open()
            da.Update(rstData)
        End Using
    End Using

    ' since save button does a post back? 
    ' then dialog will collpase - no need to close.

    ' we now need to re-load (refrsh) the GV to display
    ' changes

    strSQL = "SELECT * from People where hotel_id = " & HOtelPk
    Dim rstGV As DataTable = MyRst(strSQL)
    GV.DataSource = rstGV
    GV.DataBind()

End Sub

几乎一样!!! - 但我们设置父 PK(好吧 FK 到这一行),然后我们创建一个新行,或获取现有行。

最后但并非最不重要的一点?我有几个地方需要提取数据。我的键盘快用完了。所以我有这个辅助例程 return 数据 table.

这是一个方便的小程序。请记住,仅对来自数据键的 PK 值使用字符串连接 - 切勿将此用于来自网页的用户输入。

Public Function MyRst(strSQL As String) As DataTable

    Dim rstData As New DataTable
    Using conn As New SqlConnection(My.Settings.TEST4)
        Using cmdSQL As New SqlCommand(strSQL, conn)
            conn.Open()
            rstData.Load(cmdSQL.ExecuteReader)
            rstData.TableName = strSQL
        End Using
    End Using

    Return rstData
End Function