共用方式為


新增一列包含單選按鈕的 GridView 欄 (C#)

斯科特·米切爾

下載 PDF

本教學課程將探討如何將單選按鈕的欄新增到 GridView 控制項,讓使用者以更直覺的方式選取 GridView 的單一數據列。

簡介

GridView 控件提供大量的內建功能。 它包含數個不同的欄位,用於顯示文字、影像、超連結和按鈕。 它支援進一步自定義的範本。 按兩下滑鼠即可建立 GridView,讓每個資料列都可以透過按鈕選取,或啟用編輯或刪除功能。 儘管提供的功能過多,但通常會有額外的非支援功能需要新增的情況。 在本教學課程和接下來的兩個中,我們將探討如何增強 GridView 的功能,以包含其他功能。

本教程及下一個教程將重點放在加強行選擇過程上。 在使用可選主 GridView 搭配詳細數據檢視的說明中,我們可以在 GridView 中新增一個包含 [選取] 按鈕的 CommandField。 當按一下時,會進行回傳,且 GridView 的 SelectedIndex 屬性會更新為按下選取按鈕的資料列索引。 在 主圖形/詳細數據使用可選取的主方格檢視與詳細數據檢視 教學課程中,我們已瞭解如何使用這項功能來顯示所選 GridView 數據列的詳細數據。

雖然 [選取] 按鈕在許多情況下有效,但在其他情況下可能效果較差。 除了使用按鈕,其他兩個使用者介面元素通常用於選取:單選按鈕和複選框。 我們可以增強 GridView,讓每一列包含單選按鈕或複選框,而不是 [選取] 按鈕。 在使用者只能從 GridView 記錄中選擇一項的情況下,單選按鈕可能是比選取按鈕更佳的選擇。 在使用者可能會選取多個記錄的情況下,例如在 Web 型電子郵件應用程式中,使用者可能會想要選取多個訊息,以刪除複選框提供無法從 [選取] 按鈕或單選按鈕使用者介面取得的功能。

本教學課程將探討如何在 GridView 中新增一欄,以包含單選按鈕。 本教學課程探討使用複選框。

步驟 1:建立強化 GridView 的網頁

在開始增強 GridView 以包含單選按鈕的欄位之前,讓我們先花點時間在我們的網站專案中建立 ASP.NET 頁面,這是我們在本次教學和接下來兩個教學中所需的。 首先,新增名為 EnhancedGridView的新資料夾。 接下來,將下列 ASP.NET 頁面新增至該資料夾,請務必讓每個頁面與 Site.master 主版頁面建立關聯:

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

新增 SqlDataSource-Related 教學課程的 ASP.NET 頁面

圖 1:新增 SqlDataSource-Related 教學課程的 ASP.NET 頁面

就像在其他資料夾中一樣, Default.aspx 資料夾中 EnhancedGridView 會列出其區段中的教學課程。 回想一下, SectionLevelTutorialListing.ascx 使用者控件會提供這項功能。 因此,透過將此用戶控制項從「解決方案資源管理器」拖曳到頁面的設計視圖來新增至 Default.aspx

將 SectionLevelTutorialListing.ascx 使用者控件新增至 Default.aspx

圖 2:將使用者控制項新增 SectionLevelTutorialListing.ascxDefault.aspx按兩下以檢視完整大小的影像

最後,則要將這四個頁面新增為 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。

開始將 GridView 從 [工具箱] 拖至設計介面,以開啟RadioButtonField.aspx資料夾中的EnhancedGridView頁面。 將 GridView 設定 IDSuppliers ,並從其智慧標記中選擇建立新的數據源。 具體而言,請建立名為 SuppliersDataSource 的 ObjectDataSource,從 SuppliersBLL 物件提取其數據。

建立名為 SuppliersDataSource 的新 ObjectDataSource

圖 4:建立名為 SuppliersDataSource 的新 ObjectDataSource (按兩下以檢視完整大小的影像

[設定數據源 - SuppliersDataSource] 視窗的螢幕快照,其中已選取商務物件 SuppliersBLL,並醒目提示 [下一步] 按鈕。

圖 5:將 ObjectDataSource 設定為使用 SuppliersBLL 類別 (按一下即可檢視完整大小的影像

因為我們只要列出位於美國的供應商,因此請從 SELECT 標籤的下拉式清單中選擇 GetSuppliersByCountry(country) 方法。

[設定數據源 - SuppliersDataSource] 視窗的螢幕快照,其中已開啟 [SELECT] 索引標籤。已選取 GetSupplierByCountry 方法選項,並醒目提示 [下一步] 按鈕。

圖 6:將 ObjectDataSource 設定為使用 SuppliersBLL 類別 (按兩下以檢視完整大小的影像

從 [更新] 索引標籤中,選取 [無] 選項,然後按 [下一步]。

[設定數據源 - SuppliersDataSource] 視窗的螢幕快照,其中已開啟 [更新] 索引卷標。已選取方法選項 [無],並醒目提示 [下一步] 按鈕。

圖 7:將 ObjectDataSource 設定為使用 SuppliersBLL 類別 (按兩下以檢視完整大小的影像

GetSuppliersByCountry(country)由於 方法接受參數,[設定數據源精靈] 會提示我們輸入該參數的來源。 若要指定硬式編碼值 (在此範例中為USA),請將 [參數來源] 下拉式清單保留為 [無],然後在文本框中輸入預設值。 按一下 [完成] 以完成程序。

使用USA作為 country 參數的預設值

圖 8:使用 USA 作為參數的 country 預設值 (按兩下以檢視完整大小的影像

完成精靈之後,GridView 會包含每個供應商數據欄位的 BoundField。 移除CompanyNameCityCountry以外的所有BoundFields,並將CompanyName BoundFields的HeaderText屬性重新命名為Supplier。 執行此動作之後,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>

在本教學課程中,讓我們允許使用者在與供應商清單相同的頁面上,或在不同的頁面上檢視選取的供應商產品。 若要容納這一點,請將兩個 Button Web 控件新增至頁面。 我已將這兩個按鈕的ID設定為ListProductsSendToProducts,當ListProducts被按下時,會發生回傳,所選供應商的產品將列在同一個頁面上,而當SendToProducts被按下時,使用者將被導至另一個列出產品的頁面。

圖 9 顯示 Suppliers 透過瀏覽器檢視的 GridView 和兩個 Button Web 控制件。

來自美國的供應商已列出其名稱、城市和國家/地區資訊

圖 9:來自美國的供應商已列出其名稱、城市和國家/地區資訊(按兩下以檢視完整大小的影像

步驟 3:新增單選按鈕欄位

此時,GridView 有三個 Suppliers BoundField 顯示美國每個供應商的公司名稱、城市和國家/地區。 然而,它仍然缺少一列單選按鈕。 不幸的是,GridView 不包含內建的 RadioButtonField,否則我們可以將它新增至網格中,然後就搞定了。 相較之下,我們可以新增TemplateField並將其設定 ItemTemplate 為轉譯單選按鈕,讓每個 GridView 資料列都有一個單選按鈕。

一開始,我們可能會假設想要的使用者介面可以藉由將 RadioButton Web 控件新增至 ItemTemplate TemplateField 的 來實作。 雖然這確實會將單一單選按鈕新增至 GridView 的每一行,但這些單選按鈕無法形成分組,因此它們不會互斥。 也就是說,終端使用者可以從 GridView 同時選取多個單選按鈕。

雖然使用 RadioButton Web 控制件的 TemplateField 不提供我們需要的功能,但讓我們實作此方法,因為值得檢查結果單選按鈕未分組的原因。 首先,將TemplateField新增至供貨商 GridView,使其成為最左邊的欄位。 接下來,從 GridView 的智慧標記中,按兩下 [編輯範本] 連結,然後將 RadioButton Web 控制件從 [工具箱] 拖曳至 TemplateField s ItemTemplate (請參閱圖 10)。 將 RadioButton 的 ID 屬性設定為 RowSelector ,並將 GroupName 屬性設定為 SuppliersGroup

將 RadioButton Web 控制項新增至 ItemTemplate

圖 10:將 RadioButton Web 控件新增至 ItemTemplate按兩下以檢視完整大小的影像

透過設計工具進行這些新增之後,您的 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 s GroupName 屬性 是用於分組一系列單選按鈕的。 具有相同 GroupName 值的所有 RadioButton 控制件都會被視為分組;一次只能從群組中選取一個單選按鈕。 屬性 GroupName 會指定轉譯單選按鈕 s name 屬性的值。 瀏覽器會檢查單選按鈕 name 屬性,以判斷單選按鈕群組。

將 RadioButton Web 控制項新增至 ItemTemplate,使用瀏覽器瀏覽此頁面,然後點擊網格中的列的單選按鈕。 請注意單選按鈕並未分組,因此能夠選取所有的列,如圖 11 所示。

GridView 單選按鈕未分組

圖 11:GridView 單選按鈕未分組(按兩下以檢視完整大小的影像

造成單選按鈕未分組的原因是,儘管它們具有相同的name屬性設定,但其呈現的GroupName屬性不同。 若要查看這些差異,請從瀏覽器執行檢視/來源,並檢查單選按鈕標記:

<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值。 新增至渲染IDid屬性前方的額外name值包括單選按鈕父母控件的ID、GridView的GridViewRow、Content控制項的ID以及Web表單的ID。 新增這些 ID ,可以讓 GridView 中的每個 Web 控件都擁有獨特的 idname 值。

每個渲染的控件都需要不同的 nameid,因為這是瀏覽器唯一識別用戶端上每個控件的方式,並且是識別網頁伺服器在回傳時發生動作或變更的方式。 例如,假設每當 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 一的屬性(如果我們想要透過用戶端腳本存取單選按鈕)。 當使用者選取單選按鈕並回傳頁面之後,瀏覽器會傳回所選單選按鈕 s value 屬性的值。 因此,每個單選按鈕都需要一個獨特的value屬性。 最後,在回傳時,我們必須確保將 屬性新增 checked 至選取的單選按鈕,否則在用戶進行選取並回傳之後,單選按鈕會返回其默認狀態(全部未選取)。

有兩種方法可用來將低階標記插入範本。 其中之一是將標記與後置程式碼類別中定義的格式化方法結合。 這項技術首先在 GridView 控件教學課程中的 Using TemplateFields 中討論。 在我們的案例中,它看起來可能會像這樣:

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

在這裡,GetUniqueRadioButtonGetRadioButtonValue 是定義於程式代碼後置類別中的方法,其作用是為每個單選按鈕傳回適當的 idvalue 屬性值。 這個方法很適合用來指派 idvalue 屬性,但在需要指定 checked 屬性值時會很短,因為數據系結語法只有在數據第一次系結至 GridView 時才會執行。 因此,如果 GridView 已啟用檢視狀態,則格式化方法只會在頁面第一次載入時引發(或 GridView 明確重新系結至數據源時),因此不會在回傳時呼叫設定 checked 屬性的函式。 這是一個相當微妙的問題,有點超出這篇文章的範圍,所以我會離開它。 我仍然鼓勵您嘗試使用上述方法,並將其處理到您會碰到困難的地方。 雖然這類練習不會讓您更接近工作版本,但它將有助於更深入瞭解 GridView 和數據系結生命週期。

在範本中插入自定義、低階標記的另一種方法,以及我們將在本教學課程中使用的方法,是將 常值控件 新增至範本。 然後,在 GridView s RowCreatedRowDataBound 事件處理程式中,可以程式設計方式存取 Literal 控件,並將其 Text 屬性設定為要發出的標記。

首先從 TemplateField 中移除 ItemTemplateRadioButton,然後以 Literal 控件取代它。 將 ID 的常值控制項設為 RadioButtonMarkup

將文字控制項新增至 ItemTemplate

圖 12:將常值控制項新增至 ItemTemplate按一下檢視完整影像

接下來,建立 GridView s RowCreated 事件的事件處理程式。 不論 RowCreated 數據是否重新系結至 GridView,事件都會針對每個新增的數據列引發一次。 這表示,即使在回傳時從檢視狀態重新載入數據,RowCreated 事件仍會觸發。因此,我們選擇使用它而不是 RowDataBound,因為 RowDataBound 只有在數據明確綁定到數據 Web 控制項時才會觸發。

在此事件處理程式中,我們只想在處理數據列時繼續進行。 針對每個數據列,我們想以程式方式參考 RadioButtonMarkup Literal 控件,並將其屬性 Text 設定為要發出的標記。 如下列程式代碼所示,所發出的標記會建立一個單選按鈕,其 name 屬性被設定為 SuppliersGroupid 屬性被設定為 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,然後從SupplierID集合取得 DataKeys 在回傳過程中,我們可以確保使用者只使用其中一個與某個 GridView 行相關聯的 SupplierID 值。

新增此事件處理程式程式代碼之後,請花一分鐘的時間在瀏覽器中測試頁面。 首先,請注意,一次只能選取方格中的一個單選按鈕。 不過,選取單選按鈕並按下其中一個按鈕時,就會發生回傳,單選按鈕會全部還原成其初始狀態(也就是在回傳時,不再選取選取的單選按鈕)。 若要修正此問題,我們需要增強 RowCreated 事件處理程式,以檢查從回傳傳送的選取單選按鈕索引,並在數據列索引相符時,將 checked="checked" 屬性新增至其發出的標記。

發生回傳時,瀏覽器會傳回所選單選按鈕的 namevalueRequest.Form["name"]的值可以透過程式自動擷取。 屬性Request.Form提供一個NameValueCollection來代表表單變數。 表單變數是網頁中表單域的名稱和值,每當回傳時,網頁瀏覽器就會傳回。 因為 GridView 中單選按鈕的呈現 name 屬性是 SuppliersGroup,因此當網頁回傳時,瀏覽器會連同其他表單欄位一起傳 SuppliersGroup=valueOfSelectedRadioButton 回網頁伺服器。 然後可以使用:從 Request.Form 屬性存取這項資訊。 Request.Form["SuppliersGroup"]

由於我們需要在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"]);
    }
}

新增這個屬性後,我們知道當checked="checked"等於RowCreated時,要在事件處理程式SuppliersSelectedIndex中加入e.Row.RowIndex標記。 更新事件處理程式以包含此邏輯:

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 中顯示選取的供應商產品。

備註

與其使用 TemplateField(這個冗長的步驟3的焦點),我們不如建立一個自定義的 DataControlField 類別,來呈現適當的使用者介面和功能。 類別 DataControlField BoundField、CheckBoxField、TemplateField 和其他內建 GridView 和 DetailsView 欄位衍生來源的基類。 建立自定義 DataControlField 類別意味著單選按鈕的列可以僅使用宣告式語法來新增,這也會讓在其他網頁和其他 Web 應用程式中複製該功能變得顯著容易。

不過,如果您曾經在 ASP.NET 中建立過自定義、編譯的控件,您知道這樣做需要相當大量的準備工作,並且還需要小心處理許多細節和極端情況。 因此,我們將暫時放棄將單選按鈕行實作為自訂 DataControlField 類別,並堅持使用 TemplateField 選項。 也許我們有機會在未來的教學課程中探索建立、使用和部署自定義 DataControlField 類別!

步驟 4:在個別頁面中顯示選取的供應商產品

用戶選取 GridView 數據列之後,我們需要顯示選取的供應商產品。 在某些情況下,我們可能會想要在不同的頁面中顯示這些產品,而在其他頁面中,我們可能會想要在同一個頁面中執行。 讓我們先檢查如何在個別頁面中顯示產品;在步驟 5 中,我們將探討如何新增 GridView 以顯示 RadioButtonField.aspx 選取的供應商產品。

目前頁面上有兩個按鈕 Web 控制件,分別位於 ListProductsSendToProductsSendToProducts按下 [按鈕] 時,我們想要將使用者傳送至 ~/Filtering/ProductsForSupplierDetails.aspx。 此頁面是在主版/詳細篩選跨兩頁教學課程中建立的,顯示供應商SupplierID的產品,該供應商的資訊是通过名為SupplierID的查詢字串字段傳遞的。

若要提供這項功能,請建立 SendToProducts Button s Click 事件的事件處理程式。 在步驟 3 中,我們已新增 SuppliersSelectedIndex 屬性,其會傳回選取單選按鈕之數據列的索引。 可以從 GridView 的 SupplierID 集合中擷取對應的 DataKeys ,然後使用 ~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=SupplierID 將使用者傳送至 Response.Redirect("url")

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,請在 GridView 上方的頁面中新增一個標籤 Web 控制項。 將其 ID 屬性設定為 ChooseSupplierMsg,其 CssClass 屬性設定為 Warning,其 EnableViewStateVisible 屬性設定為 false,並將其 Text 屬性設定為 請從方格中選擇供應商。 CSS 類別 Warning 會以紅色、斜體、粗體、大字型顯示文字,並定義於 中 Styles.css。 藉由將 EnableViewStateVisible 屬性設定為 false,標籤不會渲染,除非是在控件的 Visible 屬性被程式設計地設為 true 的回傳中。

在 GridView 上方新增標籤 Web 控件

圖 13:在 GridView 上方新增標籤 Web 控件 (按一下以檢視完整大小的影像

接下來,增強Click事件處理程式,以便在ChooseSupplierMsg小於零時顯示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);
    }
}

瀏覽瀏覽器中的頁面,然後按下 SendToProducts 按鈕,再從 GridView 選取供應商。 如圖 14 所示,這會顯示標籤 ChooseSupplierMsg 。 接下來,選取供應商,然後按下 SendToProducts 按鈕。 這會帶您前往一個頁面,其中列出所選供應商所提供的產品。 圖 15 顯示在選取 Bigfoot Breweries 供應商時的 ProductsForSupplierDetails.aspx 頁面。

如果未選取供應商,則會顯示 ChooseSupplierMsg 標籤

圖 14:如果未選取供應商,則會 ChooseSupplierMsg 顯示標籤(按兩下即可檢視完整大小的影像

選取的供應商產品會顯示在ProductsForSupplierDetails.aspx

圖 15:選取的供應商產品會顯示在 ProductsForSupplierDetails.aspx 中(按兩下以檢視完整大小的影像

步驟 5:在同一頁面上顯示選取的供應商產品

在步驟 4 中,我們已瞭解如何將用戶傳送至另一個網頁,以顯示選取的供應商產品。 或者,選取的供應商產品可以顯示在相同的頁面上。 為了說明這一點,我們將新增另一個 GridView 來 RadioButtonField.aspx 顯示選取的供應商產品。

由於我們只希望產品 GridView 在選取供應商之後顯示,請在 GridView 底下Suppliers新增 Panel Web 控制項,將其ID設定為ProductsBySupplierPanel,並將其Visible屬性設定為false。 在面板中,新增選取供應商的產品文字,後面接著名為 ProductsBySupplier的 GridView。 從 GridView 的智慧標記中,選擇將其系結至名為 ProductsBySupplierDataSource的新 ObjectDataSource。

將 ProductsBySupplier GridView 系結至 New ObjectDataSource

圖 16:將 ProductsBySupplier GridView 系結至 New ObjectDataSource (按兩下以檢視完整大小的影像

接下來,將 ObjectDataSource 設定為使用 類別 ProductsBLL 。 由於我們只想要擷取所選供貨商所提供的這些產品,請指定 ObjectDataSource 應該調用GetProductsBySupplierID(supplierID) 方法來擷取其資料。 從 [UPDATE]、[插入] 和 [刪除] 索引卷標中的下拉式清單中選取 [無]。

將 ObjectDataSource 設定為使用 GetProductsBySupplierID(supplierID) 方法

圖 17:將 ObjectDataSource 設定為使用 GetProductsBySupplierID(supplierID) 方法 (按兩下即可檢視完整大小的影像

將 [更新]、[插入] 和 [刪除] 索引標籤中的 [Drop-Down 列表] 設定為 [無]

圖 18:將 UPDATE、INSERT 和 DELETE 索引卷標中的 [Drop-Down 列表] 設定為 [無] (按兩下以檢視完整大小的影像

設定 SELECT、UPDATE、INSERT 和 DELETE 索引卷標之後,按 [下一步]。 GetProductsBySupplierID(supplierID)由於 方法需要輸入參數,[建立數據源精靈] 會提示我們指定參數值的來源。

我們在此有數個選項來指定參數值的來源。 我們可以使用預設的 Parameter 物件,並以程式設計的方式將 SuppliersSelectedIndex 屬性的值指定給 ObjectDataSource 的事件處理程式中的 Parameter 的 DefaultValue 屬性。 如需重新回顧以程式設計方式為 ObjectDataSource 的參數指派值,請參閱 以程式設計方式設定 ObjectDataSource 的參數值 教學課程。

或者,我們可以使用 ControlParameter 並參考 Suppliers GridView 的 SelectedValue 屬性 (請參閱圖 19)。 GridView 的 SelectedValue 屬性會 DataKey 傳回對應至 SelectedIndex 屬性的值。 為了讓這個選項能夠運作,我們需要以程序設計方式在按下按鈕時,將 GridView 的 SelectedIndex 屬性設定為選取的數據 ListProducts 列。 作為新增的好處,藉由設定 SelectedIndex,選取的記錄將採用 SelectedRowStyle 主題中 DataWebControls 定義的 (黃色背景)。

使用 ControlParameter 將 GridView s SelectedValue 指定為參數來源

圖 19:使用 ControlParameter 將 GridView s SelectedValue 指定為參數來源(按兩下以檢視完整大小的影像

完成精靈之後,Visual Studio 會自動新增產品數據欄位的欄位。 保留ProductNameCategoryNameUnitPrice BoundFields,並將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,並在按下 ProductsBySupplierPanel 按鈕時,將 Visible Panel 的 true 屬性設定為 ListProducts。 若要達成此目的,請為 ListProducts Button 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 供應商並點選 [顯示產品] 按鈕後的結果。

大腳啤酒廠提供的產品列在同一頁上

圖 20:Bigfoot 啤酒廠提供的產品列在同一頁面上(按兩下以檢視全尺寸影像

總結

使用可選取的主要 GridView 搭配 Details DetailView 教學課程中所述,您可以使用屬性設定為 ShowSelectButton的 CommandFieldtrue,從 GridView 選取記錄。 但是 CommandField 會將它的按鈕顯示為一般按鈕、連結或圖片。 替代的數據列選取使用者介面是在每一個 GridView 數據列中提供單選按鈕或複選框。 在本教學課程中,我們探討如何新增單選按鈕欄。

不幸的是,新增一列單選按鈕並不像人們預期的那麼簡單。 沒有內建的 RadioButtonField 可以透過按鈕單擊新增,而在 TemplateField 中使用 RadioButton Web 控制項會產生一些問題。 最後,為了提供這種介面,我們要麼必須建立自定義 DataControlField 類別,要麼就在 RowCreated 事件期間將適當的 HTML 插入 TemplateField。

探索如何新增單選按鈕欄之後,讓我們將注意力轉向新增複選框欄。 使用複選取框列,使用者可以選取一或多個 GridView 數據列,然後在所有選取的數據列上執行一些作業(例如從網頁式電子郵件客戶端選取一組電子郵件,然後選擇刪除所有選取的電子郵件)。 在下一個教學中,我們將展示如何新增此類欄。

快樂的程序設計!

關於作者

斯科特·米切爾,七本 ASP/ASP.NET 書籍和 4GuysFromRolla.com 創始人的作者,自1998年以來一直與Microsoft Web 技術合作。 斯科特擔任獨立顧問、教練和作家。 他的最新書是 Sams Teach Yourself ASP.NET 2.0 in 24 Hours。 可以透過 mitchell@4GuysFromRolla.com 聯絡他。

特別感謝

本教學系列已由許多熱心的評論者審閱。 本教學課程的主要審閱者是 David Suru。 有興趣檢閱我即將推出的 MSDN 文章嗎? 如果是,請在 mitchell@4GuysFromRolla.com給我留言。