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">×</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">×</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按钮。但我打算放入一个 LinkButton - 因为它们可以轻松使用 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
我正在尝试构建一个带有嵌套网格视图的 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">×</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">×</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按钮。但我打算放入一个 LinkButton - 因为它们可以轻松使用 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