導(dǎo)言:
GridView控件提供了大量的內(nèi)置功能。它包含了一系列的域(field)來(lái)顯示諸如text、images、hyperlinks和buttons。另外它支持模板(template)用于用戶自定義界面。我們可以構(gòu)建這樣一個(gè)GridView控件,用戶僅需要點(diǎn)擊控件里的一個(gè)按鈕,每一條記錄行都可以選擇、編輯、刪除。除了控件本身內(nèi)置的功能外,在某些情況下,我們添加一些額外的、控件沒(méi)有內(nèi)置的功能。在本章及接下來(lái)的2篇教程里我們將探討如何優(yōu)化GridView,以支持額外的功能。
本篇及接下來(lái)的教程將主要探討優(yōu)化行選擇程序(row-selection process),就像在教程《使用 GridView和DetailView實(shí)現(xiàn)的主/從報(bào)表》里考察的一樣,我們?cè)贕ridView控件里添加一個(gè)包含選擇按鈕的命令域(CommandField),點(diǎn)擊該按鈕后產(chǎn)生回傳(postback),所選行的index值傳給GridView控件的SelectedIndex屬性。在那篇教程里我們看到了如何使用該功能顯示所選行的詳細(xì)信息。
除了Select button,我們經(jīng)常在用戶界面包含radio button和checkbox用于選擇記錄。在某些情況下我們可以對(duì)GridView擴(kuò)充,在每條記錄里用radio button或checkbox替換掉Select button。比如,我們只希望選擇GridView記錄中的一條時(shí),用radio button比用Select button好;再比如,當(dāng)用戶要選擇多條記錄時(shí)——就像在郵箱里同時(shí)刪除幾份郵件一樣,用checkbox是最好的。本教程先考察為GridView添加radio buttons,再考察添加checkboxes。
第一步:創(chuàng)建優(yōu)化GridView的Web頁(yè)面
在開(kāi)始之前讓我們?cè)诰W(wǎng)站項(xiàng)目里創(chuàng)建一個(gè)本節(jié)及后面2節(jié)要用到的ASP.NET頁(yè)面。新建一個(gè)名為EnhancedGridView的文件夾,然后,添加如下所示的頁(yè)面,確保使用Site.master母版。
Default.aspx
RadioButtonField.aspx
CheckBoxField.aspx
InsertThroughFooter.aspx
圖1:添加相關(guān)頁(yè)面
像其它文件夾一樣,Default.aspx頁(yè)面將顯示本節(jié)的所有教程。記得用戶控件SectionLevelTutorialListing.ascx提供該功能,從解決方案管理器里將其拖到Default.aspx頁(yè)面上。
圖2:添加用戶控件SectionLevelTutorialListing.ascx
最后,將這4篇教程添加到Web.sitemap文件里,特別的,加在“Using the SqlDataSource Control” 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>
完成后,花幾分鐘在瀏覽器查看該系列教程,如圖所示:
圖3:Site Map里完整地列出了本系列教程
第2步:在GridView控件里顯示供應(yīng)商
讓我們創(chuàng)建一個(gè)GridView控件,用于顯示來(lái)自美國(guó)的供應(yīng)商列表,同時(shí)每行記錄包含一個(gè)radio button。當(dāng)點(diǎn)擊radio button后,用戶將查看到供應(yīng)商提供的產(chǎn)品。在開(kāi)始具體研究如何實(shí)現(xiàn)以前,我們先創(chuàng)建一個(gè)顯示供應(yīng)商的GridView。
在文件夾EnhancedGridView里打開(kāi)adioButtonField.aspx頁(yè)面,進(jìn)入設(shè)計(jì)模式,從工具箱拖一個(gè)GridView到頁(yè)面。設(shè)其ID為Suppliers,在智能標(biāo)簽里選“創(chuàng)建新數(shù)據(jù)源”,特別的,我們選用ObjectDataSource,命名為SuppliersDataSource,然后選用SuppliersBLL 。
圖4:創(chuàng)建一個(gè)名為SuppliersDataSource的ObjectDataSource
圖5:設(shè)置該ObjectDataSource使用SuppliersBLL類(lèi)
因?yàn)槲覀冎幌肓谐鰜?lái)自美國(guó)的供應(yīng)商,在SELECT選項(xiàng)卡的下拉列表里選擇 GetSuppliersByCountry(country)方法。
圖6:設(shè)置該ObjectDataSource使用SuppliersBLL類(lèi)(原文如此)
在UPDATE選項(xiàng)卡選擇“(None)”,點(diǎn)下一步
圖7:設(shè)置該ObjectDataSource使用SuppliersBLL類(lèi)(原文如此)
因?yàn)镚etSuppliersByCountry(country)方法需要接受一個(gè)參數(shù),向?qū)崾疚覀冊(cè)O(shè)置參數(shù)源,在這里我們指定一個(gè)“硬編碼”值(就本例而言,我們指定USA),在數(shù)據(jù)源下拉列表里選“None”,在指定值文本框輸入“USA”。點(diǎn)“完成”結(jié)束向?qū)гO(shè)置。
圖8:為參數(shù)country使用默認(rèn)值“USA”
只保留GridView里的CompanyName, City和Country三列(BoundFields),其余的全部刪除。同時(shí)將CompanyName列的HeaderText屬性改為“Supplier”。設(shè)置完以后, GridView和ObjectDataSource控件的聲明代碼看起來(lái)和下面的差不多:
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>
在本篇教程,當(dāng)點(diǎn)擊某個(gè)供應(yīng)商時(shí),將在本業(yè)或另一頁(yè)顯示該供應(yīng)商提供的產(chǎn)品。為達(dá)到該目的,我們?cè)陧?yè)面添加2個(gè)Button Web控件。ID分別為L(zhǎng)istProducts和SendToProducts,當(dāng)點(diǎn)擊ListProducts按鈕時(shí),發(fā)生回傳(postback),接著將在本頁(yè)面顯示該供應(yīng)商的產(chǎn)品,當(dāng)點(diǎn)擊SendToProducts按鈕時(shí),將會(huì)鏈接到另一個(gè)頁(yè)面,顯示該供應(yīng)商的產(chǎn)品。
圖9顯示了GridView控件和添加的兩個(gè)Button Web控件。
圖9:顯示供應(yīng)商的 Name, City和Country信息
第3步:添加Radio Buttons列
至此,GridView里包含company name, city和country三列,但還缺少radio buttons列。不幸的是GridView控件并不包含內(nèi)置的RadioButtonField,因此只有我們自己手動(dòng)添加。我們可以添加一個(gè)模板(TemplateField)并在其ItemTemplate模板里顯示一個(gè)radio button。這樣的話就為GridView控件的每一行記錄添加了一個(gè)radio button。
我們首先可能會(huì)想到直接在TemplateField的ItemTemplate模版里添加一個(gè)RadioButton Web控件。不錯(cuò),這樣將為每一行添加radio button,但是這些radio button不能聚合,因此不能形成互斥關(guān)系。造成的后果是,最終用戶可以在GridView控件里同時(shí)選定多個(gè)radio button按鈕。
雖然這樣做不能到達(dá)我們期望的要求,不過(guò)還是值得我們花時(shí)間來(lái)考察一下為什么這些radio button不能聚合。首先,為GridView添加一個(gè)TemplateField,放置在最左邊,然后智能標(biāo)簽里選“編輯模板”,進(jìn)入TemplateField的ItemTemplate 模板,從工具箱拖一個(gè)Radio Button控件到模板(見(jiàn)圖10),設(shè)置其ID為RowSelector , GroupName屬性為SuppliersGroup。
圖10:在ItemTemplate模板添加一個(gè)RadioButton控件
完成設(shè)置后,GridView的代碼看起來(lái)應(yīng)和下面的差不多:
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控件被認(rèn)為是一個(gè)組,在一個(gè)組里面只有一個(gè)控件可以被選擇(即具有互斥性)。GroupName屬性為radio button的名稱(chēng)特征(nameattribute)指定值,瀏覽器檢查radio button的名稱(chēng)特征,再對(duì)其分組。
在瀏覽器里查看頁(yè)面,選擇所有行,可以看出這些radio button并沒(méi)有聚合(也就是不具有互斥性),如圖11所示:
圖11:GridView的Radio Buttons沒(méi)有聚合。
不能聚合的原因在于:盡管將他們的GroupName屬性設(shè)置為相同的,但提交的名稱(chēng)屬性是不同的。在瀏覽器里點(diǎn)查看/源代碼,檢查這些radio button的代碼:
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" />
我們注意到,這里的name和id值和在屬性窗口指定的準(zhǔn)確值(exact values)相比,在開(kāi)頭多了一些其它ID值(比如id="ctl00_MainContent_Suppliers_ctl02_RowSelector" 和ID="RowSelector"相比),這些多出來(lái)的IDs值來(lái)自于這些 radio buttons的父級(jí)控件(parent controls)——GridViewRow、GridView、Content control以及Web Form。加上這些IDs的目的是使GridView控件里的每個(gè)rendered Web control(具體到本例就是這些radio button)具有唯一的id值和name值。
這樣做的原因在于:在客戶端,便于瀏覽器區(qū)分每個(gè)rendered control(比如本章的radio button);在網(wǎng)絡(luò)服務(wù)器端,便于服務(wù)器區(qū)分當(dāng)頁(yè)面回傳時(shí)發(fā)生了什么事件或改變。比如,無(wú)論何時(shí),當(dāng)一個(gè) RadioButton的選中狀態(tài)(checked state)發(fā)生改變時(shí),我們希望運(yùn)行某些服務(wù)器端代碼。為此,我們可以將RadioButton的AutoPostBack屬性設(shè)置為true,同時(shí)為CheckChanged事件創(chuàng)建一個(gè)事件處理器。如果每個(gè)radio buttons的name和id值相同的話,當(dāng)發(fā)生頁(yè)面回轉(zhuǎn)時(shí),我們不能確定到底點(diǎn)擊了哪個(gè)RadioButton。
這樣做的缺點(diǎn)在于,我們不能用RadioButton Web 控件在GridView里創(chuàng)建一個(gè)radio button列。因此,我們必須在GridView row里添加適當(dāng)?shù)拇a。
注意:和RadioButton Web控件一樣,當(dāng)把radio button HTML控件添加到模板時(shí),它也會(huì)包含唯一的名稱(chēng)特征。如果你不熟悉HTML控件,沒(méi)關(guān)系,因?yàn)楹苌偈褂盟绕湓贏SP.NET 2.0里。如果有興趣了解更多,見(jiàn) K. Scott Allen的博客里的文章Web Controls and HTML Controls.
用Literal控件注入Radio Button代碼
為了在GridView控件里對(duì)radio buttons進(jìn)行聚合,我們要在ItemTemplate模板里手動(dòng)注入radio button代碼。每個(gè)radio button具有相同的name特性,但id特性必須是唯一的(因?yàn)槲覀兛赡苄枰ㄟ^(guò)客戶端腳本訪問(wèn)某個(gè)radio button)。因?yàn)楫?dāng)用戶選擇了一個(gè)radio button,頁(yè)面回傳后,瀏覽器將返回該按鈕的一個(gè)值,所以每個(gè)radio button要具備一個(gè)唯一值特性(unique value attribute)。最后,當(dāng)選擇一個(gè)radio button后,我們應(yīng)確保為該按鈕添加checked屬性。另外,用戶做了選擇并發(fā)生回傳后,radio button將回到默認(rèn)狀態(tài)(可任意指定)。
可以有2種方法在模板里注入代碼。其中一種是在代碼里調(diào)用定義在后臺(tái)代碼類(lèi)(code-behind class)里的方法,并使用格式化的形式。這個(gè)方法我們?cè)诮坛獭对贕ridView控件中使用TemplateField》里論述過(guò)。在本例,代碼看起來(lái)像這個(gè)樣子:
input type="radio" id='%# GetUniqueRadioButtonID(...) %>'
name='SuppliersGroup' value='%# GetRadioButtonValue(...) %>' ... />
其中,GetUniqueRadioButton和GetRadioButtonValue是定義在后臺(tái)代碼類(lèi)里的方法,其作用是返回特定的id和value值。用這種可以對(duì)radio button的id屬性和 value屬性賦值,但不能對(duì)checked屬性賦值。因?yàn)橹挥挟?dāng)數(shù)據(jù)第一次綁定到該GridView控件時(shí),數(shù)據(jù)綁定語(yǔ)法才能成功執(zhí)行。所以只有當(dāng)啟用GridView的試圖狀態(tài),并且是第一次登錄頁(yè)面(或者明確的讓GridView重新綁定數(shù)據(jù)源)時(shí)這種格式化的方法才能奏效。所以,當(dāng)頁(yè)面發(fā)生回傳時(shí),對(duì)checked屬性賦值的這個(gè)功能是失效的。這個(gè)問(wèn)題有點(diǎn)超出了本教程的范圍,所以在這里我將它擱置一邊,然而我仍然鼓勵(lì)你用上面的這個(gè)方法。這個(gè)練習(xí)將使你更深入的理解GridView以及數(shù)據(jù)綁定的生命周期。
第2種,也是本教程要用的方法是在模板里添加一個(gè)Literal控件。在GridView的RowCreated或RowDataBound事件處理器里,我們可以通過(guò)編程來(lái)訪問(wèn)Literal控件,并設(shè)置其Text屬性。
在TemplateField的ItemTemplate模板里,移除RadioButton控件,換成Literal控件,設(shè)其ID為RadioButtonMarkup。
圖12:在ItemTemplate模板里添加一個(gè)Literal控件
然后,為GridView的RowCreated事件創(chuàng)建事件處理器。RowCreated事件是這樣的,不管數(shù)據(jù)是不是重新綁定到GridView,只要在GridView里新增一行記錄就將引發(fā)RowCreated事件。那意味著,當(dāng)發(fā)生回傳事件時(shí),哪怕數(shù)據(jù)來(lái)自視圖狀態(tài),也會(huì)引發(fā)RowCreated事件。我們使用RowCreated事件而不使用RowDataBound事件的原因在于,只有當(dāng)數(shù)據(jù)明確的綁定到數(shù)據(jù)Web控件時(shí)才會(huì)引發(fā)RowDataBound事件.
在RowCreated事件處理器里,我們處理的是某一行記錄。對(duì)每一行記錄,我們通過(guò)編程引用Literal控件RadioButtonMarkup,然后在其Text屬性里聲明代碼。比如下面的代碼,我們創(chuàng)建一個(gè)radio button ,設(shè)置其name屬性為SuppliersGroup,id屬性為RowSelectorX,其中X代表 GridView row的index值,將value屬性也設(shè)置為GridView row的index值。
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);
}
}
當(dāng)選擇GridView控件的某條記錄時(shí),我們關(guān)心的是該供應(yīng)商的SupplierID值。我們首先會(huì)想到讓radio button的value屬性返回該SupplierID值(而不是該條記錄的index值)。然而這樣盲目地獲取并傳遞一個(gè)SupplierID值是很危險(xiǎn)的。以我們的GridView控件為例,它列出了所有的美國(guó)供應(yīng)商,如果我們直接通過(guò)radio button來(lái)傳遞SupplierID,我們無(wú)法阻止一個(gè)帶有惡意的用戶對(duì)回傳過(guò)來(lái)的SupplierID造假。通過(guò)將value屬性設(shè)置為某條記錄的index值,當(dāng)發(fā)生頁(yè)面回傳時(shí),從DataKeys集合里獲取該供應(yīng)商的SupplierID值。這樣的話,我們就能確保用戶只能使用GridView里某個(gè)供應(yīng)商的對(duì)應(yīng)的SupplierID值。
添加完事件處理器代碼后,花幾分鐘在瀏覽器里測(cè)試該頁(yè)面,首先確保每次只能選擇一個(gè)radio button。然而,當(dāng)選擇一個(gè)radio button并點(diǎn)擊下面的按鈕,在頁(yè)面發(fā)生回傳后,所有的radio button都回到最初的狀態(tài)(意即,發(fā)生回傳后,選中的radio button又恢復(fù)未選狀態(tài))。怎樣解決這個(gè)問(wèn)題呢?我們?cè)赗owCreated事件處理器里添加代碼,先確定發(fā)生頁(yè)面回傳后,選中的那個(gè)radio button的index值,然后添加checked="checked"屬性。
當(dāng)發(fā)生頁(yè)面回傳后,瀏覽器返回選中的radio button的name和value值.我們可以通過(guò)編程來(lái)獲取值,比如:Request.Form["name"]。Request.Form屬性用一個(gè)NameValueCollection來(lái)表示form變量。在這里,form變量就是發(fā)生回轉(zhuǎn)時(shí),瀏覽器返回的那些names和values值。 因?yàn)镚ridView控件里的radio buttons的name屬性是SuppliersGroup,當(dāng)頁(yè)面發(fā)生回轉(zhuǎn)時(shí),瀏覽器向網(wǎng)絡(luò)服務(wù)器傳回“SuppliersGroup=valueOfSelectedRadioButton”(連同其它form fields一起傳回)。我們可以用Request.Form屬性訪問(wèn)這些信息:Request.Form["SuppliersGroup"]
我們不僅需要在RowCreated事件處理器中確定所選radio button的index值,在Click事件處理器里同樣需要。讓我們?cè)诤笈_(tái)代碼類(lèi)里創(chuàng)建SuppliersSelectedIndex。如果沒(méi)有radio button被選定則返回-1,如果有radio button被選定則返回它的index值。如下:
private int SuppliersSelectedIndex
{
get
{
if (string.IsNullOrEmpty(Request.Form["SuppliersGroup"]))
return -1;
else
return Convert.ToInt32(Request.Form["SuppliersGroup"]);
}
}
當(dāng)SuppliersSelectedIndex的值與e.Row.RowIndex的值相同時(shí),我們應(yīng)在RowCreated事件處理器里添加checked="checked"代碼。修改如下:
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 += " />";
}
}
經(jīng)過(guò)上述修改后,選中的radio button在頁(yè)面回傳后仍然處于選中狀態(tài)?,F(xiàn)在我們可以指定某個(gè)radio button處于選中狀態(tài)。當(dāng)?shù)谝淮蔚卿涰?yè)面時(shí)我們可以指定選中GridView里的第一條記錄的radio button按鈕(默認(rèn)狀態(tài)是沒(méi)有一個(gè)radio button被選中,就像現(xiàn)在一樣)。為此,我們只需簡(jiǎn)單地將if (SuppliersSelectedIndex == e.Row.RowIndex) 改成if (SuppliersSelectedIndex == e.Row.RowIndex || (!Page.IsPostBack e.Row.RowIndex == 0)).
到這一步,我們?yōu)镚ridView創(chuàng)建了一個(gè)聚合的radio buttons列。它只允許選中其中的任一條記錄,并在頁(yè)面發(fā)生回傳后仍處于選中狀態(tài)。接下來(lái),我們將顯示某個(gè)選中的供應(yīng)商提供的產(chǎn)品。在第4步,我們將看如何將用戶鏈接到另一個(gè)顯示產(chǎn)品的頁(yè)面,并傳遞該供應(yīng)商的SupplierID值;在第5步,我們將探討如何在相同頁(yè)面顯示供應(yīng)商的產(chǎn)品。
注意:與其用TemplateField模板(在第3步討論的那樣),還不如創(chuàng)建一個(gè)自定義的DataControlField class類(lèi)來(lái)構(gòu)建用戶界面并提供相關(guān)功能。DataControlField class是一個(gè)基本類(lèi),GridView和DetailsView控件中內(nèi)置的BoundField、CheckBoxField、TemplateField等都源于它。如果創(chuàng)建自定義DataControlField的話,我們可以在聲明代碼中添加radio buttons列,復(fù)制其它頁(yè)面的函數(shù)及應(yīng)用程序也要容易些。
如果你在ASP.NET里創(chuàng)建過(guò)自定義控件的話,你應(yīng)該知道那需要了解更多的知識(shí),并且需要小心處理很多問(wèn)題。因此,我們目前不用自定義DataControlField class類(lèi),還是堅(jiān)持用TemplateField模板?;蛟S我們將在以后的教程里探討如何使用自定義DataControlField class類(lèi)。
第4步:在另一個(gè)頁(yè)面顯示供應(yīng)商的產(chǎn)品
在GridView里選中一條記錄后,我們需要顯示該供應(yīng)商的產(chǎn)品。有時(shí)候下我們想在另一個(gè)頁(yè)面顯示產(chǎn)品,而有時(shí)候我們想在同一頁(yè)面顯示數(shù)據(jù)。首先,我們探討如何在另一個(gè)頁(yè)面顯示產(chǎn)品,在第5步,我們將在RadioButtonField.aspx頁(yè)面添加一個(gè)GridView顯示產(chǎn)品。
當(dāng)前,頁(yè)面上有2個(gè)Button Web控件——ListProducts和SendToProducts。當(dāng)點(diǎn)擊SendToProducts時(shí),我們希望將用戶鏈接到位于~/Filtering/ProductsForSupplierDetails.aspx的頁(yè)面。我們?cè)诮坛獭犊珥?yè)面的主/從報(bào)表》里已經(jīng)創(chuàng)建了該頁(yè)面,供應(yīng)商的SupplierID是由一個(gè)名為SupplierID的querystring field傳遞的。
為提供該功能,我們?yōu)镾endToProducts的Click事件創(chuàng)建事件處理器。在第3步,我們添加了SuppliersSelectedIndex屬性,用于返回所選行記錄的index值。在如下代碼中,相應(yīng)的SupplierID可以從GridView控件的DataKeys集合獲取,并轉(zhuǎn)到~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=SupplierID using Response.Redirect("url")頁(yè)面。
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);
}
}
當(dāng)在GridView控件里選中一個(gè)radio button時(shí),以上代碼運(yùn)行正常。但是,如果最開(kāi)始GridView里沒(méi)有任何radio buttons被選中,而用戶又點(diǎn)擊了SendToProducts按鈕,SuppliersSelectedIndex就會(huì)賦值為-1,這將會(huì)拋出一個(gè)異常,因?yàn)樵贒ataKeys集合里沒(méi)有index為-1的情況。不過(guò)沒(méi)有關(guān)系,就像在第3步中探討的一樣,修改RowCreated事件處理器,使GridView默認(rèn)選中第一個(gè)radio button。
為了應(yīng)對(duì)SuppliersSelectedIndex為-1的情況,我們?cè)贕ridView上面添加一個(gè)Label Web控件,設(shè)其ID為ChooseSupplierMsg,CssClass 屬性為Warning,EnableViewState和Visible屬性為false,Text屬性為“Please choose a supplier from the grid.”。定義在Styles.css文件里的CSS class Warning將文本顯示為紅色、斜體、大號(hào)加粗。通過(guò)將EnableViewState和Visible屬性設(shè)置為false,該Label控件將不可見(jiàn),除非在某些頁(yè)面回傳時(shí),我們有意地通過(guò)編程將其Visible屬性設(shè)置為true。
圖13:在GridView控件上添加一個(gè)Label Web控件
接著,在Click事件處理器里添加代碼,如果SuppliersSelectedIndex的值小于0時(shí)Label控件ChooseSupplierMsg將可見(jiàn),并將用戶直接鏈接到~/Filtering/ProductsForSupplierDetails.aspx?SupplierID=SupplierID頁(yè)面:
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);
}
}
在瀏覽器里訪問(wèn)該頁(yè)面,在不選供應(yīng)商的情況下點(diǎn)擊SendToProducts按鈕,如圖14所示,label控件ChooseSupplierMsg變?yōu)榭梢?jiàn)了。然后,選擇一個(gè)供應(yīng)商,再單擊SendToProducts按鈕,這會(huì)將你鏈接到一個(gè)顯示該供應(yīng)商產(chǎn)品的頁(yè)面。圖15顯示了當(dāng)選擇供應(yīng)商Bigfoot Breweries時(shí),ProductsForSupplierDetails.aspx
頁(yè)面的情況。
圖14:未選供應(yīng)商時(shí),Label控件ChooseSupplierMsg為可見(jiàn)
圖15:ProductsForSupplierDetails.aspx頁(yè)面顯示了供應(yīng)商的產(chǎn)品
第5步:在本頁(yè)顯示所選供應(yīng)商的產(chǎn)品
在第4步,我們探討了怎樣在另一頁(yè)面顯示供應(yīng)商的產(chǎn)品,另外我們也可以在本頁(yè)面顯示產(chǎn)品。鑒于此,我們?cè)赗adioButtonField.aspx頁(yè)面添加另一個(gè)GridView控件展示所選供應(yīng)商的產(chǎn)品。
一旦選擇一個(gè)供應(yīng)商后,我們只想在GridView里顯示該供應(yīng)商的產(chǎn)品。在名為Suppliers的GridView控件下,添加一個(gè)Panel Web控件,設(shè)其ID為ProductsBySupplierPanel,將Visible屬性設(shè)置為false,text屬性設(shè)置為“Products for the Selected Supplier”, 緊接著添加一個(gè)名為ProductsBySupplier的GridView控件。在其智能標(biāo)簽里,將其綁定到一個(gè)名為ProductsBySupplierDataSource的ObjectDataSource控件。
圖16:將GridView控件ProductsBySupplier綁定到一個(gè)新的ObjectDataSource
然后,將設(shè)置該ObjectDataSource使用ProductsBLL類(lèi)。由于我們只想獲取一個(gè)供應(yīng)商的產(chǎn)品數(shù)據(jù),指定該ObjectDataSource控件調(diào)用GetProductsBySupplierID(supplierID)方法,同時(shí)在UPDATE, INSERT和DELETE選項(xiàng)卡的下拉列表里選“(None)” 。
圖17:指定該ObjectDataSource控件調(diào)用GetProductsBySupplierID(supplierID)方法
圖18:在UPDATE, INSERT和DELETE選項(xiàng)卡的下拉列表里選“(None)”
完成對(duì)SELECT, UPDATE, INSERT和DELETE選項(xiàng)卡的設(shè)置后,點(diǎn)下一步。因?yàn)镚etProductsBySupplierID(supplierID)方法需要一個(gè)輸入?yún)?shù),因此向?qū)崾疚覀冎付▍?shù)值的來(lái)源。
我們有幾種選擇指定參數(shù)值來(lái)源。我們可以使用默認(rèn)值,也可以在ObjectDataSource控件的Selecting事件處理器里,通過(guò)編程的方式,用SuppliersSelectedIndex屬性的值對(duì)Parameter的DefaultValue屬性賦值??梢栽谇懊娴慕坛獭毒幊淘O(shè)置ObjectDataSource的參數(shù)值》里溫習(xí)相關(guān)內(nèi)容。
此外,我們可以使用一個(gè)ControlParameter,它涉及到名為Suppliers的GridView控件的SelectedValue屬性(見(jiàn)圖19)。GridView控件的SelectedValue屬性將返回與SelectedIndex屬性相匹配的DataKey值。為此,當(dāng)點(diǎn)擊ListProducts按鈕時(shí),我們將編程把GridView控件的SelectedIndex屬性設(shè)置為選中的那行記錄。另外設(shè)置了SelectedIndex后,選中的記錄將運(yùn)用定義在DataWebControls主題中的SelectedRowStyle(顯示為黃色背景)。
圖19:用一個(gè)ControlParameter當(dāng)參數(shù)源來(lái)指定GridView的SelectedValue值
完成向?qū)Ш?,Visual Studio將自動(dòng)添加產(chǎn)品的數(shù)據(jù)域(data fields)。將除了ProductName, CategoryName和UnitPrice外的其它列全部刪除,并將HeaderText屬性分別設(shè)為“Product”, “Category”和“Price”。再將UnitPrice列格式化為貨幣形式。完成上述修改后,Panel, GridView和ObjectDataSource的聲明代碼看起來(lái)應(yīng)該像下面的差不多:
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>
當(dāng)點(diǎn)擊ListProducts按鈕時(shí),我們需要將GridView的SelectedIndex屬性設(shè)置為SelectedSuppliersIndex,并將Panel控件ProductsBySupplierPanel的Visible屬性設(shè)置為true。為L(zhǎng)istProducts按鈕的Click事件創(chuàng)建事件處理器,添加如下代碼:
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;
}
}
如果沒(méi)有從GridView里選擇供應(yīng)商,Label控件ChooseSupplierMsg將顯示出來(lái),而Panel控件ProductsBySupplierPanel則不可見(jiàn)。反之,如果選擇了一個(gè)供應(yīng)商,ProductsBySupplierPanel將顯示出來(lái),并且GridView的SelectedIndex屬性將更新。
圖20為選擇供應(yīng)商Bigfoot Breweries并單擊“Show Products on Page”按鈕后的效果圖。
圖20:在本頁(yè)顯示供應(yīng)商Bigfoot Breweries的產(chǎn)品
總結(jié):
就像在教程《使用 GridView和DetailView實(shí)現(xiàn)的主/從報(bào)表》里探討的一樣,如果在GridView里使用一個(gè)CommandField,并設(shè)置其ShowSelectButton屬性為true,我們就可以在GridView里選擇記錄。但是CommandField僅僅將其按鈕顯示為常規(guī)的button, link或image。在一個(gè)只能選擇一條記錄的用戶界面里,要為每一個(gè)GridView row提供一個(gè)radio button或checkbox。本教程探究了如何添加一個(gè)radio button列。
然而,添加一個(gè)radio button列并沒(méi)有想像的那么容易。沒(méi)有內(nèi)置的RadioButtonField供我們添加;在模板里使用RadioButton Web控件也會(huì)引發(fā)其它的問(wèn)題。要?jiǎng)?chuàng)建這種用戶界面,我們要么創(chuàng)建自定義的DataControlField類(lèi),要么在RowCreated事件里將適當(dāng)?shù)腍TML注入模板。
在探討了如何添加radio buttons列后,我們將注意力轉(zhuǎn)向添加checkboxes列。使用checkboxes列的話,用戶可以選擇一條或多條GridView rows并執(zhí)行相同的操作。(比如在郵箱里,我們同時(shí)選擇多封郵件并將之刪除)。在接下來(lái)的教程里我們看如何來(lái)實(shí)現(xiàn)。
祝編程快樂(lè)!
作者簡(jiǎn)介
本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書(shū),是4GuysFromRolla.com的創(chuàng)始人,自1998年以來(lái)一直應(yīng)用 微軟Web技術(shù)。大家可以點(diǎn)擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數(shù)據(jù)教程》,希望對(duì)大家的學(xué)習(xí)ASP.NET有所幫助。
您可能感興趣的文章:- asp.net 上傳下載輸出二進(jìn)制流實(shí)現(xiàn)代碼
- asp.net 字符串、二進(jìn)制、編碼數(shù)組轉(zhuǎn)換函數(shù)
- asp.net(c#)實(shí)現(xiàn)從sqlserver存取二進(jìn)制圖片的代碼
- asp.net 將一個(gè)圖片以二進(jìn)制值的形式存入Xml文件中的實(shí)例代碼
- ASP.NET實(shí)現(xiàn)圖片以二進(jìn)制的形式存入數(shù)據(jù)庫(kù)
- asp.net實(shí)現(xiàn)圖片以二進(jìn)制流輸出的兩種方法
- 在ASP.NET 2.0中操作數(shù)據(jù)之五十:為GridView控件添加Checkbox
- 在ASP.NET 2.0中操作數(shù)據(jù)之五十一:從GridView的頁(yè)腳插入新記錄
- 在ASP.NET 2.0中操作數(shù)據(jù)之五十二:使用FileUpload上傳文件
- 在ASP.NET 2.0中操作數(shù)據(jù)之五十三:在Data Web控件顯示二進(jìn)制數(shù)據(jù)