使用 Repeater 控件和 DataList (VB) 跨两页筛选母版/详细信息
在本教程中,我们将了解如何跨两页分隔大纲/详细信息报表。 在“母版”页中,我们使用 Repeater 控件呈现类别列表,单击该列表会将用户带到“详细信息”页,其中两列 DataList 显示属于所选类别的产品。
简介
在 “跨两页筛选母版/详细信息筛选” 教程中,我们检查了使用 GridView 显示系统中所有供应商的模式。 此 GridView 包含一个 HyperLinkField,该字段呈现为指向第二个页面的链接,并在 querystring 中传递 SupplierID
。 第二页使用 GridView 列出所选供应商提供的产品。
此类双页母版/详细信息报表也可以使用 DataList 和 Repeater 控件来完成。 唯一的区别在于,DataList 和 Repeater 都不支持 HyperLinkField 控件。 相反,我们必须在控件的 ItemTemplate
中添加 HyperLink Web 控件或定位点 HTML 元素 (<a>
) 。 然后,可以使用声明性或编程方法自定义 HyperLink NavigateUrl
的 href
属性或定位点的 属性。
在本教程中,我们将探索一个示例,该示例使用 Repeater 控件在一页上列出项目符号列表中的类别。 每个列表项将包括类别的名称和说明,类别名称显示为指向第二页的链接。 单击此链接会将用户移动到第二个页面,其中 DataList 将显示属于所选类别的产品。
步骤 1:在项目符号列表中显示类别
创建任何主/详细信息报表的第一步是首先显示“主”记录。 因此,我们的第一个任务是在“母版”页中显示类别。 CategoryListMaster.aspx
打开 文件夹中的页面DataListRepeaterFiltering
,添加 Repeater 控件,并从智能标记中选择添加新的 ObjectDataSource。 配置新的 ObjectDataSource,以便它从 CategoriesBLL
类的 GetCategories
方法访问其数据 (请参阅图 1) 。
图 1:将 ObjectDataSource 配置为使用 CategoriesBLL
类 GetCategories
的方法 (单击以查看全尺寸图像)
接下来,定义 Repeater 的模板,使其将每个类别名称和说明显示为项目符号列表中的一项。 我们不必担心将每个类别链接指向详细信息页面。 下面显示了 Repeater 和 ObjectDataSource 的声明性标记:
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="ObjectDataSource1"
EnableViewState="False">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><%# Eval("CategoryName") %> - <%# Eval("Description") %></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
完成此标记后,请花点时间通过浏览器查看进度。 如图 2 所示,Repeater 呈现为项目符号列表,其中显示了每个类别的名称和说明。
图 2:每个类别显示为项目符号列表项 (单击以查看全尺寸图像)
步骤 2:将类别名称转换为“详细信息”页的链接
若要允许用户显示给定类别的“详细信息”信息,我们需要添加指向每个项目符号列表项的链接,单击该链接会将用户带到第二页, (ProductsForCategoryDetails.aspx
) 。 然后,第二页将使用 DataList 显示所选类别的产品。 为了确定单击链接的类别,我们需要通过某种机制将单击的 CategoryID
类别传递到第二页。 将标量数据从一个页面传输到另一个页面的最简单直接方法是通过 querystring,我们将在本教程中使用的选项。 具体而言,页面ProductsForCategoryDetails.aspx
需要通过名为 CategoryID
的查询字符串字段传递所选categoryID
值。 例如,若要查看“饮料”类别的产品(其 为 CategoryID
1),用户将访问 ProductsForCategoryDetails.aspx?CategoryID=1
。
若要为 Repeater 中的每个项目符号列表项创建超链接,我们需要将 HyperLink Web 控件或 HTML 定位点元素 (<a>
) 添加到 ItemTemplate
。 在每行的超链接显示相同的情况下,这两种方法都足够了。 对于 Repeaters,我更喜欢使用定位点元素。 若要使用定位点元素,请将 Repeater 的 ItemTemplate 更新为:
<li>
<a href='ProductsForCategoryDetails.aspx?CategoryID=<%# Eval("CategoryID") %>'>
<%# Eval("CategoryName") %>
</a> - <%# Eval("Description") %>
</li>
请注意, CategoryID
可以直接在定位点元素的 href
属性中注入;但是,若要执行此操作,请务必 href
用撇号 (分隔属性值,) 记下引号,因为 Eval
属性内 href
的方法用引号分隔其字符串 ("CategoryID"
) 引号。 或者,可以改用 HyperLink Web 控件:
<li>
<asp:HyperLink runat="server" Text='<%# Eval("CategoryName") %>'
NavigateUrl='<%# "ProductsForCategoryDetails.aspx?CategoryID=" &
Eval("CategoryID") %>'>
</asp:HyperLink>
- <%# Eval("Description") %>
</li>
请注意,URL 的静态部分如何使用 ProductsForCategoryDetails.aspx?CategoryID
字符串串联直接追加到数据绑定语法中的结果 Eval("CategoryID")
中。
使用 HyperLink 控件的一个好处是,如果需要,可以从 Repeater 的 ItemDataBound
事件处理程序以编程方式访问它。 例如,你可能希望将类别名称显示为文本,而不是作为没有关联产品的类别的链接。 此类检查可以在事件处理程序中ItemDataBound
以编程方式执行;对于没有关联产品的类别,HyperLink 的 NavigateUrl
属性可以设置为空白字符串,从而导致该特定类别名称呈现为纯文本 (而不是链接) 。 有关通过ItemDataBound
事件处理程序基于编程逻辑设置 DataList 和 Repeater 内容格式的详细信息,请参阅设置 DataList 和 Repeater 基于数据的格式教程。
如果继续操作,请随意在页面中使用定位点元素或 HyperLink 控件方法。 无论采用哪种方法,在通过浏览器查看页面时,每个类别名称都应呈现为指向 的链接 ProductsForCategoryDetails.aspx
,传入适用的 CategoryID
值 (请参阅图 3) 。
图 3:类别名称现在链接到 ProductsForCategoryDetails.aspx
(单击以查看全尺寸图像)
步骤 3:列出属于所选类别的产品
页面 CategoryListMaster.aspx
完成后,我们可以将注意力转向实现“详细信息”页 ProductsForCategoryDetails.aspx
。 打开此页,将“工具箱”中的 DataList 拖到Designer,并将其 属性设置为 ID
ProductsInCategory
。 接下来,从 DataList 的智能标记中选择将新的 ObjectDataSource 添加到页面,并将其 ProductsInCategoryDataSource
命名为 。 对其进行配置,使其调用 ProductsBLL
类的 GetProductsByCategoryID(categoryID)
方法;将 INSERT、UPDATE 和 DELETE 选项卡中的下拉列表设置为 (None) 。
图 4:将 ObjectDataSource 配置为使用 ProductsBLL
类的方法 GetProductsByCategoryID(categoryID)
(单击以查看全尺寸图像)
GetProductsByCategoryID(categoryID)
由于 方法接受输入参数 (categoryID
) ,选择数据源向导为我们提供了指定参数源的机会。 使用 QueryStringField CategoryID
将参数源设置为 QueryString。
图 5:使用查询字符串字段 CategoryID
作为参数的源 (单击以查看全尺寸图像)
正如我们在前面的教程中看到的,在完成“选择数据源”向导后,Visual Studio 会自动为列出每个数据字段名称和值的 DataList 创建 ItemTemplate
。 将此模板替换为仅列出产品名称、供应商和价格的模板。 此外,将 DataList 的 RepeatColumns
属性设置为 2。 进行这些更改后,DataList 和 ObjectDataSource 的声明性标记应如下所示:
<asp:DataList ID="ProductsInCategory" DataKeyField="ProductID" RepeatColumns="2"
DataSourceID="ProductsInCategoryDataSource" EnableViewState="False"
runat="server">
<ItemTemplate>
<h5><%# Eval("ProductName") %></h5>
<p>
Supplied by <%# Eval("SupplierName") %><br />
<%# Eval("UnitPrice", "{0:C}") %>
</p>
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="ProductsInCategoryDataSource"
OldValuesParameterFormatString="original_{0}" runat="server"
SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
<SelectParameters>
<asp:QueryStringParameter Name="categoryID" QueryStringField="CategoryID"
Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
若要查看此页的操作,请从 CategoryListMaster.aspx
页面开始;接下来,单击类别项目符号列表中的链接。 这样做会将你带到 ProductsForCategoryDetails.aspx
,通过 querystring 传递 CategoryID
。 然后,中的 ProductsInCategoryDataSource
ProductsForCategoryDetails.aspx
ObjectDataSource 将仅获取指定类别的产品,并将它们显示在 DataList 中,后者每行呈现两个产品。 图 6 显示了查看饮料时的屏幕截图 ProductsForCategoryDetails.aspx
。
图 6:显示饮料,每行两个 (单击以查看全尺寸图像)
步骤 4:显示ProductsForCategoryDetails.aspx的类别信息
当用户单击 中的 CategoryListMaster.aspx
类别时,会转到 ProductsForCategoryDetails.aspx
并显示属于所选类别的产品。 但是,在 中 ProductsForCategoryDetails.aspx
,没有有关所选类别的视觉提示。 用户本应单击“饮料”,但意外单击了“调味品”,一旦他们到达 ProductsForCategoryDetails.aspx
,就无法意识到自己的错误。 为了缓解此潜在问题,我们可以在页面顶部 ProductsForCategoryDetails.aspx
显示有关所选类别的信息(其名称和说明)。
为此,请在 中的 ProductsForCategoryDetails.aspx
Repeater 控件上方添加 FormView。 接下来,从名为 CategoryDataSource
FormView 的智能标记将新的 ObjectDataSource 添加到页面,并将其配置为使用 CategoriesBLL
类的 GetCategoryByCategoryID(categoryID)
方法。
图 7:通过 CategoriesBLL
类 GetCategoryByCategoryID(categoryID)
的方法访问有关类别的信息 (单击以查看全尺寸图像)
与 ProductsInCategoryDataSource
步骤 3 中添加的 ObjectDataSource 一样, CategoryDataSource
的“配置数据源”向导会提示我们输入方法的输入参数的源 GetCategoryByCategoryID(categoryID)
。 使用与之前完全相同的设置,将参数源设置为 QueryString,将 QueryStringField 值设置为 CategoryID
(请参阅图 5) 。
完成向导后,Visual Studio 会自动为 FormView 创建 ItemTemplate
、 EditItemTemplate
和 InsertItemTemplate
。 由于我们提供了只读接口,因此可以随意删除 EditItemTemplate
和 InsertItemTemplate
。 此外,请随意自定义 FormView 的 ItemTemplate
。 删除多余的模板并自定义 ItemTemplate 后,FormView 和 ObjectDataSource 的声明性标记应如下所示:
<asp:FormView ID="FormView1" runat="server" DataKeyNames="CategoryID"
DataSourceID="CategoryDataSource" EnableViewState="False" Width="100%">
<ItemTemplate>
<h3>
<asp:Label ID="CategoryNameLabel" runat="server"
Text='<%# Bind("CategoryName") %>' />
</h3>
<p>
<asp:Label ID="DescriptionLabel" runat="server"
Text='<%# Bind("Description") %>' />
</p>
</ItemTemplate>
</asp:FormView>
<asp:ObjectDataSource ID="CategoryDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategoryByCategoryID" TypeName="CategoriesBLL">
<SelectParameters>
<asp:QueryStringParameter Name="categoryID" Type="Int32"
QueryStringField="CategoryID" />
</SelectParameters>
</asp:ObjectDataSource>
图 8 显示了通过浏览器查看此页面时的屏幕截图。
注意
除了 FormView 之外,我还在 FormView 上方添加了一个 HyperLink 控件,该控件会将用户带回) (CategoryListMaster.aspx
类别列表。 请随意将此链接放在其他位置或完全省略它。
图 8:类别信息现在显示在页面顶部 (单击以查看全尺寸图像)
步骤 5:如果没有任何产品属于所选类别,则显示消息
该 CategoryListMaster.aspx
页面列出系统中的所有类别,无论是否有任何关联的产品。 如果用户单击没有关联产品的类别,则不会呈现 中的 ProductsForCategoryDetails.aspx
DataList,因为其数据源将没有任何项。 正如我们在以前的教程中看到的那样,GridView 提供了一个 EmptyDataText
属性,该属性可用于指定在其数据源中没有记录时要显示的文本消息。 遗憾的是,DataList 和 Repeater 都没有此类属性。
为了显示一条消息,通知用户所选类别没有匹配的产品,我们需要向页面添加一个 Label 控件,该 Text
页的属性分配了在没有匹配产品时显示的消息。 然后,我们需要根据 DataList 是否包含任何项,以编程方式设置其 Visible
属性。
为此,请先在 DataList 下添加标签。 将其 ID
属性设置为 NoProductsMessage
,将其 Text
属性设置为“没有所选类别的产品...”接下来,我们需要根据是否已将任何数据绑定到 DataList 以编程方式设置此 Label Visible
的属性 ProductsInCategory
。 必须在数据绑定到 DataList 之后进行此分配。 对于 GridView、DetailsView 和 FormView,我们可以为控件 DataBound
的事件创建事件处理程序,该事件处理程序在数据绑定完成后触发。 但是,DataList 和 Repeater DataBound
都没有可用的事件。
对于此特定示例,我们可以在事件处理程序中Page_Load
分配 Label 的 Visible
属性,因为数据将在页面Load
的事件之前分配给 DataList。 但是,此方法在一般情况下不起作用,因为 ObjectDataSource 中的数据可能会在页面生命周期的后面绑定到 DataList。 例如,如果显示的数据基于另一个控件中的值,例如在使用 DropDownList 保存“主”记录时显示主/详细信息报表时,数据可能不会反弹到数据 Web 控件,直到 PreRender
页面生命周期的阶段。
适用于所有情况的一种解决方案是在绑定项类型 AlternatingItem
Item
或 时,在 DataList 的 ItemDataBound
(或 ItemCreated
) 事件处理程序中将 属性False
分配给 Visible
。 在这种情况下,我们知道数据源中至少有一个数据项,因此可以隐藏 NoProductsMessage
标签。 除了此事件处理程序,我们还需要 DataList DataBinding
事件的事件处理程序,其中将 Label 的 Visible
属性初始化为 True
。 由于事件在事件之前ItemDataBound
触发,因此 Label 的 Visible
属性最初将设置为 True
;但是,如果有任何数据项,则会将其设置为 False
。DataBinding
以下代码实现此逻辑:
Protected Sub ProductsInCategory_DataBinding(sender As Object, e As EventArgs) _
Handles ProductsInCategory.DataBinding
'Show the Label
NoProductsMessage.Visible = True
End Sub
Protected Sub ProductsInCategory_ItemDataBound(s As Object, e As DataListItemEventArgs) _
Handles ProductsInCategory.ItemDataBound
'If we have a data item, hide the Label
If e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = _
ListItemType.AlternatingItem Then
NoProductsMessage.Visible = False
End If
End Sub
Northwind 数据库中的所有类别都与一个或多个产品相关联。 为了测试此功能,我已手动调整了本教程的 Northwind 数据库,将与“农产品”类别 (CategoryID
= 7) 关联的所有产品重新分配到“海鲜”类别 (CategoryID
= 8) 。 这可以通过选择“新建查询”并使用以下 UPDATE
语句在服务器资源管理器中完成:
UPDATE Products SET
CategoryID = 8
WHERE CategoryID = 7
相应地更新数据库后,返回到 CategoryListMaster.aspx
页面并单击“生成”链接。 由于不再有任何产品属于“生产”类别,应会看到“没有所选类别的产品...”。消息,如图 9 所示。
图 9:如果没有属于所选类别的产品,则显示一条消息 (单击以查看全尺寸图像)
总结
虽然母版/详细信息报表可以在单个页面上显示主控记录和详细信息记录,但在许多网站中,它们跨两个网页进行分隔。 在本教程中,我们介绍了如何通过使用“master”网页中的 Repeater 以及“详细信息”页中列出的关联产品,在项目符号列表中列出类别来实现此类主/详细信息报表。 母版网页中的每个列表项都包含一个指向沿行 CategoryID
值传递的详细信息页的链接。
在详细信息页中,通过 ProductsBLL
类的 方法检索指定供应商的 GetProductsByCategoryID(categoryID)
这些产品。 参数 categoryID
值是使用 CategoryID
查询字符串值作为参数源以声明方式指定的。 我们还了解了如何使用 FormView 在详细信息页中显示类别详细信息,以及如果没有属于所选类别的产品,如何显示消息。
编程快乐!
关于作者
斯科特·米切尔是七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自 1998 年以来一直在使用 Microsoft Web 技术。 Scott 担任独立顾问、培训师和作家。 他的最新一本书是 山姆斯在 24 小时内 ASP.NET 2.0。 可以在 上mitchell@4GuysFromRolla.com联系他,也可以通过他的博客(可在 中找到http://ScottOnWriting.NET)。
特别感谢...
本教程系列由许多有用的审阅者审阅。 本教程的首席审阅者是 Zack Jones 和 Liz Shulok。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处mitchell@4GuysFromRolla.com放置一行。
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈