添加 GridView 单选按钮列 (C#)

作者 :斯科特·米切尔

下载 PDF

本教程介绍如何在 GridView 控件中添加一个单选按钮列,为用户提供一种更直观的方法来选择 GridView 中的单个行。

介绍

GridView 控件提供了大量内置功能。 它包含许多用于显示文本、图像、超链接和按钮的不同字段。 它支持用于进一步自定义的模板。 只需单击几下鼠标,即可创建 GridView,以便通过按钮选择每一行,或者启用编辑或删除功能。 尽管提供了大量功能,但经常会出现需要添加其他不支持的功能的情况。 在本教程和接下来的两个教程中,我们将探讨如何增强 GridView 的功能,以包含其他功能。

本教程和下一篇重点介绍如何增强行选择过程。 如在 主/详细信息中使用可选择的主 GridView 和 Detail DetailView进行研究,我们可以在 GridView 中添加一个 CommandField,其中包括一个选择按钮。 单击后,将发出回发请求,GridView 的 SelectedIndex 属性会更新为“选择”按钮被单击的行的索引。 在 母版/详细信息中使用可选主 GridView 和 Details DetailView 教程,我们了解了如何使用此功能显示所选 GridView 行的详细信息。

虽然“选择”按钮在很多情况下都有效,但它可能不适用于其他按钮。 除了使用按钮,其他两个用户界面元素通常用于选择:单选按钮和复选框。 我们可以改进 GridView,使每行包含单选按钮或复选框,而不是选择按钮。 当用户在场景中只能选择一个 GridView 记录时,可以优先考虑使用单选按钮而不是“选择”按钮。 在类似于基于 Web 的电子邮件应用程序中,复选框提供用户无法通过“选择”按钮或单选按钮用户界面实现的功能,例如在需要选择和删除多个邮件记录的情况下。

本教程介绍如何向 GridView 添加单选按钮列。 接下来的教程将探索复选框的用法。

步骤 1:创建增强 GridView 网页

在开始增强 GridView 以包含单选按钮列之前,让我们先花点时间在网站项目中创建 ASP.NET 页面,本教程和接下来的两个页面都需要这些页面。 首先添加名为 <a0/a0> 的新文件夹。 接下来,将以下 ASP.NET 页添加到该文件夹,确保将每个页面与 Site.master 母版页相关联:

  • Default.aspx
  • RadioButtonField.aspx
  • CheckBoxField.aspx
  • InsertThroughFooter.aspx

为 SqlDataSource-Related 教程添加 ASP.NET 页

图 1:为 SqlDataSource-Related 教程添加 ASP.NET 页

与其他文件夹中一样, Default.aspxEnhancedGridView 文件夹中将列出其部分中的教程。 回想一下, SectionLevelTutorialListing.ascx 用户控件提供了此功能。 因此,通过将此用户控件从解决方案资源管理器拖动到页面的设计视图中,将它添加到Default.aspx

将 SectionLevelTutorialListing.ascx 用户控件添加到 Default.aspx

图 2:将用户控件Default.aspx添加到 SectionLevelTutorialListing.ascx单击以查看全尺寸图像

最后,将这四个页面作为条目添加到 Web.sitemap 文件中。 具体而言,请在使用 SqlDataSource 控件 <siteMapNode>后添加以下标记:

<siteMapNode 
    title="Enhancing the GridView" 
    url="~/EnhancedGridView/Default.aspx" 
    description="Augment the user experience of the GridView control.">
    <siteMapNode 
        url="~/EnhancedGridView/RadioButtonField.aspx" 
        title="Selection via a Radio Button Column" 
        description="Explore how to add a column of radio buttons in the GridView." />
    <siteMapNode 
        url="~/EnhancedGridView/CheckBoxField.aspx" 
        title="Selection via a Checkbox Column" 
        description="Select multiple records in the GridView by using a column of 
            checkboxes." />
    <siteMapNode 
        url="~/EnhancedGridView/InsertThroughFooter.aspx" 
        title="Add New Records through the Footer" 
        description="Learn how to allow users to add new records through the 
            GridView's footer." />
</siteMapNode>

更新 Web.sitemap后,请花点时间通过浏览器查看教程网站。 左侧菜单现在包括用于编辑、插入和删除教程的项目。

站点地图现在包括增强 GridView 教程的条目

图 3:网站地图现在包含用于增强 GridView 教程的条目

步骤 2:在 GridView 中显示供应商

在本教程中,我们生成一个 GridView,其中列出了来自美国的供应商,每个 GridView 行都提供单选按钮。 通过单选按钮选择供应商后,用户可以通过单击按钮查看供应商的产品。 虽然此任务听起来可能很微不足道,但有许多微妙之处,使其特别棘手。 在深入探讨这些微妙之处之前,让我们先获取一个列出供应商的 GridView。

首先,打开EnhancedGridView文件夹中的RadioButtonField.aspx页面,然后从工具箱中拖出一个GridView到设计器。 将 GridView 设置为IDSuppliers,并从其智能标记中选择创建新的数据源。 具体而言,创建一个名为 SuppliersDataSource 的 ObjectDataSource,它从 SuppliersBLL 对象中提取数据。

创建一个名为 SuppliersDataSource 的新 ObjectDataSource

图 4:新建名为 SuppliersDataSource ObjectDataSource (单击以查看全尺寸图像

“配置数据源 - SuppliersDataSource”窗口的屏幕截图,其中选择了业务对象 SuppliersBLL,并突出显示了“下一步”按钮。

图 5:将 ObjectDataSource 配置为使用 SuppliersBLL 类(单击以查看全尺寸图像

由于我们只想列出美国的这些供应商, GetSuppliersByCountry(country) 因此请从 SELECT 选项卡中的下拉列表中选择该方法。

“配置数据源 - SuppliersDataSource”窗口的屏幕截图,其中已打开 SELECT 选项卡。已选择 GetSupplierByCountry 方法选项,并突出显示“下一步”按钮。

图 6:将 ObjectDataSource 配置为使用 SuppliersBLL 类(单击以查看全尺寸图像

在“更新”选项卡中,选择“无”选项,然后单击“下一步”。

“配置数据源 - SuppliersDataSource”窗口的屏幕截图,其中打开了“更新”选项卡。选择方法选项(无),并突出显示“下一步”按钮。

图 7:将 ObjectDataSource 配置为使用 SuppliersBLL 类(单击以查看全尺寸图像

GetSuppliersByCountry(country)由于该方法接受参数,“配置数据源”向导会提示我们输入该参数的源。 若要指定硬编码值(美国,在此示例中),请将“参数源”下拉列表保留为“无”,并在文本框中输入默认值。 单击“完成”以完成向导。

将 USA 用作国家/地区参数的默认值

图 8:使用 USA 作为参数的 country 默认值(单击以查看全尺寸图像

完成向导后,GridView 将为每个供应商数据字段添加一个 BoundField。 删除除 CompanyNameCityCountry BoundFields 以外的所有 BoundFields,并将 CompanyName BoundFields 的 HeaderText 属性重命名为“供应商”。 执行此作后,GridView 和 ObjectDataSource 声明性语法应如下所示。

<asp:GridView ID="Suppliers" runat="server" AutoGenerateColumns="False"
    DataKeyNames="SupplierID" DataSourceID="SuppliersDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" 
            SortExpression="CompanyName" />
        <asp:BoundField DataField="City" HeaderText="City" 
            SortExpression="City" />
        <asp:BoundField DataField="Country" HeaderText="Country" 
            SortExpression="Country" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="SuppliersDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetSuppliersByCountry" TypeName="SuppliersBLL">
    <SelectParameters>
        <asp:Parameter DefaultValue="USA" Name="country" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>

在本教程中,允许用户选择查看所选供应商的产品,可以与供应商列表所在页面相同,也可以在其他页面查看。 若要适应这一点,请将两个按钮 Web 控件添加到页面。 我已将这两个按钮的ID设置为ListProductsSendToProducts,我的想法是当点击ListProducts时,会发生回发并将在同一页面列出所选供应商的产品,但点击SendToProducts时,用户将被引导到一个列出产品的另一个页面。

图 9 显示了 Suppliers 通过浏览器查看的 GridView 和两个按钮 Web 控件。

美国供应商的姓名、城市和国家/地区信息已列出

图 9:美国供应商的姓名、城市和国家/地区信息已列出(单击可查看全尺寸图像

步骤 3:添加单选按钮列

此时,Suppliers GridView 已显示三个 BoundFields,分别展示美国供应商的公司名称、所在城市和国家。 但是,它仍然缺少单选按钮列。 遗憾的是,GridView 不包含内置 RadioButtonField,否则我们只需将它添加到网格中即可完成。 相反,我们可以添加一个 TemplateField,并配置其 ItemTemplate 以呈现单选按钮,从而在每个 GridView 行中生成一个单选按钮。

最初,我们假设可以通过将 RadioButton Web 控件添加到 ItemTemplate TemplateField 来实现所需的用户界面。 虽然这确实会将单个单选按钮添加到 GridView 的每一行,但单选按钮不能分组,因此不是相互排斥的。 也就是说,最终用户可以从 GridView 中同时选择多个单选按钮。

尽管使用 RadioButton Web 控件的 TemplateField 不提供我们需要的功能,但让我们实现此方法,因为有必要检查生成的单选按钮未分组的原因。 首先,将 TemplateField 添加到供应商 GridView,使其成为最左侧的字段。 接下来,在 GridView 智能标记中,单击“编辑模板”链接,并将 RadioButton Web 控件从工具箱拖动到 TemplateField s( ItemTemplate 请参阅图 10)。 将 RadioButton s ID 属性设置为 RowSelector ,并将 GroupName 属性设置为 SuppliersGroup.

向 ItemTemplate 添加 RadioButton Web 控件

图 10:向 ItemTemplate 添加 RadioButton Web 控件 (单击以查看全尺寸图像

通过设计器进行这些添加后,GridView 的标记应如下所示:

<asp:GridView ID="Suppliers" runat="server" AutoGenerateColumns="False"
    DataKeyNames="SupplierID" DataSourceID="SuppliersDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:RadioButton ID="RowSelector" runat="server" 
                    GroupName="SuppliersGroup" />
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" 
            SortExpression="CompanyName" />
        <asp:BoundField DataField="City" HeaderText="City" 
            SortExpression="City" />
        <asp:BoundField DataField="Country" HeaderText="Country" 
            SortExpression="Country" />
    </Columns>
</asp:GridView>

RadioButton 的 GroupName 属性 用于将一系列单选按钮进行分组。 具有相同 GroupName 值的所有 RadioButton 控件都被视为分组;一次只能从组中选择一个单选按钮。 该 GroupName 属性指定呈现的单选按钮属性 name 的值。 浏览器检查单选按钮 name 属性以确定单选按钮分组。

将 RadioButton Web 控件添加到 ItemTemplate 后,通过浏览器访问该页面,并单击网格行中的单选按钮。 注意单选按钮没有被分组,这使得可以选择所有行,如图 11 所示。

GridView 中的单选按钮没有分组

图 11:GridView 的单选按钮未分组(单击以查看全尺寸图像

单选按钮未分组的原因是,尽管它们具有相同的GroupName属性设置,但由于它们的呈现name属性不同。 若要查看这些差异,请在浏览器中打开“查看源码”并检查单选按钮代码:

<input id="ctl00_MainContent_Suppliers_ctl02_RowSelector" 
    name="ctl00$MainContent$Suppliers$ctl02$SuppliersGroup" 
    type="radio" value="RowSelector" />
<input id="ctl00_MainContent_Suppliers_ctl03_RowSelector" 
    name="ctl00$MainContent$Suppliers$ctl03$SuppliersGroup" 
    type="radio" value="RowSelector" />
<input id="ctl00_MainContent_Suppliers_ctl04_RowSelector" 
    name="ctl00$MainContent$Suppliers$ctl04$SuppliersGroup" 
    type="radio" value="RowSelector" />
<input id="ctl00_MainContent_Suppliers_ctl05_RowSelector" 
    name="ctl00$MainContent$Suppliers$ctl05$SuppliersGroup" 
    type="radio" value="RowSelector" />

请注意,nameid属性不是在“属性”窗口中指定的确切值,而是前面加了一些其他ID值。 添加到呈现idname属性前面的附加ID值是单选按钮父控件的ID、GridView的GridViewRow、内容控件的ID、Web窗体的ID。 添加这些 ID,以便 GridView 中每个呈现的 Web 控件都具有唯一的 id 值和 name 值。

每个呈现的控件都需要不同的nameid,因为这是浏览器唯一标识客户端上每个控件的方式,以及它如何向 Web 服务器标识在回发时发生的动作或更改。 例如,假设我们希望每当 RadioButton 的选中状态发生变化时运行一些服务器端代码。 可以通过将 RadioButton s AutoPostBack 属性设置为 true 并为 CheckChanged 事件创建事件处理程序来实现此目的。 但是,如果所有单选按钮的呈现 nameid 值相同,则回发时无法确定哪个特定的单选按钮被点击。

简而言之,我们不能使用 RadioButton Web 控件在 GridView 中创建单选按钮的整列。 相反,我们必须使用相当古老的技术来确保将适当的标记注入到每个 GridView 行中。

注释

与 RadioButton Web 控件一样,单选按钮 HTML 控件添加到模板时,将包含唯一name属性,使网格中的单选按钮不再分组。 如果你不熟悉 HTML 控件,请随意忽略此注释,因为很少使用 HTML 控件,尤其是在 ASP.NET 2.0 中。 但是,如果你有兴趣了解详细信息,请参阅 K. Scott Allen 的博客文章 Web 控件和 HTML 控件

使用文本控件注入单选按钮标记

为了正确对 GridView 中的所有单选按钮进行分组,我们需要手动将单选按钮标记注入其中 ItemTemplate。 每个单选按钮都需要相同的 name 属性,但应具有唯 id 一属性(如果我们想要通过客户端脚本访问单选按钮)。 用户选择单选按钮并回发页面后,浏览器将发送回所选单选按钮属性 value 的值。 因此,每个单选按钮都需要一个唯一的value属性。 最后,在回发时,我们需要确保将 checked 属性添加到选中的一个单选按钮,否则在用户做出选择并回发后,单选按钮将返回到其默认状态(所有未选中状态)。

有两种方法可用于将低级别标记注入模板。 一种方法是混合使用标记和调用代码隐藏类中定义的格式设置方法。 此方法首先在 GridView 控件教程的 Using TemplateFields 中 进行了讨论。 在本例中,它可能如下所示:

<input type="radio" id='<%# GetUniqueRadioButtonID(...) %>' 
    name='SuppliersGroup' value='<%# GetRadioButtonValue(...) %>' ... />

代码隐藏类中定义了 GetUniqueRadioButtonGetRadioButtonValue 这两个方法,它们会返回每个单选按钮的相应 idvalue 属性值。 此方法适用于分配 id 属性和 value 属性,然而在需要指定 checked 属性值时会显得效果不佳,因为数据绑定语法仅在数据首次绑定到 GridView 时才执行。 因此,如果 GridView 已启用视图状态,则格式设置方法仅在首次加载页面时触发(或者当 GridView 显式反弹到数据源时),因此设置 checked 属性的函数不会在回发时调用。 这是一个相当微妙的问题,有点超出本文的范围,所以我就到这里。 不过,我确实鼓励你尝试使用上述方法,并将其执行到遇到困难的程度。 虽然此类练习不会让你更接近工作版本,但它将有助于深入了解 GridView 和数据绑定生命周期。

在模板中注入自定义的低级别标记以及本教程将使用的方法的另一种方法是向模板添加 文本控件 。 然后,在 GridView s RowCreatedRowDataBound 事件处理程序中,可以通过编程方式访问文本控件,并将其 Text 属性设置为要发出的标记。

首先从 TemplateField s ItemTemplate中删除 RadioButton,并将其替换为文本控件。 将文本控件 ID 设置为 RadioButtonMarkup.

向 ItemTemplate 添加文本控件

图 12:向ItemTemplate添加文本控件(单击查看完整尺寸图像

接下来,为 GridView 事件 RowCreated 创建事件处理程序。 对于添加的每一行,事件 RowCreated 都会触发一次,无论数据是否正在反弹到 GridView。 这意味着,即使是在从视图状态重新加载数据的回发过程中,RowCreated 事件仍然会触发。这也是我们使用它而不是 RowDataBound 的原因,因为 RowDataBound 仅在数据显式绑定到数据 Web 控件时才会触发。

在此事件处理程序中,仅当处理数据行时,我们才希望继续。 对于每个数据行,我们希望以编程方式引用 RadioButtonMarkup Literal 控件,并将其 Text 属性设置为要输出的标记。 如以下代码所示,发出的标记将创建一个单选按钮,其 name 属性设置为 SuppliersGroup,其 id 属性设置为 RowSelectorX,其中 X 是 GridView 行的索引,其 value 属性也设置为 GridView 行的索引。

protected void Suppliers_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // Grab a reference to the Literal control
        Literal output = (Literal)e.Row.FindControl("RadioButtonMarkup");
        // Output the markup except for the "checked" attribute
        output.Text = string.Format(
            @"<input type="radio" name="SuppliersGroup" " +
            @"id="RowSelector{0}" value="{0}" />", e.Row.RowIndex);
    }
}

选择了 GridView 的行并发生回发时,我们对所选供应商的 SupplierID 感兴趣。 因此,人们可能会认为每个单选按钮的实际值应是 SupplierID (而不是 GridView 行的索引)。 虽然在某些情况下可能有效,但盲目接受和处理 SupplierID 可能会导致安全风险。 例如,我们的 GridView 仅列出美国的这些供应商。 然而,如果 SupplierID 直接从单选按钮传递,怎么样才能阻止一个调皮的用户在回发时篡改传回的 SupplierID 值呢? 通过将行索引用作value,然后从DataKeys集合中的回发中获取SupplierID,我们可以确保用户仅使用与某个 GridView 行关联的SupplierID值之一。

添加此事件处理程序代码后,花一分钟时间在浏览器中测试页面。 首先,请注意,一次只能选择网格中的一个单选按钮。 但是,选择单选按钮并单击其中一个按钮时,将发生回发,单选按钮将全部恢复为其初始状态(即回发时,所选单选按钮不再被选中)。 若要解决此问题,我们需要扩充 RowCreated 事件处理程序,以便检查从回发发送的所选单选按钮索引,并将该属性添加到 checked="checked" 行索引匹配项的发出标记中。

发生回发时,浏览器会发送回所选单选按钮的 namevalue。 可以通过编程方式检索该值 Request.Form["name"]Request.Form属性提供NameValueCollection来表示表单变量。 表单变量是网页中表单字段的名称和值,每当发生回发操作时,Web 浏览器会将其发送回服务器。 由于 GridView 中单选按钮的呈现的 name 属性为 SuppliersGroup,因此当网页回发时,浏览器将 SuppliersGroup=valueOfSelectedRadioButton 发送回 Web 服务器(以及其他窗体字段)。 然后,可以使用: Request.Form["SuppliersGroup"]Request.Form属性访问此信息。

因为我们需要在RowCreated事件处理程序中确定选定的单选按钮索引,而且我们也需要在 Button Web 控件的事件处理程序中进行相同操作Click,我们需要在代码隐藏类SuppliersSelectedIndex中添加一个属性,该属性在没有选中单选按钮时返回-1,而在选中其中一个时返回所选索引。

private int SuppliersSelectedIndex
{
    get
    {
        if (string.IsNullOrEmpty(Request.Form["SuppliersGroup"]))
            return -1;
        else
            return Convert.ToInt32(Request.Form["SuppliersGroup"]);
    }
}

添加此属性后,我们知道在SuppliersSelectedIndex等于e.Row.RowIndex时,可以在checked="checked"事件处理程序中添加RowCreated标记。 更新事件处理程序以包括以下逻辑:

protected void Suppliers_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // Grab a reference to the Literal control
        Literal output = (Literal)e.Row.FindControl("RadioButtonMarkup");
        // Output the markup except for the "checked" attribute
        output.Text = string.Format(
            @"<input type="radio" name="SuppliersGroup" " +
            @"id="RowSelector{0}" value="{0}"", e.Row.RowIndex);
        // See if we need to add the "checked" attribute
        if (SuppliersSelectedIndex == e.Row.RowIndex)
            output.Text += @" checked="checked"";
        // Add the closing tag
        output.Text += " />";
    }
}

通过此更改,所选单选按钮在回发后保持选中状态。 现在,既然我们可以指定选择哪个单选按钮,我们就可以更改行为,使得在首次访问页面时选择第一个 GridView 行的单选按钮(而非当前默认未选择任何单选按钮的行为)。 若要默认选择第一个单选按钮,只需将if (SuppliersSelectedIndex == e.Row.RowIndex)语句更改为以下内容: if (SuppliersSelectedIndex == e.Row.RowIndex || (!Page.IsPostBack && e.Row.RowIndex == 0))

此时,我们已向 GridView 添加了一列分组单选按钮,以便可以在跨回发操作中选择并记住单个 GridView 行。 后续步骤是显示所选供应商提供的产品。 在步骤 4 中,我们将了解如何将用户重定向到另一个页面,并传送所选 SupplierID 的相关数据。 在步骤 5 中,我们将在同一页上查看如何在 GridView 中显示所选供应商的产品。

注释

我们可以创建自定义 DataControlField 类来呈现相应的用户界面和功能,而不是使用 TemplateField(此冗长的步骤 3 的焦点)。 DataControlField是 BoundField、CheckBoxField、TemplateField 和其他内置 GridView 和 DetailsView 字段派生的基类。 通过创建自定义DataControlField类,只需使用声明式语法即可添加单选按钮列,并且这也使得在其他网页和 Web 应用程序上更轻松地复制该功能。

但是,如果你曾经在 ASP.NET 中创建自定义、已编译的控件,则你知道这样做需要大量的工作,并附带大量必须仔细处理的微妙和边缘事例。 因此,我们暂时不再考虑将单选按钮列实现为自定义 DataControlField 类,继续使用 TemplateField 选项。 也许我们有机会在将来的教程中了解如何创建、使用和部署自定义 DataControlField 类!

步骤 4:在单独的页面中显示所选供应商的产品

用户选择了 GridView 行后,我们需要显示所选供应商的产品。 在某些情况下,我们可能希望在单独的页面中显示这些产品,而另一些情况下,我们可能希望在同一页中执行此作。 让我们首先检查如何在单独的页面中显示产品;在步骤 5 中,我们将介绍如何添加 GridView 以显示 RadioButtonField.aspx 所选供应商的产品。

目前页面上有两个按钮 Web 控件:ListProductsSendToProducts。 单击SendToProducts按钮时,我们希望将用户发送到~/Filtering/ProductsForSupplierDetails.aspx。 此页面是在“母版/详细信息筛选跨两页”教程中创建的,并显示通过命名SupplierID的查询字符串字段传递的供应商SupplierID的产品。

若要提供此功能,请为 SendToProducts Button s Click 事件创建事件处理程序。 在步骤 3 中,我们添加了 SuppliersSelectedIndex 属性,该属性返回选中单选按钮所在行的索引。 用户可以从 GridView 的 DataKeys 集合中检索到相应的 SupplierID,然后使用 Response.Redirect("url") 将用户发送到 ~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=SupplierID

protected void SendToProducts_Click(object sender, EventArgs e)
{
    // Send the user to ~/Filtering/ProductsForSupplierDetails.aspx
    int supplierID = 
        Convert.ToInt32(Suppliers.DataKeys[SuppliersSelectedIndex].Value);
    Response.Redirect(
        "~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=" 
        + supplierID);
    }
}

只要从 GridView 中选择其中一个单选按钮,此代码就会非常有效。 如果最初 GridView 中没有任何单选按钮被选中,用户单击 SendToProducts 按钮后,SuppliersSelectedIndex 将会是 -1,这将导致引发异常,因为 -1 超出了 DataKeys 集合的索引范围。 但是,如果决定按照步骤 3 中所述更新 RowCreated 事件处理程序,以便最初选择 GridView 中的第一个单选按钮,则这不是一个问题。

若要容纳值SuppliersSelectedIndex-1,请将标签 Web 控件添加到 GridView 上方的页面。 将其ID属性设置为ChooseSupplierMsg,其CssClass属性设置为Warning,其EnableViewStateVisible属性设置为false,其Text属性设置为“请从网格中选择供应商”。 CSS 类 Warning 以红色、斜体、粗体、大字体显示文本,并在其中 Styles.css定义。 通过将EnableViewStateVisible属性设置为false,标签不会被渲染,除非在某些回发中控件的Visible属性被编程设置为true的情况。

在 GridView 上方添加标签 Web 控件

图 13:在 GridView 上方添加标签 Web 控件(单击以查看全尺寸图像

接下来,扩充 Click 事件处理程序以显示 ChooseSupplierMsg Label(如果 SuppliersSelectedIndex 小于零),并将用户重定向到 ~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=SupplierID 其他位置。

protected void SendToProducts_Click(object sender, EventArgs e)
{
    // make sure one of the radio buttons has been selected
    if (SuppliersSelectedIndex < 0)
        ChooseSupplierMsg.Visible = true;
    else
    {
        // Send the user to ~/Filtering/ProductsForSupplierDetails.aspx
        int supplierID = 
            Convert.ToInt32(Suppliers.DataKeys[SuppliersSelectedIndex].Value);
        Response.Redirect(
            "~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=" 
            + supplierID);
    }
}

访问浏览器中的页面,在从 GridView 中选择供应商之前单击 SendToProducts 该按钮。 如图 14 所示,这会显示 ChooseSupplierMsg 标签。 接下来,选择供应商并单击 SendToProducts 该按钮。 这会将你移动到一个页面,其中列出了所选供应商提供的产品。 图 15 显示了选择了 Bigfoot Breweries 供应商时的ProductsForSupplierDetails.aspx页面。

如果未选择供应商,则会显示 ChooseSupplierMsg 标签

图 14ChooseSupplierMsg 如果未选择供应商,则会显示标签(单击以查看全尺寸图像

所选供应商的产品展示于“ProductsForSupplierDetails.aspx”页面

图 15:所选供应商的产品显示在 ProductsForSupplierDetails.aspx单击以查看全尺寸图像

步骤 5:在同一页上显示所选供应商的产品

在步骤 4 中,我们了解了如何将用户发送到另一个网页以显示所选供应商的产品。 或者,所选供应商的产品可以在同一页上显示。 为了说明这一点,我们将添加另一个 GridView 以显示 RadioButtonField.aspx 所选供应商的产品。

由于我们只希望在选择供应商后显示此 GridView 的产品,因此请在 Suppliers GridView 下面添加一个面板 Web 控件,并将其 ID 属性设置为 ProductsBySupplierPanel,以及将 Visible 属性设置为 false。 在面板中,添加“所选供应商的产品”文本,后跟名为 GridView 的网格视图 ProductsBySupplier。 从 GridView 智能标记中,选择绑定到名为 ProductsBySupplierDataSource 的新 ObjectDataSource。

将 ProductsBySupplier GridView 绑定到 New ObjectDataSource

图 16:将 ProductsBySupplier GridView 绑定到新的 ObjectDataSource(单击可查看全尺寸图像

接下来,将 ObjectDataSource 配置为使用 ProductsBLL 类。 由于我们只想检索所选供应商提供的这些产品,因此指定 ObjectDataSource 应调用 GetProductsBySupplierID(supplierID) 方法来检索其数据。 从 UPDATE、INSERT 和 DELETE 选项卡中的下拉列表中选择(无)。

将 ObjectDataSource 配置为使用 GetProductsBySupplierID(supplierID) 方法

图 17:将 ObjectDataSource 配置为使用 GetProductsBySupplierID(supplierID) 方法(单击以查看全尺寸图像

在 UPDATE、INSERT 和 DELETE 选项卡中将 Drop-Down 列表设置为“无”

图 18:在 UPDATE、INSERT 和 DELETE 选项卡中将 Drop-Down 列表设置为“无”(单击以查看全尺寸图像

配置 SELECT、UPDATE、INSERT 和 DELETE 选项卡后,单击“下一步”。 GetProductsBySupplierID(supplierID)由于该方法需要输入参数,“创建数据源”向导会提示我们指定参数值的源。

此处提供了几个用于指定参数值源的选项。 可以使用默认 Parameter 对象,并通过编程方式将 SuppliersSelectedIndex 属性的值分配给 ObjectDataSource 事件处理程序中的 Parameter s DefaultValueSelecting 属性。 请参阅 以编程方式设置 ObjectDataSource 的参数值 教程,以复习如何以编程方式分配值给 ObjectDataSource 的参数。

或者,我们可以使用 ControlParameter 并引用 Suppliers GridViewSelectedValue属性(请参阅图 19)。 GridView 的属性 SelectedValue 返回 DataKeySelectedIndex 该属性对应的值。 为了使这个选项正常工作,我们需要在单击ListProducts按钮时,以编程方式将 GridView 的SelectedIndex属性设置为选定的行。 作为额外的好处,通过设置 SelectedIndex,所选记录将采用 DataWebControls 主题中定义的 SelectedRowStyle(黄色背景)。

使用 ControlParameter 将 GridView s SelectedValue 指定为参数源

图 19:使用 ControlParameter 将 GridView s SelectedValue 指定为参数源(单击以查看全尺寸图像

完成向导后,Visual Studio 将自动添加与产品数据相关的字段。 删除所有BoundFields,只保留 ProductNameCategoryNameUnitPrice,并将 HeaderText 字段更改为 Product、Category 和 Price。 配置UnitPrice的 BoundField,使其值格式化为货币。 进行这些更改后,Panel、GridView 和 ObjectDataSource 声明性标记应如下所示:

<asp:Panel runat="server" ID="ProductsBySupplierPanel" Visible="False">
    <h3>
        Products for the Selected Supplier</h3>
    <p>
        <asp:GridView ID="ProductsBySupplier" runat="server" 
            AutoGenerateColumns="False" DataKeyNames="ProductID"
            DataSourceID="ProductsBySupplierDataSource" EnableViewState="False">
            <Columns>
                <asp:BoundField DataField="ProductName" HeaderText="Product" 
                    SortExpression="ProductName" />
                <asp:BoundField DataField="CategoryName" HeaderText="Category" 
                    ReadOnly="True" SortExpression="CategoryName" />
                <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" 
                    HeaderText="Price" HtmlEncode="False" 
                    SortExpression="UnitPrice" />
            </Columns>
        </asp:GridView>
        <asp:ObjectDataSource ID="ProductsBySupplierDataSource" runat="server" 
            OldValuesParameterFormatString="original_{0}"
            SelectMethod="GetProductsBySupplierID" TypeName="ProductsBLL">
            <SelectParameters>
                <asp:ControlParameter ControlID="Suppliers" Name="supplierID" 
                    PropertyName="SelectedValue" Type="Int32" />
            </SelectParameters>
        </asp:ObjectDataSource>
    </p>
</asp:Panel>

若要完成本练习,我们需要将 GridView 的SelectedIndex属性设置为SelectedSuppliersIndex,并在单击ListProducts按钮时,将 Panel 的Visible属性设置为true。 为此,请为 ListProducts 按钮 Web 控件的 Click 事件创建事件处理程序并添加以下代码:

protected void ListProducts_Click(object sender, EventArgs e)
{
    // make sure one of the radio buttons has been selected
    if (SuppliersSelectedIndex < 0)
    {
        ChooseSupplierMsg.Visible = true;
        ProductsBySupplierPanel.Visible = false;
    }
    else
    {
        // Set the GridView's SelectedIndex
        Suppliers.SelectedIndex = SuppliersSelectedIndex;
        // Show the ProductsBySupplierPanel panel
        ProductsBySupplierPanel.Visible = true;
    }
}

如果未从 GridView 中选择供应商,则会显示 ChooseSupplierMsg 标签,并且 ProductsBySupplierPanel 面板将被隐藏。 否则,如果已选择供应商,则会显示 ProductsBySupplierPanel 并更新 GridView 的 SelectedIndex 属性。

图 20 显示选中 Bigfoot Breweries 供应商后的结果,单击“页面上显示产品”按钮。

Bigfoot 啤酒厂提供的产品在同一页上列出

图 20:大脚啤酒厂提供的产品在同一页上列出(单击可查看全尺寸图像

概要

主/详细信息使用可选主 GridView 和 Details DetailView 教程中所述,可以使用属性 ShowSelectButton 设置为 true的 CommandField 从 GridView 中选择记录。 CommandField 将其按钮显示为普通按钮、链接或图像。 另一种行选择用户界面是在每个 GridView 行中提供单选按钮或复选框。 在本教程中,我们探讨了如何添加单选按钮列。

遗憾的是,添加单选按钮列并不像人们想象的那样直观或简单。 没有可在单击按钮时添加的内置 RadioButtonField,并且使用 TemplateField 中的 RadioButton Web 控件会引入其自己的一组问题。 最后,为了提供此类接口,我们要么必须创建自定义 DataControlField 类,要么在 RowCreated 事件期间将相应的 HTML 注入到 TemplateField 中。

在我们研究完如何添加单选按钮列之后,现在来关注一下如何添加复选框列。 使用一列复选框,用户可以选择一个或多个 GridView 行,然后对所有所选行执行一些作(例如从基于 Web 的电子邮件客户端选择一组电子邮件,然后选择删除所有选定的电子邮件)。 在下一教程中,我们将了解如何添加此类列。

快乐编程!

关于作者

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

特别致谢

本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 David Suru。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请给我写信。mitchell@4GuysFromRolla.com