考察DataGrid控件 Part 7 (上)
本文英文原版:
http://aspnet.4guysfromrolla.com/articles/080702-1.aspx
導(dǎo)言:
在編輯界面里,默認(rèn)的是TextBoxes控件,不過(guò)你可以對(duì)界面進(jìn)行定制使其更具靈活性。比如,假如DataGrid控件里的某個(gè)列是True/False域,我們展現(xiàn)一對(duì)True/False單選項(xiàng)要比默認(rèn)的TextBoxes控件要好的多;再比如,假設(shè)一個(gè)列為某個(gè)表的外鍵(foreign key),我們提供一個(gè)DropDownList控件供用戶選擇恰當(dāng)?shù)倪x項(xiàng)或許是個(gè)更好的辦法。定制DataGrid的編輯界面需要額外的代碼,不過(guò)這些都比較簡(jiǎn)單,我個(gè)人認(rèn)為,難的是理解發(fā)生在頁(yè)面背后的運(yùn)行機(jī)制原理。
用EditItemTemplate定制編輯界面
為了定制DataGrid的編輯界面,我們要用到模版列(TemplateColumn)。記得在本系列文章的Part 5部分提到過(guò),可以向DataGrid添加模版列以自定義HTML輸出效果。如果是綁定列(BoundColumn)的話,默認(rèn)的是TextBox界面。在本文,我們要用到的是一個(gè)real-world示例。在本系列的前面部分,我們看到的示例關(guān)于ASPFAQs.com上的常見(jiàn)問(wèn)題解答。用到的是FAQ數(shù)據(jù)庫(kù),它有一系列的數(shù)據(jù)表,其中最主要的是tblFAQ表,每一行記錄對(duì)應(yīng)一條FAQ(即常見(jiàn)問(wèn)題解答)。其中一個(gè)列FAQCategoryID,它是數(shù)據(jù)表tblFAQCategory的一個(gè)外鍵,該數(shù)據(jù)表的每一行記錄對(duì)應(yīng)一個(gè)FAQ種類(lèi)(這些種類(lèi)包括Array, Appilcation Object, Email, ASP.NET, Forms等等) 。下面是這2個(gè)表的重要部分的定義:
基于前6章的基礎(chǔ)知識(shí),你應(yīng)該快速而容易的創(chuàng)建一個(gè)DataGrid來(lái)顯示每條FAQ,包括FAQ的種類(lèi)名稱(chēng)(注意不是integer類(lèi)型的)。下面的SQL語(yǔ)句可以挑選出每條FAQ的種類(lèi)名稱(chēng):
SELECT FAQID, F.FAQCategoryID, Name AS CategoryName, Description
FROM tblFAQ F
INNER JOIN tblFAQCategory FC ON FC.FAQCategoryID = F.FAQCategoryID
(演示頁(yè)面為 http://aspnet.4guysfromrolla.com/demos/dgExample13.aspx )
現(xiàn)在假設(shè)你想讓用戶編輯DataGrid,你可能會(huì)想,“哈哈,我已經(jīng)看完前6章,我知道怎么做了,首先添加一個(gè)EditCommandColumn!”不錯(cuò),這是正確的第一步,不過(guò)我們還要做更多的工作,因?yàn)槟J(rèn)時(shí),綁定列呈現(xiàn)的編輯界面不太理想。看一下這個(gè)演示頁(yè)面
( http://aspnet.4guysfromrolla.com/demos/dgExample14.aspx ),點(diǎn)擊某行的Edit按鈕,你會(huì)看到什么?要是你懶得點(diǎn)這個(gè)演示頁(yè)面的話,下面的這個(gè)圖顯示了最終效果。
我們注意到在編輯模式里,列Category已經(jīng)轉(zhuǎn)變成了默認(rèn)的TextBox。咋一看好像沒(méi)什么問(wèn)題,假設(shè)某人想把編號(hào)為2的Category由Strings變?yōu)锳rrays,他只需要鍵入Arrays。然而,從各個(gè)方面考慮這并不是最優(yōu)方案。首先,Category列是數(shù)據(jù)表tblFAQCategory的外鍵,雖然你可以遍歷tblFAQCategory來(lái)查找恰當(dāng)?shù)腸ategory名稱(chēng),然后用對(duì)應(yīng)的FAQCategoryID值來(lái)更新處于編輯狀態(tài)的tblFAQ row,但這樣顯的有點(diǎn)凌亂。再者,假如用戶輸入有誤呢,比如將“Strings”輸入成“String”?你怎么辦呢?彈出一個(gè)錯(cuò)誤消息嗎?假設(shè)用戶是有意的,那么要在tblFAQCategory里新添加一行記錄嗎?
明顯,理想情況是在Category列將TextBox替換成listbox,設(shè)置一些選項(xiàng)供用戶選擇。為此,我們需要在模版列里指定一個(gè)EditItemIndex控件,當(dāng)用戶選擇一行編輯時(shí),達(dá)到自定義HTML輸出效果的目的。前面提到過(guò)要自定義編輯界面,我們要用到模版列(TemplateColumn),而非綁定列(BoundColumn)。因此,首先我們需要將顯示category的綁定列轉(zhuǎn)換為模版列,如下面的代碼所顯(關(guān)于模版列的更多信息請(qǐng)參考Part 5):
<asp:datagrid id="dgPopularFAQs" runat="server" ... >
<Columns>
<asp:EditCommandColumn EditText="Edit" CancelText="Cancel"
UpdateText="OK" />
<asp:BoundColumn DataField="FAQID" ItemStyle-Width="10%"
ReadOnly="True"
ItemStyle-HorizontalAlign="Center"
HeaderText="FAQ ID" />
<asp:TemplateColumn HeaderText="Category">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "CategoryName") %>
</ItemTemplate>
</asp:TemplateColumn>
<asp:BoundColumn DataField="Description" HeaderText="FAQ Question"
/>
</Columns>
</asp:datagrid>
在此,我們僅僅從數(shù)據(jù)源將CategoryName列展示出來(lái),還沒(méi)完;當(dāng)前,如果用戶點(diǎn)擊“Edit”按鈕的話,并不會(huì)呈現(xiàn)出某個(gè)category列,換句話說(shuō),他們只會(huì)看到category的text文本,而不是TextBox,DropDownList等。這是因?yàn)樵诰庉嬆J嚼锞庉嬆承杏涗浀脑挘覀儾皇褂媒壎心敲淳捅仨毷褂肏TML。為此我們需要在TemplateColumn控件里使用EditItemTemplate(好比只顯示而不編輯數(shù)據(jù)時(shí)在ItemTemplate里用HTML呈現(xiàn)效果)
因此,在編輯模式里我們用DropDownList來(lái)顯示category。為此我們只需要添加DropDownList控件即可,像這樣:
<asp:TemplateColumn HeaderText="Category">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "CategoryName") %>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList runat="server" id="lstCategories"
DataValueField="FAQCategoryID"
DataTextField="Name"
DataSource="???" />
</EditItemTemplate>
</asp:TemplateColumn>
既然我們希望DropDownList顯示現(xiàn)有的FAQ種類(lèi),我們需要將DropDownList綁定到一個(gè)數(shù)據(jù)源。當(dāng)對(duì)DropDownList進(jìn)行綁定時(shí)我們需要指定DropDownList顯示的文字和對(duì)應(yīng)的傳遞值。既然DropDownList顯示的每一項(xiàng)對(duì)應(yīng)表tblFAQCategory里的一行記錄,比較妥當(dāng)?shù)淖龇ㄊ荄ropDownList將種類(lèi)的名稱(chēng)(即Name列)顯示出來(lái),而傳遞的值是AQCategoryID,因?yàn)樵诒韙blFAQCategory里一個(gè)AQCategoryID對(duì)應(yīng)一個(gè)Name,而AQCategoryID是主鍵值(如果你對(duì)此完全被搞糊涂了,我建議你參考這篇文章:Creating Databound DropDown Lists in ASP.NET http://www.4guysfromrolla.com/webtech/071101-1.shtml )。這就是我在上文提到DataValueField和DataTextFields的原因。
當(dāng)然,我們應(yīng)該明白這樣道理:當(dāng)你指定DropDownList綁定哪些列時(shí),實(shí)際上你已經(jīng)指定了數(shù)據(jù)源。從本質(zhì)上來(lái)說(shuō),我們需要用表tblFAQCategory的行記錄來(lái)構(gòu)建一個(gè)數(shù)據(jù)集(DataSet),在深入研究以前,我們先考察一下當(dāng)編輯DataGrid里的數(shù)據(jù)時(shí),在后臺(tái)到底發(fā)生了什么呢?
考察DataGrid控件 Part 7 (中)
本文英文原版:
http://aspnet.4guysfromrolla.com/articles/080702-1.2.aspx
DataGrid的幕后機(jī)制
調(diào)用DataGrid的DataBind()方法時(shí),它枚舉數(shù)據(jù)源的內(nèi)容。對(duì)數(shù)據(jù)源里的每一項(xiàng)(item),相應(yīng)的添加一個(gè)DataGridItem實(shí)例。每個(gè)DataGridItem都有一個(gè)ItemType屬性,要么被標(biāo)明為Item、或者AlternatingItem,又或者EditItem(當(dāng)然還有其它ItemType類(lèi)型,不過(guò)對(duì)本例而言,我們只關(guān)注這3種)。添加的第一個(gè)(以及所有為奇數(shù))的DataGridItem被標(biāo)明為一個(gè)ItemType of Item(也就是Item、AlternatingItem或EditItem);而添加的第二個(gè)(以及所有為偶數(shù))的DataGridItem被標(biāo)明為AlternateItem。
一旦指定DataGridItem的ItemType屬性后,便可以運(yùn)用相應(yīng)的用戶界面屬性。打個(gè)比方,如果指定ItemType屬性為Item,那么我們就可以使用Item對(duì)應(yīng)的ItemStyle屬性了。我們知道,DataGrid用Table類(lèi)(Table class)來(lái)控制顯示界面(自然地,DataGrid呈現(xiàn)為一個(gè)HTML表格);此外DataGridItem源自于TableRow class類(lèi),那就意味著DataGridItem對(duì)象將呈現(xiàn)為一個(gè)HTML表格行(TABLE row).
記得DataGrid class類(lèi)有一個(gè)EditItemIndex屬性,我們對(duì)DataGrid進(jìn)行編輯時(shí)就要用到這個(gè)屬性。比如,在DataGrid的OnEditCommand事件處理器里,我們要做的是將DataGrid的EditItemIndex設(shè)置為點(diǎn)擊了"Edit"按鈕的那一行的index值。 當(dāng)枚舉數(shù)據(jù)源時(shí),如果當(dāng)前行與EditItemIndex相匹配,那么就將該DataGridItem行標(biāo)記為EditItem,進(jìn)行編輯。編輯時(shí),如果某列為綁定列(BoundColumn),該列就呈現(xiàn)為默認(rèn)的TextBox,該TextBox的Text屬性的值就是用戶編輯輸入的值;如果某列為模版列(TempalteColumn),那么就呈現(xiàn)為它的EditItemTemplate(如果設(shè)置為available的話)。
綜上,當(dāng)調(diào)用DataGrid的DataBind()方法時(shí),將對(duì)數(shù)據(jù)源進(jìn)行枚舉,數(shù)據(jù)將一行一行的添加到DataGrid(DataGrid從本質(zhì)來(lái)說(shuō)就是一個(gè)HTML表格)。有一點(diǎn)很重要,對(duì)添加到DataGrid的每一行("row")來(lái)說(shuō),可以對(duì)其使用任何的數(shù)據(jù)綁定語(yǔ)法,就本章而言,我們就在模版列的ItemTemplate里使用了數(shù)據(jù)綁定語(yǔ)法(the <%# ... %>)。此外,行里的任何控件也有自己的DataBind()方法。
言歸正傳,對(duì)放置在EditItemTemplate標(biāo)簽里的DropDownList控件來(lái)說(shuō),當(dāng)調(diào)用DataGrid的DataBind()方法時(shí),“處于編輯狀態(tài)的”DropDownList的DataBind()也會(huì)被調(diào)用。基于前面的知識(shí),可以推斷出我們應(yīng)該將DropDownList的數(shù)據(jù)源設(shè)置為某個(gè)數(shù)據(jù)集(DataSet)并自動(dòng)調(diào)用其DataBind()方法。問(wèn)題是怎樣設(shè)置DropDownList的數(shù)據(jù)源(DataSource)屬性呢?答案是使用數(shù)據(jù)綁定語(yǔ)法!
指定DropDownList的DataSource屬性
我們使用我們熟悉的數(shù)據(jù)綁定語(yǔ)句來(lái)指定DropDownList的DataSource屬性,具體來(lái)說(shuō),我們用表tblFAQCategory來(lái)填充一個(gè)數(shù)據(jù)集,再用一個(gè)函數(shù)將該數(shù)據(jù)集返回,像下面這樣:
<asp:TemplateColumn HeaderText="Category">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "CategoryName") %>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList runat="server" id="lstCategories"
DataValueField="FAQCategoryID"
DataTextField="Name"
DataSource="<%# GetCategories() %>" />
</EditItemTemplate>
</asp:TemplateColumn>
函數(shù)GetCategories()僅僅返回一個(gè)由表tblFAQCategory的行進(jìn)行填充的數(shù)據(jù)集。這個(gè)函數(shù)很簡(jiǎn)單,像下面這樣:
<% @Import Namespace="System.Data" %>
<% @Import Namespace="System.Data.SqlClient" %>
<script language="vb" runat="server">
'Create a connection
Dim myConnection as New SqlConnection(connString)
Dim ddlDataSet as DataSet = New DataSet()
Function GetCategories() as DataSet
'Populate the ddlDataSet
Const strSQLDDL as String = _
"SELECT FAQCategoryID, Name FROM tblFAQCategory ORDER BY Name"
Dim myDataAdapter as SqlDataAdapter = New _
SqlDataAdapter(strSQLDDL, myConnection)
myDataAdapter.Fill(ddlDataSet, "Categories")
Return ddlDataSet
End Function
...
演示頁(yè)面為:
http://aspnet.4guysfromrolla.com/demos/dgExample15.aspx
注意,數(shù)據(jù)庫(kù)連接(connection)和數(shù)據(jù)集ddlDataSet都是在頁(yè)面上定義的,這意味著ASP.NET Web 頁(yè)面上的任何一個(gè)函數(shù)都可以訪問(wèn)這2個(gè)對(duì)象。對(duì)數(shù)據(jù)庫(kù)連接對(duì)象進(jìn)行完整(globally)定義的原因是我們要用到2個(gè)獲取數(shù)據(jù)庫(kù)信心的函數(shù)—GetCategories() 和 BindData()。相比起來(lái),與其為這2個(gè)函數(shù)分別創(chuàng)建creating, opening和closing的分段連接對(duì)象,我們還不如直接使用完整的數(shù)據(jù)庫(kù)連接對(duì)象。請(qǐng)注意,該連接對(duì)象在達(dá)到目的時(shí)(也就是當(dāng)頁(yè)面完全顯示出來(lái)后)自動(dòng)關(guān)閉。
GetCategories()函數(shù)簡(jiǎn)單易懂,只是用表tblFAQCategory的查詢結(jié)果來(lái)填充數(shù)據(jù)集ddlDataSet,再將填充好了的數(shù)據(jù)集返回。在演示頁(yè)面里當(dāng)點(diǎn)擊"Edit"按鈕時(shí),DropDownList控件確實(shí)被綁定了,再仔細(xì)看看,那發(fā)現(xiàn)了什么問(wèn)題?
對(duì)了,當(dāng)點(diǎn)擊某行的"Edit"按鈕時(shí),DropDownList顯示的是數(shù)據(jù)集里第一行的類(lèi)類(lèi)名,而非其本來(lái)的類(lèi)名。比如,第一個(gè)FAQ的類(lèi)名是"Strings",當(dāng)你點(diǎn)擊第一行的"Edit"按鈕后,顯示的卻是"Application Object"。還好,要修補(bǔ)這個(gè)小問(wèn)題我們只需要使用一些數(shù)據(jù)綁定語(yǔ)法和代碼,我們?cè)谧詈笠徊糠衷儆懻摗?
考察DataGrid控件 Part 7 (下)
本文英文原版
http://aspnet.4guysfromrolla.com/articles/080702-1.3.aspx
確保DropDownList恰當(dāng)?shù)腟elected Item
在編輯模式里,為確保DropDownList有恰當(dāng)?shù)腟elected Item,我們需要編程設(shè)置DropDownList的SelectedIndex屬性。為此,我們需要使用數(shù)據(jù)綁定語(yǔ)法,具體的說(shuō),我們要調(diào)用一個(gè)函數(shù),將所選擇的FAQ的FAQCategoryID值傳給它,代碼如下:
<asp:TemplateColumn HeaderText="Category">
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "CategoryName") %>
</ItemTemplate>
<EditItemTemplate>
<asp:DropDownList runat="server" id="lstCategories"
DataValueField="FAQCategoryID"
DataTextField="Name"
DataSource="<%# GetCategories() %>"
SelectedIndex='<%# GetSelIndex(Container.DataItem("FAQCategoryID")) %>'
/>
</EditItemTemplate>
</asp:TemplateColumn>
函數(shù)GetSelIndex()接收的是編輯行的FAQCategoryID值(那就是為什么我們要在DataGrid里包含F(xiàn)AQCategoryID列,雖然并不在DataGrid里將該列顯示出來(lái))。記得DropDownLis控件的SelectedIndex屬性是一個(gè)integer類(lèi)型的值,且大小介于0和它包含的項(xiàng)的總數(shù)(number of items)之間。比方,我們想將選擇第五項(xiàng),我們只需要將SelectedIndex屬性設(shè)置為4(因?yàn)榈谝豁?xiàng)的index值為0)
所以,函數(shù)GetSelIndex()返回一個(gè)integer類(lèi)型的值,它對(duì)應(yīng)DropDownList的所選項(xiàng)。我們知道數(shù)據(jù)集ddlDataSet用來(lái)填充(populate)控件DropDownList,同時(shí)數(shù)據(jù)集ddlDataSet每行的index與DropDownList的每項(xiàng)的index一一對(duì)應(yīng)。所以,函數(shù)GetSelIndex()遍歷數(shù)據(jù)集,將每行與傳入的FAQCategoryID值相比較。一旦匹配的話,函數(shù)就返回該行的index,然后對(duì)DropDownList的SelectedIndex屬性賦值。聽(tīng)起來(lái)有點(diǎn)暈,下面的代碼也許能更好的便于你理解:
Function GetSelIndex(CatID as String) as Integer
Dim iLoop as Integer
'Loop through each row in the DataSet
Dim dt as DataTable = ddlDataSet.Tables("Categories")
For iLoop = 0 to dt.Rows.Count - 1
If Int32.Parse(CatID) = _
Int32.Parse(dt.Rows(iLoop)("FAQCategoryID")) then
Return iLoop
End If
Next iLoop
End Function
演示頁(yè)面為: http://aspnet.4guysfromrolla.com/demos/dgExample16.aspx
在OnUpdateCommand事件處理器里檢索DropDownList的值
當(dāng)用戶編輯某行記錄時(shí),將會(huì)顯示"Update" 和 "Cancel"按鈕供選擇(在演示頁(yè)面里,我將"Update"替換為"OK"了)。當(dāng)用戶點(diǎn)擊"Update"按鈕時(shí)將觸發(fā)OnUpdateCommand事件。就像在Part 6探討過(guò)的一樣,為了用輸入的數(shù)據(jù)對(duì)數(shù)據(jù)庫(kù)進(jìn)行更新,我們需要為OnUpdateCommand事件編寫(xiě)一個(gè)事件處理器。
為了獲取用戶從DropDownList選取的值,我們使用FindControl方法來(lái)得到DropDownList控件的實(shí)例;訪問(wèn)SelectedItem.Value屬性,也就是用戶在DropDownList控件里所選的條目的FAQCategoryID值。OnUpdateCommand事件處理器的代碼如下:
Sub dgPopFAQs_Update(sender As Object, e As DataGridCommandEventArgs)
'Determine what category was selected
Dim strCategoryID as String, strCategoryName as String
strCategoryID = CType(e.Item.FindControl("lstCategories"), _
DropDownList).SelectedItem.Value
strCategoryName = CType(e.Item.FindControl("lstCategories"), _
DropDownList).SelectedItem.Text
... Make a SQL call to update the database ...
'Return the DataGrid to its pre-editing state
dgPopularFAQs.EditItemIndex = -1
BindData()
End Sub
演示頁(yè)面為: http://aspnet.4guysfromrolla.com/demos/dgExample17.aspx
使用EditItemTemplate定制其它編輯界面
在本文我們考察了使用EditItemTemplate來(lái)包含一個(gè)DropDownList控件。當(dāng)然,你也可以用它來(lái)為DataGrid里的其它列進(jìn)行編輯界面定制。比如,你可能覺(jué)得DataGrid默認(rèn)生成的TextBox 太小,你可以用EditItemTemplate來(lái)增大TextBox 的尺寸。另一個(gè)比較有實(shí)際意義的運(yùn)用是,假如有一個(gè)Yes/No類(lèi)型的列,你可以添加一個(gè)包含"Yes" / "No"值的DropDownList控件或干脆設(shè)置一個(gè) 單選按鈕,這比使用TextBox要好的多。
注意演示頁(yè)面,在編輯界面里我們使用的是75-column的TextBox.
結(jié)語(yǔ):
本文對(duì)Part 6的內(nèi)容進(jìn)行了擴(kuò)展,允許對(duì)DataGrid的編輯界面進(jìn)行用戶定制。我們首先探討了如何來(lái)模版列里用EditItemTemplate對(duì)某個(gè)列進(jìn)行編輯界面定制。然后,考察了如何將表tblFAQCategory的內(nèi)容綁定到DropDownList,以及自動(dòng)的選擇恰當(dāng)?shù)臈l目(item)。最后,我們學(xué)習(xí)了如何讀取用戶從DropDownList選取的值;這一步很重要,它關(guān)系到用用戶期望的值對(duì)數(shù)據(jù)庫(kù)進(jìn)行更新。
謹(jǐn)記,用EditItemTemplate對(duì)編輯界面進(jìn)行用戶定制的潛力是無(wú)窮的,只是看你的能力而已!
祝編程快樂(lè)!
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
