GridView 中的可展开行
在 GridView 中添加可展开行以实现主/从视图。

引言
本文展示了如何为 ASP.NET GridView
控件添加功能,以允许使用可展开行显示主/从记录。 它可能不适用于一次性返回大量记录的情况。
此示例基于我为一家兽医院所做的工作,展示了治疗费用超过与客户约定的限额的预约。 展开详细信息会列出构成费用的交易。
背景
我被客户要求提供此功能,并在网上找到了一些介绍不同解决方案的文章,但我发现所有这些方案都过于复杂。 但是,其中一些其他解决方案,例如 这个方案,更适合大型记录集,因为我的解决方案在页面加载时呈现每个记录的所有详细信息,而不是仅在请求时才呈现。
Using the Code
此解决方案不需要任何特殊类或自定义控件,只需要一点 JavaScript。 它只是在主(主)GridView
中的每个现有行之后添加一个新行,在 RowDataBound
事件中创建一个扩展的详细信息(详细)GridView
。
ASP 页面的代码如下所示
<asp:GridView ID="grdOverLimitList" runat="server" AutoGenerateColumns="False"
DataSourceID="SQLOverLimitList" CssClass="gridview" AllowSorting="True"
AlternatingRowStyle-CssClass="alternating"
SortedAscendingHeaderStyle-CssClass="sortedasc"
SortedDescendingHeaderStyle-CssClass="sorteddesc"
FooterStyle-CssClass="footer" >
<AlternatingRowStyle CssClass="alternating"></AlternatingRowStyle>
<Columns>
<asp:TemplateField>
<ItemTemplate>
<%--This is a placeholder for the details GridView--%>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="PetID" HeaderText="Pet ID" SortExpression="Pet ID" />
<asp:BoundField DataField="AppointmentID" HeaderText="Appointment ID"
SortExpression="AppointmentID" />
<asp:BoundField DataField="Horse Name" HeaderText="Horse Name"
SortExpression="Horse Name" />
<asp:BoundField DataField="Client Surname" HeaderText="Client Surname"
SortExpression="Client Surname" />
<asp:BoundField DataField="Senior Clinician" HeaderText="Senior Clinician"
SortExpression="Senior Clinician" />
<asp:BoundField DataField="Warning Limit"
DataFormatString="{0:£#,##0.00;(£#,##0.00);''}"
HeaderText="Warning Limit" SortExpression="Warning Limit" />
<asp:BoundField DataField="Cost"
DataFormatString="{0:£#,##0.00;(£#,##0.00);''}"
HeaderText="Cost" SortExpression="Cost" />
</Columns>
<EmptyDataTemplate>
No data to display
</EmptyDataTemplate>
<FooterStyle CssClass="footer"></FooterStyle>
<SortedAscendingHeaderStyle CssClass="sortedasc"></SortedAscendingHeaderStyle>
<SortedDescendingHeaderStyle CssClass="sorteddesc"></SortedDescendingHeaderStyle>
</asp:GridView>
<asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server">
</asp:ToolkitScriptManager>
<asp:SqlDataSource ID="SQLOverLimitList" runat="server"
ConnectionString="<%$ ConnectionStrings:DatabaseConnectionString %>"
SelectCommand="sp_Equine_OverLimitList" SelectCommandType="StoredProcedure">
</asp:SqlDataSource>
<asp:SqlDataSource ID="SQLOverLimitDetail" runat="server"
ConnectionString="<%$ ConnectionStrings:DatabaseConnectionString %>"
SelectCommand="sp_Equine_OverLimitDetail" SelectCommandType="StoredProcedure">
</asp:SqlDataSource>
这只是设置一个简单的 GridView
,即主表。 请注意,它包含一个空的 ItemTemplate
列,其中将放置“显示/隐藏”按钮。
接下来是此 GridView
的 RowDataBound
事件的后台代码
Protected Sub grdOverLimitList_RowDataBound(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _
Handles grdOverLimitList.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
'Configure the datasource for the expanded details
Dim appID As String = Convert.ToString(DataBinder.Eval_
(e.Row.DataItem, "AppointmentID")) 'Get the unique ID for this record
SQLOverLimitDetail.SelectParameters.Clear() 'Remove all select parameters
'from the datasource for the expanded details
SQLOverLimitDetail.SelectParameters.Add("AppID", appID) 'Add the select parameter
'to the datasource for the expanded details using the unique ID of the record
'Create a new GridView for displaying the expanded details
Dim gv As New GridView
gv.DataSource = SQLOverLimitDetail
gv.ID = "grdSQLOverLimitDetail" & e.Row.RowIndex 'Since a gridview is
'being created for each row they each need a unique ID, so append the row index
gv.AutoGenerateColumns = False
gv.CssClass = "subgridview"
AddHandler gv.RowDataBound, AddressOf grdOverLimitDetails_RowDataBound 'Add a
'rowdatabound method for the new GridView
'Add fields to the expanded details GridView
Dim bf1 As New BoundField
bf1.DataField = "Date Added"
bf1.DataFormatString = "{0:d}"
bf1.HeaderText = "Date Added"
gv.Columns.Add(bf1)
Dim bf2 As New BoundField
bf2.DataField = "Treatment"
bf2.HeaderText = "Treatment"
gv.Columns.Add(bf2)
Dim bf3 As New BoundField
bf3.DataField = "Total Cost"
bf3.HeaderText = "Total Cost"
bf3.DataFormatString = "{0:c}"
gv.Columns.Add(bf3)
'Create the show/hide button which will be displayed on each row of the main GridView
Dim btn As Web.UI.WebControls.Image = New Web.UI.WebControls.Image
btn.ID = "btnDetail"
btn.ImageUrl = "~/Images/detail.gif"
btn.Attributes.Add("onclick", "javascript: gvrowtoggle_
(" & e.Row.RowIndex + (e.Row.RowIndex + 2) & ")") 'Adds the javascript
'function to the show/hide button, passing the row to be toggled as a parameter
'Add the expanded details row after each record in the main GridView
Dim tbl As Table = DirectCast(e.Row.Parent, Table)
Dim tr As New GridViewRow(e.Row.RowIndex + 1, -1, _
DataControlRowType.EmptyDataRow, DataControlRowState.Normal)
tr.CssClass = "hidden"
Dim tc As New TableCell()
tc.ColumnSpan = grdOverLimitList.Columns.Count
tc.BorderStyle = BorderStyle.None
tc.BackColor = Drawing.Color.AliceBlue
tc.Controls.Add(gv) 'Add the expanded details GridView to the newly-created cell
tr.Cells.Add(tc) 'Add the newly-created cell to the newly-created row
tbl.Rows.Add(tr) ' Add the newly-ccreated row to the main GridView
e.Row.Cells(0).Controls.Add(btn) 'Add the show/hide button to the main GridView row
gv.DataBind() 'Bind the expanded details GridView to its datasource
End If
End Sub
这有大量的注释,因此希望它能解释自己,但它包含以下基本步骤
- 清除来自详细信息视图的数据源的 select 参数,并使用当前记录的唯一 ID 添加一个新参数。
- 创建一个新的
GridView
实例,并指定其数据源、ID、CSS 类和RowDataBound
方法。 这将是我们的详细信息GridView
。 - 将绑定字段添加到新的
GridView
。 - 在主记录行中创建显示/隐藏按钮,添加“
OnClick
”属性以指向下面的 JavaScript。 请注意,它实际上是一张图片,而不是一个按钮,以消除任何关于回发的疑问。 - 在主
GridView
中的当前行之后创建一个新的空行,然后用新的GridView
填充它,并将显示/隐藏按钮添加到主记录行。 - 将新的
GridView
绑定到其数据源。
您会注意到,在创建显示/隐藏按钮时,此代码将相关详细信息行的索引(我知道公式看起来有点疯狂,但相信我!)作为下面的 JavaScript 中的变量行传递。 此脚本以通常的方式位于我的 ASP.NET 页面的顶部。
<script type="text/javascript">
function gvrowtoggle(row) {
try {
row_num = row; //row to be hidden
ctl_row = row - 1; //row where show/hide button was clicked
rows = document.getElementById('<%= grdOverLimitList.ClientID %>').rows;
rowElement = rows[ctl_row]; //elements in row where show/hide button was clicked
img = rowElement.cells[0].firstChild; //the show/hide button
if (rows[row_num].className !== 'hidden') //if the row is not currently hidden
//(default)...
{
rows[row_num].className = 'hidden'; //hide the row
img.src = '../Images/detail.gif'; //change the image for the show/hide button
}
else {
rows[row_num].className = ''; //set the css class of the row to default
//(to make it visible)
img.src = '../Images/close.gif'; //change the image for the show/hide button
}
}
catch (ex) {alert(ex) }
}
</script>
一些相当简单的代码,当单击显示/隐藏按钮时,它只是检查详细信息行的当前类,如果可见则将其设置为“隐藏”,如果隐藏则将其设置为默认类。
最后,我们只需要创建一个名为“hidden
”的 CSS 类,它将隐藏详细信息行,直到被请求。 我还包括了我的 CSS 用于设置 GridView
的样式,以防您感兴趣
.hidden
{
display: none;
}
/*GridView---------------------------------------------*/
.gridview
{
width: 100%;
border: 1px solid black;
background: white;
text-align: center;
}
.gridview th
{
text-align: center;
background: #013b82;
color: white;
}
.gridview .pager
{
text-align: center;
background: #013b82;
color: White;
font-weight: bold;
border: 1px solid #013b82;
}
.gridview .pager a
{
color: #666;
}
.gridview a
{
text-decoration: none;
color: White;
}
.gridview a:hover
{
color: Silver;
}
.gridview .sortedasc
{
background-color: #336699;
}
.gridview .sortedasc a
{
padding-right: 15px;
background-image :url(../images/up_arrow.png);
background-repeat: no-repeat;
background-position: right center;
}
.gridview .sortedasc a:hover
{
color:White;
}
.gridview .sorteddesc
{
background-color: #336699;
}
.gridview .sorteddesc a
{
padding-right: 15px;
background-image :url(../images/down_arrow.png);
background-repeat: no-repeat;
background-position: right center;
}
.gridview .sorteddesc a:hover
{
color:White;
}
.gridview .alternating
{
background: #d8d8d8;
}
/*-----------------------------------------------------*/
/*SubGridView------------------------------------------*/
.subgridview
{
width:80%;
border: none;
text-align:left;
margin: 0px 0px 5px 25px;
background: whitesmoke;
}
.subgridview th
{
background: silver;
color: Black;
}
/*-----------------------------------------------------*/
关注点
这没有什么特别难的,只是需要一些思考。 一件确实让我有点抓狂的事情是,在 Firefox/Chrome 中,GridViewRow
的默认样式是“table-row”,而在 Internet Explorer 中,它是“block”。 这就是为什么在 JavaScript 中使详细信息行可见时,className
为空的原因,导致浏览器使用默认值。
历史
- 2011 年 2 月 22 日:初始帖子