使用 Repeater 控件和数据列表跨两个页面进行母版/详细信息筛选 (VB)

作者 :斯科特·米切尔

下载 PDF

在本教程中,我们将了解如何跨两页分隔主报表/详细信息报表。 在“母版”页中,我们使用 Repeater 控件呈现类别列表,单击后,会将用户带到“详细信息”页,其中两列 DataList 显示属于所选类别的产品。

介绍

“跨两页 筛选母版/详细信息筛选”教程中,我们检查了此模式,该模式使用 GridView 显示系统中的所有供应商。 此 GridView 包括一个 HyperLinkField,它呈现为指向第二个页面的链接,并传递 SupplierID 查询字符串。 第二页使用 GridView 列出所选供应商提供的这些产品。

可以使用 DataList 和 Repeater 控件完成此类双页母版/详细信息报告。 唯一的区别是,DataList 和 Repeater 都不支持 HyperLinkField 控件。 相反,我们必须在控件ItemTemplate内添加 HyperLink Web 控件或定位 HTML 元素(<a>)。 然后,可以使用声明性或编程方法自定义 HyperLink NavigateUrl 的属性或定位点 href 的属性。

在本教程中,我们将探讨一个示例,该示例使用 Repeater 控件在一页上列出项目符号列表中的类别。 每个列表项将包括类别的名称和说明,类别名称显示为指向第二页的链接。 单击此链接会将用户移动到第二页,其中 DataList 将显示属于所选类别的产品。

步骤 1:在项目符号列表中显示类别

创建任何主/详细信息报表的第一步是首先显示“master”记录。 因此,我们的第一个任务是在“母版”页中显示类别。 打开 CategoryListMaster.aspx 文件夹中的页面 DataListRepeaterFiltering ,添加 Repeater 控件,并从智能标记中选择添加新 ObjectDataSource。 配置新的 ObjectDataSource,以便从类GetCategories的方法访问其数据CategoriesBLL(请参阅图 1)。

将 ObjectDataSource 配置为使用 CategoriesBLL 类的 GetCategories 方法

图 1:将 ObjectDataSource 配置为使用 CategoriesBLLGetCategories 的方法(单击以查看全尺寸图像

接下来,定义 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:每个类别显示为项目符号列表项(单击以查看全尺寸图像

若要允许用户显示给定类别的“详细信息”信息,我们需要添加指向单击时将用户带到第二页ProductsForCategoryDetails.aspx()的每个项目符号列表项的链接。 然后,第二页将使用 DataList 显示所选类别的产品。 为了确定单击其链接的类别,我们需要通过某些机制将单击的 CategoryID 类别传递到第二页。 将标量数据从一页传输到另一个页面的最简单方法是通过查询字符串,这是我们将在本教程中使用的选项。 具体而言,页面需要通过名为 CategoryIDProductsForCategoryDetails.aspx> 的查询字符串字段传递所选categoryID值。 例如,若要查看“饮料”类别的产品(共 1 个 CategoryID ),用户将访问 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)。

类别名称现在链接到ProductsForCategoryDetails.aspx

图 3:现在链接到 ProductsForCategoryDetails.aspx 的类别名称(单击以查看全尺寸图像

步骤 3:列出属于所选类别的产品

完成页面后CategoryListMaster.aspx,我们已准备好将注意力转向实现“详细信息”页面。 ProductsForCategoryDetails.aspx 打开此页面,将工具箱中的 DataList 拖到设计器上,并将其属性设置为 ID ProductsInCategory。 接下来,从 DataList 的智能标记中选择向页面添加新的 ObjectDataSource,将其命名 ProductsInCategoryDataSource。 对其进行配置,使其调用 ProductsBLLGetProductsByCategoryID(categoryID) 的方法;将 INSERT、UPDATE 和 DELETE 选项卡中的下拉列表设置为 “无”。

将 ObjectDataSource 配置为使用 ProductsBLL 类的 GetProductsByCategoryID(categoryID) 方法

图 4:将 ObjectDataSource 配置为使用 ProductsBLLGetProductsByCategoryID(categoryID) 的方法(单击以查看全尺寸图像

由于该方法 GetProductsByCategoryID(categoryID) 接受输入参数(categoryID),“选择数据源”向导提供了指定参数源的机会。 使用 QueryStringField CategoryID将参数源设置为 QueryString。

使用 Querystring 字段 CategoryID 作为参数的源

图 5:使用 Querystring 字段 CategoryID 作为参数的源(单击可查看全尺寸图像

如前面的教程中所述,完成“选择数据源”向导后,Visual Studio 会自动为 DataList 创建一个 ItemTemplate 列出每个数据字段名称和值的 DataList。 将此模板替换为仅列出产品名称、供应商和价格的模板。 此外,将 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完成 CategoryID 查询字符串传递。 然后, ProductsInCategoryDataSource ObjectDataSource ProductsForCategoryDetails.aspx 将仅获取指定类别的这些产品,并在 DataList 中显示它们,后者每行呈现两个产品。 图 6 显示了查看饮料时的屏幕截图 ProductsForCategoryDetails.aspx

饮料显示,每行两个

图 6:显示饮料,每行两个(单击以查看全尺寸图像

步骤 4:显示有关ProductsForCategoryDetails.aspx的类别信息

当用户单击某个类别 CategoryListMaster.aspx时,它们将被带到 ProductsForCategoryDetails.aspx 并显示属于所选类别的产品。 但是,对于 ProductsForCategoryDetails.aspx 所选类别,没有视觉提示。 一个意在点击饮料的用户,但意外点击了调味品,一旦他们到达 ProductsForCategoryDetails.aspx,就没有办法意识到他们的错误。 为了缓解此潜在问题,我们可以在页面顶部 ProductsForCategoryDetails.aspx 显示有关所选类别(其名称和说明)的信息。

为此,请在 Repeater 控件 ProductsForCategoryDetails.aspx上方添加一个 FormView。 接下来,将新的 ObjectDataSource 添加到 FormView 的智能标记 CategoryDataSource 的页面,并将其配置为使用 CategoriesBLLGetCategoryByCategoryID(categoryID) 的方法。

通过 CategoriesBLL 类的 GetCategoryByCategoryID(categoryID) 方法访问有关类别的信息

图 7:通过 CategoriesBLLGetCategoryByCategoryID(categoryID) 的方法访问有关类别的信息(单击以查看全尺寸图像

ProductsInCategoryDataSource 步骤 3 中添加的 ObjectDataSource 一样, CategoryDataSource“配置数据源”向导会提示我们输入 GetCategoryByCategoryID(categoryID) 方法的输入参数的源。 使用与之前完全相同的设置,将参数源设置为 QueryString,并将 QueryStringField 值设置为 CategoryID (请参阅图 5)。

完成向导后,Visual Studio 会自动创建 FormView ItemTemplateEditItemTemplateInsertItemTemplate FormView。 由于我们提供了只读接口,因此可以随意删除 EditItemTemplateInsertItemTemplate。 此外,可以随意自定义 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 页列出系统中的所有类别,而不考虑是否有任何关联的产品。 如果用户单击没有关联产品的类别,则不会呈现 DataList ProductsForCategoryDetails.aspx ,因为其数据源将没有任何项目。 如过去教程中所述,GridView 提供了一个 EmptyDataText 属性,可用于指定文本消息,以在数据源中没有记录时显示。 遗憾的是,DataList 和 Repeater 都没有这样的属性。

为了显示一条消息,告知用户所选类别没有匹配的产品,我们需要将标签控件添加到为其属性分配给该 Text 邮件的页面,以便在没有匹配产品的情况下显示该邮件。 然后,我们需要根据 DataList 是否包含任何项以编程方式设置其 Visible 属性。

为此,请先在 DataList 下添加标签。 将其ID属性NoProductsMessage设置为“Text没有所选类别的产品...”。接下来,我们需要根据是否绑定到 ProductsInCategory DataList 来以编程方式设置此标签Visible的属性。 必须在数据绑定到 DataList 之后进行此分配。 对于 GridView、DetailsView 和 FormView,我们可以为控件 DataBound 的事件创建事件处理程序,该事件处理程序在数据绑定完成后触发。 但是,DataList 和 Repeater DataBound 都没有可用的事件。

对于此特定示例,我们可以在事件处理程序中Page_Load分配 Label Visible 的属性,因为数据将在页面Load的事件之前分配给 DataList。 但是,此方法在一般情况下不起作用,因为 ObjectDataSource 中的数据可能会在页面生命周期的后面绑定到 DataList。 例如,如果显示的数据基于另一个控件中的值,例如,当使用 DropDownList 显示主/详细信息报表以保存“master”记录时,在页面生命周期中的阶段之前 PreRender ,数据可能不会反弹到数据 Web 控件。

一种适用于所有情况的解决方案是在绑定项类型或项类型时将属性分配给 Visible DataList 的ItemDataBound事件处理程序ItemCreated中的属性ItemAlternatingItemFalse 在这种情况下,我们知道数据源中至少有一个数据项,因此可以隐藏 NoProductsMessage 标签。 除了此事件处理程序,我们还需要 DataList 事件的 DataBinding 事件处理程序,在该事件处理程序中,我们将 Label Visible 的属性初始化为 TrueDataBinding由于事件在事件之前ItemDataBound触发,因此标签Visible的属性最初将设置为True;但是,如果有任何数据项,则会将其设置为 False。 以下代码实现此逻辑:

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 数据库,将与“产品”类别(= 7)关联的所有产品重新分配到“海鲜”类别CategoryIDCategoryID = 8)。 可以通过选择“新建查询”并使用以下 UPDATE 语句,从服务器资源管理器中完成此操作:

UPDATE Products SET
    CategoryID = 8
WHERE CategoryID = 7

相应地更新数据库后,返回到 CategoryListMaster.aspx 页面并单击“生成”链接。 由于不再有任何属于“产品”类别的产品,因此应看到“所选类别没有产品...”。消息,如图 9 所示。

如果没有属于所选类别的产品,则会显示消息

图 9:如果没有属于所选类别的产品,则会显示一条消息(单击以查看全尺寸图像

总结

虽然主报表/详细信息报表可以在一个页面上同时显示主记录和详细信息记录,但在许多网站中,它们跨两个网页分开。 在本教程中,我们介绍了如何使用“母版”网页中的 Repeater 和“详细信息”页中列出的关联产品,实现此类大纲/详细信息报告。 母版网页中的每个列表项都包含一个指向沿行 CategoryID 值传递的详细信息页的链接。

在检索指定供应商的产品的详细信息页中,是通过 ProductsBLL 类的方法完成的 GetProductsByCategoryID(categoryID) 。 参数 categoryID 值是使用 CategoryID querystring 值作为参数源以声明方式指定的。 我们还了解了如何使用 FormView 在详细信息页中显示类别详细信息,以及如何显示消息(如果没有属于所选类别的产品)。

快乐编程!

关于作者

斯科特·米切尔,七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0。 他可以通过他的博客联系到mitchell@4GuysFromRolla.com他,可以在该博客中找到http://ScottOnWriting.NET

特别感谢...

本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Zack Jones 和 Liz Shulok。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请把我扔一条线。mitchell@4GuysFromRolla.com