使用 Repeater 控件和 DataList (VB) 跨两页筛选母版/详细信息

作者 :Scott Mitchell

下载 PDF

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

简介

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

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

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

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

创建任何主/详细信息报表的第一步是首先显示“主”记录。 因此,我们的第一个任务是在“母版”页中显示类别。 CategoryListMaster.aspx打开 文件夹中的页面DataListRepeaterFiltering,添加 Repeater 控件,并从智能标记中选择添加新的 ObjectDataSource。 配置新的 ObjectDataSource,以便它从 CategoriesBLL 类的 GetCategories 方法访问其数据 (请参阅图 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 类别传递到第二页。 将标量数据从一个页面传输到另一个页面的最简单直接方法是通过 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) 。

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

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

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

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

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

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

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

使用查询字符串字段 CategoryID 作为参数的源

图 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 。 然后,中的 ProductsInCategoryDataSourceProductsForCategoryDetails.aspx ObjectDataSource 将仅获取指定类别的产品,并将它们显示在 DataList 中,后者每行呈现两个产品。 图 6 显示了查看饮料时的屏幕截图 ProductsForCategoryDetails.aspx

显示饮料,每行两个

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

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

当用户单击 中的 CategoryListMaster.aspx类别时,会转到 ProductsForCategoryDetails.aspx 并显示属于所选类别的产品。 但是,在 中 ProductsForCategoryDetails.aspx ,没有有关所选类别的视觉提示。 用户本应单击“饮料”,但意外单击了“调味品”,一旦他们到达 ProductsForCategoryDetails.aspx,就无法意识到自己的错误。 为了缓解此潜在问题,我们可以在页面顶部 ProductsForCategoryDetails.aspx 显示有关所选类别的信息(其名称和说明)。

为此,请在 中的 ProductsForCategoryDetails.aspxRepeater 控件上方添加 FormView。 接下来,从名为 CategoryDataSource FormView 的智能标记将新的 ObjectDataSource 添加到页面,并将其配置为使用 CategoriesBLL 类的 GetCategoryByCategoryID(categoryID) 方法。

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

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

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

完成向导后,Visual Studio 会自动为 FormView 创建 ItemTemplateEditItemTemplateInsertItemTemplate 。 由于我们提供了只读接口,因此可以随意删除 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 页面列出系统中的所有类别,无论是否有任何关联的产品。 如果用户单击没有关联产品的类别,则不会呈现 中的 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 页面生命周期的阶段。

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

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放置一行。