GridView是ASP.NET界面開(kāi)發(fā)中的一個(gè)重要的控件,對(duì)GridView使用的熟練程度直接影響軟件開(kāi)發(fā)的進(jìn)度及功能的實(shí)現(xiàn)。(車(chē)延祿)
GridView的主要新特性:
1.與DataSource控件結(jié)合實(shí)現(xiàn)了顯示與數(shù)據(jù)操作的分離,大大減化了代碼的編寫(xiě)量;
2.實(shí)現(xiàn)"雙向綁定",無(wú)需手動(dòng)檢索數(shù)據(jù)。
2.在列的類(lèi)型上新增了CheckBoxField和ImageField兩個(gè)類(lèi)型列;
3.對(duì)排序和分頁(yè)可以實(shí)現(xiàn)異步操作;
4.對(duì)其視圖狀態(tài)進(jìn)行優(yōu)化,使其運(yùn)行效率更高;
GridView與DataSource控件
要談GridView就必需要談DataSource,這兩個(gè)對(duì)象可稱得上是“最佳拍檔”。DataSource控件負(fù)責(zé)與數(shù)據(jù)源的交互,而GridView負(fù)責(zé)數(shù)據(jù)的顯示。它們之間通過(guò)“雙向綁定”聯(lián)系起來(lái),即DataSource控件將檢索出來(lái)的數(shù)據(jù)綁定到GridView中顯示,而GridView中修改和刪除的數(shù)據(jù)直接綁定到DataSource數(shù)據(jù)源去。這兩個(gè)過(guò)程由這兩個(gè)控件相互配合實(shí)現(xiàn)的,無(wú)需我們編寫(xiě)代碼。如果我們只用GridView顯示數(shù)據(jù),而不用DataSource控件的話,那你只好編寫(xiě)代碼實(shí)現(xiàn)DataSource要作的工作了。做為用過(guò)VS2003的用戶對(duì)這個(gè)工作并不陌生,但這種代碼實(shí)現(xiàn)不但工作量大,而且使用復(fù)雜,容量產(chǎn)生大量可讀性差的代碼,當(dāng)然也是初學(xué)者的最大障礙。好在VS2005為我們提供了DataSource控件,使我們不再如此痛苦地Coding了(當(dāng)然,如果有的情況下DataSource控件還無(wú)法完全替代Coding)。
所以在使用GridView控件的時(shí)候我們最好配合DataSource控件使用,不要再去折磨自己了。 下面的案例不做特殊說(shuō)明均以GridView+DataSource控件實(shí)現(xiàn)。
一、GridView外觀設(shè)置:
1、總體外觀設(shè)置
ShowFooter:(bool)是否顯示頁(yè)腳
ShowHeader:(bool)是否顯示頁(yè)眉
GridLines:(enum)None-不顯示格線;Horizontal-顯示水平格線;Virtical-顯示豎直格線;Both-顯示水平和豎直格線
EmptyDataText:(string)如果數(shù)據(jù)源中內(nèi)容為NULL時(shí)在GridView中顯示的值
AlternatingRowStyle:交替項(xiàng)的樣式
EditRowStyle:編輯項(xiàng)的樣式
EmptyDataRowStyle:空數(shù)據(jù)項(xiàng)的樣式
FooterStyle:頁(yè)腳樣式
HeaderStyle:頁(yè)眉樣式
PagerStyle:分頁(yè)樣式
RowStyle:行樣式
SelectedRowStyle:選中項(xiàng)樣式
注:1、使用GridView的時(shí)候我們一般可以使用“自動(dòng)套用樣式”選中一個(gè)樣式,然后在此樣式的基礎(chǔ)上修改上面的屬性,從而制作出滿意外觀效果。
2、上面的樣式可以對(duì)GridView進(jìn)行總體的外觀設(shè)置,如果對(duì)某一列進(jìn)行設(shè)置的話那可要在GridView右上角“智能菜單中”點(diǎn)擊“編輯列”進(jìn)行設(shè)置。
如:
直接套用樣式后的外觀

修改RowStyle-BackColor、RowStyle-Font、RowStyle-HorizontalAlign 等屬性后的效果

2、對(duì)綁定列進(jìn)行外觀設(shè)置
對(duì)上面的圖我們可以看出外觀方面有幾個(gè)問(wèn)題:
a.頁(yè)眉是英文的
b.所有的數(shù)據(jù)都是居中對(duì)齊
c.time列的數(shù)據(jù)中顯示了時(shí)分秒的數(shù)據(jù)
d.price列中應(yīng)加上"RMB"的符號(hào)
e.最好對(duì)價(jià)格列的數(shù)據(jù)以紅色字體顯示
上面的這些問(wèn)題我們可以通地單獨(dú)對(duì)列進(jìn)行格式設(shè)置來(lái)實(shí)現(xiàn)
點(diǎn)擊GridView右上角“智能菜單中”點(diǎn)擊“編輯列”,打開(kāi)"字段"窗口:

這個(gè)圖總體可以分作三大部分:可用字段,選中的字段,BoundField屬性
(1)“可用字段”:顯示了可供我們使用的列的類(lèi)型
BoundField:綁定列,將數(shù)據(jù)庫(kù)中的數(shù)據(jù)以字符形式綁定顯示
CheckBoxField:復(fù)選框列,一般用來(lái)綁定數(shù)據(jù)庫(kù)中的Bit型數(shù),以復(fù)選框的形式顯示在GridView中
HyperLinkField:超鏈接列,可以用數(shù)據(jù)源中的數(shù)據(jù)作超鏈接文本也可以把所有超鏈接文本設(shè)為統(tǒng)一的文本
ImageField:圖片列,綁定數(shù)據(jù)源中的圖片路徑,并把圖片顯示出來(lái)
CommandField:命令列,常用的“選擇”,“刪除”,“編輯、更新、取消”
ButtonField:按鈕列,其它做用的按鈕
TemplateField:模板列,可以更靈活地自定義顯示格式
(2)“選定的字段”:從“可用字段”中添加進(jìn)來(lái)的,用來(lái)在GridView中顯示的列。其下方有個(gè)復(fù)選框“自動(dòng)生成字段”,如果選中了就會(huì)根據(jù)DataSource控件中檢索出來(lái)的數(shù)據(jù)自動(dòng)生成列,如果你要自己設(shè)置列的格式,請(qǐng)將此復(fù)選框清空。(車(chē)延祿)
(3)“BoundField屬性”:設(shè)置“每個(gè)可用字段”的屬性
我們可以根據(jù)自己的需要從“可用字段”中選擇列添加到“選中的字段中”,然后設(shè)置BoundField屬性。
在此我們先主要看BoundField列,其它類(lèi)型的列后面再講解。
BoundField中的重要屬性:
ControlStyle:當(dāng)前列中控件的樣式
HeaderStyle:當(dāng)前列中頁(yè)眉的樣式
FooterStyle:當(dāng)前列中頁(yè)腳的樣式
ItemStyle:當(dāng)前列中數(shù)據(jù)行的樣式
ReadOnly:當(dāng)前列是否是只讀列,編輯的時(shí)候不顯示文本框
SortExpression:排序表達(dá)式,這里只填數(shù)據(jù)源的列名
Visible:當(dāng)前列是否可見(jiàn)
HeaderText:頁(yè)眉文本
FooterText:頁(yè)腳文本
DataField:當(dāng)前列的數(shù)據(jù)行要顯示哪個(gè)字段的數(shù)據(jù),填寫(xiě)字段名
DataFormatString:對(duì)顯示的數(shù)據(jù)進(jìn)行格式化顯示
上面的屬性太多,大家可以一時(shí)記下來(lái),其實(shí)也沒(méi)必要去記,只要見(jiàn)文知意就可以了。下面我們就用上面的內(nèi)容來(lái)解決外觀方法的問(wèn)題
第一步:清空“自動(dòng)生成字段”復(fù)選框
第二步:從“可用字段”中選擇“BoundField”添加到“選定的字段”中
第三步:點(diǎn)擊“選定的字段”中的每個(gè)字段,在“BoundField屬性”中設(shè)置
HeaderText--頁(yè)眉文本。如:“汽車(chē)名稱”
DataField--要在此列顯示數(shù)據(jù)的列名
第四步:設(shè)置"name"列的ItemStyle中的HorizontalAlign屬性為left,這樣只把name列的數(shù)據(jù)左對(duì)齊
第五步:設(shè)置"price"列的ItemStyle中的HorizontalAlign屬性為right,這樣只把price列的數(shù)據(jù)右對(duì)齊
第六步:在time列的DataFormatString屬性中設(shè)置為:"{0:d}"
第七步:在price列的DataFormatString屬性中設(shè)置為:"<font color=red>RMB</font>{0}"

好了,大家可以看到上面的問(wèn)題我們都已解決了,效果還可以吧。
3、對(duì)數(shù)據(jù)行進(jìn)行外觀設(shè)計(jì)
對(duì)于列的設(shè)計(jì)我們可以通過(guò)可視化的界面進(jìn)行設(shè)計(jì),但是對(duì)于數(shù)據(jù)行的外觀設(shè)置就沒(méi)有那么簡(jiǎn)單了。由于我們不知道將來(lái)有多少行數(shù)據(jù)顯示,也不知道哪一條數(shù)據(jù)顯示在哪一行中,所以很難通過(guò)可視化的方式進(jìn)行設(shè)置。但這并不代表我們沒(méi)有辦法根據(jù)綁定的數(shù)據(jù)不同設(shè)置不同行的外觀。
要解決這個(gè)問(wèn)題我們先來(lái)研究一下GridView的綁定數(shù)據(jù)的過(guò)程:
當(dāng)我們?yōu)镚ridView設(shè)置了已配好數(shù)據(jù)源后,在運(yùn)行的時(shí)候,會(huì)把數(shù)據(jù)源中的數(shù)據(jù)綁定顯示出來(lái)。但由于GridView隱藏了自身在數(shù)據(jù)綁定過(guò)程中的構(gòu)建過(guò)程,所以使好多學(xué)JavaEE的同學(xué)感覺(jué)不如在<Table>標(biāo)記中嵌入for循環(huán)清晰,更使有的同學(xué)感到“GridView自動(dòng)生成的行讓人有點(diǎn)不太放心”。其實(shí),這是個(gè)思維導(dǎo)向的問(wèn)題,只要我們理解了在數(shù)據(jù)綁定時(shí)GridView界面的構(gòu)建過(guò)程,就不難發(fā)現(xiàn)GridView數(shù)據(jù)綁定的思路并不復(fù)雜。關(guān)鍵的是大家不要羈絆于JavaEE生成表格的思路中,要以一種開(kāi)放與求知的心態(tài)來(lái)研究GridView。
實(shí)際上在GridView在數(shù)據(jù)綁定的過(guò)程中是逐行實(shí)現(xiàn)的。是從GridView最上面的一行(頁(yè)眉或上分頁(yè)行)到最下面的一行(頁(yè)腳或下分頁(yè)行)逐行建造的。在建造每一行的過(guò)程中,又分為兩步:創(chuàng)建行和綁定行兩個(gè)步驟。下面分開(kāi)說(shuō):
創(chuàng)建行:就是根據(jù)數(shù)據(jù)源的結(jié)構(gòu)創(chuàng)建出n個(gè)單元格,并根據(jù)GridView的樣式和BoundFField屬性的樣式設(shè)置好每個(gè)單元格的樣式(背景色,前景色,字體等)。如果該行中有控件或靜態(tài)文本(設(shè)計(jì)時(shí)已寫(xiě)死的文本,如頁(yè)眉文本)那在創(chuàng)建的時(shí)候也會(huì)直接創(chuàng)建控件或靜態(tài)文本。就像開(kāi)茶館一樣,把椅子、桌子、茶杯、開(kāi)水都備好了,就等客人來(lái)了(客人還沒(méi)來(lái)呢,因?yàn)檫@只是創(chuàng)建行,還沒(méi)有綁定行呢)。
綁定行:“創(chuàng)建行”執(zhí)行完后,再進(jìn)行“綁定行”。綁定行實(shí)際上就是把數(shù)據(jù)源中當(dāng)前行的數(shù)據(jù)逐一填寫(xiě)到已創(chuàng)建好的行的相應(yīng)單元格里去。綁定行執(zhí)行完畢時(shí)才真正把一行數(shù)據(jù)建造完畢。這里就像把客人按排到相應(yīng)的已準(zhǔn)備好的坐位上喝茶。
在GridView綁定DataSource控件的時(shí)候?qū)嶋H上就是循環(huán)執(zhí)行“創(chuàng)建行”和“綁定行”的過(guò)程。如果我們能深入到每一行的“建造”過(guò)程中去,我們就可以根據(jù)綁定出來(lái)的數(shù)據(jù)控制當(dāng)前行的外觀樣式了。
非常幸運(yùn),GridView 為我們提供了兩個(gè)事件:RowCreated和RowDataBound事件

RowCreated事件:是每一行“創(chuàng)建行”完成時(shí)被觸發(fā)的事件。
RowDataBound事件:是每一行“綁定行”完成時(shí)被觸發(fā)的事件。
因此在綁定GridView顯示數(shù)據(jù)過(guò)程也就是循環(huán)觸發(fā)RowCreated事件和RowDataBound事件的過(guò)程。
下面的代碼驗(yàn)證了我上面的闡述:(車(chē)延祿)
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
Response.Write(e.Row.RowType.ToString()+" Created=>");
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
Response.Write(e.Row.RowType.ToString()+" DataBound=>");
}
執(zhí)行結(jié)果:

好了通過(guò)上面的講述,大家應(yīng)當(dāng)明白GridView的綁定過(guò)程了,也正有了上面的RowCreated和RowDataBound這兩個(gè)能被自動(dòng)觸發(fā)的事件,我們就可以像孫猴子鉆入鐵扇公主的肚子里那樣鉆入到GridView的創(chuàng)建過(guò)程中去,想怎么拆騰就怎么拆騰,完全由你了。
那下面我們就拆騰一下吧,對(duì)數(shù)據(jù)行實(shí)現(xiàn)下面的控制:
1.把“上市時(shí)間”列改為"yyyy年MM月dd日"的中文格式顯示
2.把價(jià)格高于50萬(wàn)元的高檔汽車(chē)用黃色前景和紅色字體顯示
3.計(jì)算當(dāng)前頁(yè)面中所有汽車(chē)的平均價(jià)格,并顯示在頁(yè)腳中
解決思路:
在GridView控件的RowDataBound事件中,取得每一行中相應(yīng)單元格中的數(shù)據(jù),把數(shù)據(jù)處理,重新寫(xiě)回單元格中。
代碼如下:
//成員變量,用來(lái)累計(jì)當(dāng)前頁(yè)面中所有汽車(chē)的價(jià)格總和
private double _PriceSum;
protected void Page_Load(object sender, EventArgs e)
{
//顯示頁(yè)腳,以便在其中顯示平均價(jià)格
GridView1.ShowFooter = true;
}
//修改日期的顯示格式
private void ChangeDate(GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (e.Row.RowState == DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate)
{
//取出“上市時(shí)間”單元格中的日期文本
string dt = e.Row.Cells[3].Text;
if (dt != null && dt.Trim() != "")
{
//將日期轉(zhuǎn)換為長(zhǎng)日期字符串格式
e.Row.Cells[3].Text = Convert.ToDateTime(dt).ToLongDateString();
}
}
}
}
//把價(jià)格大于50萬(wàn)的汽車(chē)用黃色背景紅色字體顯示
private void HighLightCar(GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (e.Row.RowState == DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate)
{
string strprice = e.Row.Cells[4].Text;//取出來(lái)的單元格的文本帶有RMB字符串,如:<font color=red>RMB</font>50.47
//截取出其中的價(jià)格
strprice = strprice.Substring(strprice.LastIndexOf(">")+1);
//將價(jià)格轉(zhuǎn)換為double型數(shù)據(jù)
double price = double.Parse(strprice);
if (price > 50)
{
e.Row.BackColor = System.Drawing.Color.Yellow;//使用預(yù)設(shè)顏色來(lái)設(shè)置行的背景色
e.Row.ForeColor = System.Drawing.Color.FromName("#ff0000");//使用十六進(jìn)制的顏色,設(shè)置前景色
}
}
}
}
//計(jì)算當(dāng)前頁(yè)中所有汽車(chē)的平均價(jià)格,并在頁(yè)腳顯示
private void GetAvgPrice(GridViewRowEventArgs e)
{
//累加當(dāng)前頁(yè)中的汽車(chē)的價(jià)格總和(_PriceSum)
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (e.Row.RowState == DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate)
{
string strprice = e.Row.Cells[4].Text;//帶有RMB字符串,如:RMB50.47
strprice = strprice.Substring(strprice.LastIndexOf(">") + 1);
double price = double.Parse(strprice);
_PriceSum += price;
}
}
//根據(jù)_PriceSum計(jì)算平均價(jià)格,并在頁(yè)腳顯示
if (e.Row.RowType == DataControlRowType.Footer)
{
e.Row.Cells[4].Text = "<font color=red>RMB</font>"+(_PriceSum / GridView1.PageSize).ToString();
e.Row.Cells[4].HorizontalAlign = HorizontalAlign.Right;
}
}
//RowDataBound事件處理程序,調(diào)用上面的三個(gè)方法進(jìn)行設(shè)置
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
ChangeDate(e);
HighLightCar(e);
GetAvgPrice(e);
}
執(zhí)行結(jié)果:

在上面的函數(shù)中,有幾個(gè)需要重點(diǎn)說(shuō)明的問(wèn)題:
1.GridViewRowEventArgs e形參:這是在建造行對(duì)象時(shí)產(chǎn)生的事件數(shù)據(jù),它里面包含正在建造的當(dāng)前行對(duì)象的信息,通過(guò)它我們可以取得正在建造的行對(duì)象和行對(duì)象中的單元格對(duì)象,并可以取得或設(shè)置這些對(duì)象相應(yīng)的屬性。
2.e.Row.RowType屬性:(DataControlRowType枚舉類(lèi)型)它返回正在建造的行對(duì)象的類(lèi)型(如:頁(yè)眉、頁(yè)腳、數(shù)據(jù)行、分頁(yè)行等)
3.e.Row.RowState屬性:(DataControlRowState枚舉類(lèi)型)它返回正在建造的行對(duì)象的狀態(tài)(如:普通狀態(tài)、交替狀態(tài)、編輯狀態(tài)、選中狀態(tài)等)
下面這段代碼,大家一定不能漏掉
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (e.Row.RowState == DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate)
{
}
}
它代表我們的操作只對(duì)數(shù)據(jù)行起作用,不對(duì)頁(yè)眉和頁(yè)腳起作用(因?yàn)轫?yè)眉頁(yè)腳的文本中并不包含我們需要的數(shù)據(jù))。并且僅當(dāng)數(shù)據(jù)行處理普通狀態(tài)和交替狀態(tài)時(shí)起作用,在編輯和選中狀態(tài)不是不起作用的(編輯狀態(tài)下,數(shù)據(jù)不是顯示在單元格的Text中,而是顯示在單元格的控件中的)。(車(chē)延祿)
二、超鏈接列的使用
超鏈接列也是開(kāi)發(fā)過(guò)程中使用比較多的列的類(lèi)型,它是在GridView的單元格中顯示超鏈接信息,在點(diǎn)擊超鏈接時(shí)跳轉(zhuǎn)到相應(yīng)的頁(yè)面上去。

如上圖所示,由于我們Car表中的列很多,如果在一個(gè)頁(yè)面中全顯示出來(lái)會(huì)使GridView中單元格的數(shù)據(jù)變形,很影響我們的外觀。我們可以只把幾項(xiàng)主要信息顯示出來(lái),然后在每一行的后面加一個(gè)超鏈接列,當(dāng)點(diǎn)擊超鏈接列時(shí)轉(zhuǎn)到一個(gè)詳細(xì)頁(yè)面,把每一型號(hào)汽車(chē)的具體信息詳細(xì)顯示出來(lái)。
效果如下圖
汽車(chē)列表頁(yè)面

汽車(chē)詳細(xì)信息頁(yè)面

下面我們看看如何實(shí)現(xiàn)這種效果
第一步:向頁(yè)面中加入ObjectDataSource,GridView并配置好它們。
第二步:點(diǎn)擊GridView的智能菜單進(jìn)入GridView字段設(shè)置頁(yè)面,如下圖所示
設(shè)置DataNavigateUrlField為主鍵列Code
設(shè)置DataNavigateUrlFormatterString為Default7.aspx?id={0}
設(shè)置HeaderText為"查看"
設(shè)置Text為"詳細(xì)"
第三步:點(diǎn)擊確定按鈕完成添加

這里的屬性較多,在這里我們一起來(lái)看一下
HeaderText:該超鏈接列的頁(yè)眉文本
FooterText:該超鏈接列的頁(yè)腳文本
Text:超鏈接列中的鏈接文字,這個(gè)屬性用來(lái)把該列所有單元格的超鏈接的文字設(shè)成相同的。
DataTextField:指定數(shù)據(jù)源的一個(gè)字段名,把該字段的內(nèi)容作為超鏈接列的超鏈接文本。如果同時(shí)設(shè)置了Text屬性和DataTextField屬性,則忽略Text屬性的設(shè)置。
DataTextFormatterString:為綁定顯示的DataTextFied數(shù)據(jù)設(shè)置顯示格式
DataNavigateUrlField:指定數(shù)據(jù)源的一個(gè)字段名,該字段的內(nèi)容作為超鏈接列導(dǎo)向的網(wǎng)址,或者把該字段的值作為超鏈接傳遞的參數(shù)。
DataNavigateUrlFormatterString:為 DataNavigateUrlField列設(shè)置超鏈接的導(dǎo)航格式。如Default7.aspx?id={0}則該列的超鏈接導(dǎo)航到 Default7.aspx頁(yè)面,并且把DataNavigateUrlField中指定的字段值賦給id,作為查詢字符串傳遞至 Default7.aspx頁(yè)面
如果超鏈接列需要向目標(biāo)頁(yè)面傳遞多個(gè)字段的值作為參數(shù)的話,那么請(qǐng)在DataNavigateUrlField中指定多個(gè)字段名并用逗號(hào)隔開(kāi),然后再在DataNavigateUrlFormatterString屬性中用"&"鏈接多個(gè)參數(shù)(如:default7.aspx?id={0}&name={1})
三、命令列的使用
命令列主要提供了GridView中常用的一些修改、更新、刪除的操作,在GridView與DataSource控件結(jié)合使用的情況下,我們并不需要為些編寫(xiě)太多的代碼就可以實(shí)現(xiàn)簡(jiǎn)單的維護(hù)操作,如果在復(fù)雜的情況下(如一對(duì)多鏈接查詢時(shí)),我們?nèi)孕枰帉?xiě)代碼來(lái)實(shí)現(xiàn)更新與刪除。(車(chē)延祿)

使用命令列有兩種方式:
1.在“可選列”中添加相應(yīng)的CommandField列的子項(xiàng)
2.在“可選列”中添加CommandField列,然后設(shè)置CommandField列的屬性ShowEditButton、ShowDeleteButton等
一旦添加了命令按鈕,我們就可以對(duì)GridView實(shí)現(xiàn)簡(jiǎn)單的修改和更新的操作了。但是如果要想實(shí)現(xiàn)刪除操作的話還不行,還需要為GridView設(shè)置一個(gè)DataKeyNames屬性。如果不設(shè)置此屬性的話編譯是沒(méi)問(wèn)題的,但運(yùn)行時(shí)會(huì)產(chǎn)生“未提供參數(shù)”的異常,這一點(diǎn)切記。

DataKeyNames屬性用來(lái)設(shè)置GridView對(duì)應(yīng)的數(shù)據(jù)源的主鍵列,只有設(shè)置這個(gè)屬性,在刪除的時(shí)候才會(huì)把要?jiǎng)h除的主鍵值傳遞給DataSource控件執(zhí)行刪除功能。
現(xiàn)在編輯界面如圖:

我們一起來(lái)看一下運(yùn)行結(jié)果所存在的問(wèn)題:
1.主鍵列Code不應(yīng)當(dāng)被編輯
2.GridView自動(dòng)產(chǎn)生的文本框太寬,把我們的GridView都給擠變形了。
3.民族列在瀏覽時(shí)一直都顯示民族代號(hào),應(yīng)當(dāng)顯示民族名稱
4.民族列在修改的時(shí)候最好使用下拉列表讓用戶選擇相應(yīng)的民族。
5.文本輸入框沒(méi)有任何驗(yàn)證,對(duì)輸錯(cuò)的信息在提交的時(shí)候會(huì)產(chǎn)生異常
6.刪除時(shí)沒(méi)有確認(rèn)提示
7.刪除時(shí)產(chǎn)生外鍵引用的異常
這里的問(wèn)題實(shí)在是太多了,有的問(wèn)題很簡(jiǎn)單就可以解決,但有的問(wèn)題我們借助后面的模板列會(huì)更簡(jiǎn)單一些。
1、防止某列被編輯,只需要在“編輯列”對(duì)應(yīng)框中將該列的ReadOnly屬性設(shè)為true就可以了。
2、代碼控制編輯狀態(tài)下文本框的寬度(這種方式有點(diǎn)復(fù)雜,可以用模板列實(shí)現(xiàn))
我們上面已經(jīng)分析過(guò)GridView每一行的建造過(guò)程了,它包含“創(chuàng)建行”和“綁定行”兩步操作,“創(chuàng)建行”的過(guò)程中會(huì)把一些靜態(tài)的東西創(chuàng)建出來(lái),當(dāng)然也包含文本框的創(chuàng)建。因此我們只需要在RowCreated事件中把編輯列中的文本框的長(zhǎng)度改變一下就可以了。
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
//這里可能是VS2005的一個(gè)Bug,所以我只好把整型強(qiáng)制轉(zhuǎn)為DataControlRowState枚舉類(lèi)型再與e.Row.RowState屬性進(jìn)行比較
//4-Normal行的編輯狀態(tài),5-Alternating行處于編輯狀態(tài)
if (e.Row.RowState == (DataControlRowState)4||e.Row.RowState==(DataControlRowState)5)
{
//Width屬性是Unit類(lèi)型,不能直接賦整型值
//Unit.Pixel():按像素設(shè)置絕對(duì)寬度;Unit.Percent():按百分比設(shè)置寬度
((TextBox)e.Row.Cells[1].Controls[0]).Width = Unit.Pixel(60);
((TextBox)e.Row.Cells[3].Controls[0]).Width = Unit.Pixel(60);
((TextBox)e.Row.Cells[4].Controls[0]).Width = Unit.Pixel(100);
}
}
}

3.瀏覽狀態(tài)時(shí)顯示民族名稱,只需要將實(shí)體中添加一個(gè)只讀屬性NationName,然后用它替換Nation字段,綁定到GridView就可以了。
4.對(duì)主表中的數(shù)據(jù)刪除時(shí)級(jí)聯(lián)刪除相關(guān)子表數(shù)據(jù)
這種問(wèn)題用很多種實(shí)現(xiàn)方式:
a.使用數(shù)據(jù)庫(kù)中外鍵的級(jí)聯(lián)刪除功能
b.使用存儲(chǔ)過(guò)程或觸發(fā)器。
c.在程序代碼中刪除
前兩種方式實(shí)現(xiàn)方式主要是數(shù)據(jù)庫(kù)方面的知識(shí),這里不多說(shuō)了,下面我們主要看看如何使用程序代碼刪除數(shù)據(jù)。
當(dāng)我們點(diǎn)擊刪除按鈕的時(shí)候,GridView會(huì)自動(dòng)把要?jiǎng)h除的行對(duì)象傳遞給DataSource控件,然后DataSource調(diào)用相關(guān)的業(yè)務(wù)類(lèi)的方法執(zhí)行刪除,再然后就發(fā)生“外鍵引用異常”......
整個(gè)這個(gè)過(guò)程是系統(tǒng)自動(dòng)完成的,那我們要想自己寫(xiě)代碼進(jìn)行級(jí)聯(lián)刪除,并且禁止系統(tǒng)自動(dòng)調(diào)用默認(rèn)的刪除功能,那如何實(shí)現(xiàn)呢?
下面顯示了GridView中的一部分事件

大家會(huì)注意到其中的事件大部分都有兩類(lèi):***ed和***ing兩類(lèi)。如:Deleted和Deleting事件
***ed事件:系統(tǒng)執(zhí)先完操作后觸發(fā),如Deleted事件是系統(tǒng)調(diào)用默認(rèn)刪除功能,刪除成功后自動(dòng)觸發(fā)
***ing事件:系統(tǒng)執(zhí)行操作之前觸發(fā),如Deleting事件是系統(tǒng)調(diào)用默認(rèn)刪除功能之前觸發(fā)。
現(xiàn)在大家應(yīng)當(dāng)清楚在點(diǎn)擊刪除按鈕后如何添加自己的刪除處理程序了,答案當(dāng)然是應(yīng)當(dāng)在Deleting中編寫(xiě)代碼進(jìn)行級(jí)聯(lián)刪除。
主要代碼如下:
protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
//取得要?jiǎng)h除行的主鍵的值
string str = GridView1.DataKeys[e.RowIndex].Value.ToString();
//使用事務(wù)
using (TransactionScope ts = new TransactionScope())
{
//刪除該主鍵值在刪除簡(jiǎn)歷表中對(duì)應(yīng)的數(shù)據(jù)
new WorkDA().deleteInfo(str);
//刪除該主鍵值在刪除家庭關(guān)系表中對(duì)應(yīng)的數(shù)據(jù)
new FamilyDA().deleteInfo(str);
//刪除該主鍵值在基本信息表中對(duì)應(yīng)的數(shù)據(jù)
new InfoDA().delete(str);
//提交事務(wù)
ts.Complete();
}
//取銷(xiāo)事件進(jìn)一步響影,即該刪除操作不再往上提交至DataSource控件。
e.Cancel = true;
}
問(wèn)題還沒(méi)全部解決,剩下的問(wèn)題我們留給模板列來(lái)解決。(雖然不用模板列也可以解決上面所有的問(wèn)題,但是那可需要大量的Coding,所以建議大家使用模板列)
四、模板列
關(guān)于模板列我們可以用四個(gè)字來(lái)描述--“無(wú)所不能”。很多棘手的問(wèn)題,在遇到模板列的時(shí)候會(huì)迎刃而解,因?yàn)槟0辶械亩ㄖ乒δ芎軓?qiáng)大,允許我們建立不同狀態(tài)下的模板,所以我們用代碼控制起它來(lái)也就更加靈活自如了。(車(chē)延祿)
創(chuàng)建模板列一般有兩種方式:
在“編輯列”對(duì)話框中向“選中列”中添加TemplateField字段
在“編輯列”對(duì)話框中點(diǎn)擊“選中列”中相應(yīng)的列,再點(diǎn)擊“確定”按鈕上方的“將此字段轉(zhuǎn)換為T(mén)emplateField”文字,這就把現(xiàn)有的列轉(zhuǎn)換為模板列了。這種轉(zhuǎn)換方式大家要小心使用,因?yàn)橐坏┺D(zhuǎn)換為模板列,就沒(méi)辦法再轉(zhuǎn)換回去了。
當(dāng)一個(gè)綁定列被轉(zhuǎn)換成模板列后會(huì)發(fā)生如下變化:
變化前的代碼:

變化后的代碼:

變化后的設(shè)計(jì)界面

通過(guò)上面的圖我們可以看到,當(dāng)一個(gè)綁定列轉(zhuǎn)換為模板列后,該列的數(shù)據(jù)顯示時(shí),會(huì)把數(shù)據(jù)源的數(shù)據(jù)綁定到模板列的Label控件中去。而不是像綁定列那樣直接把數(shù)據(jù)綁定到單元格的Text屬性上。
所以取模板列的單元格中的值與取綁定列的單元格中的值是有區(qū)別的:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (e.Row.RowState == DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate)
{
//在RowDataBound事件中操作綁定列的單元格中的顯示值
e.Row.Cells[4].Text += "...";
//在RowDataBound事件中操作模板列的單元格中的顯示值
((Label)e.Row.Cells[3].Controls[1]).Text ="...";
}
}
}
在綁定列中,數(shù)據(jù)直接綁定在單元格的Text屬性上,所以可以直接用e.Row.Cells[4].Text進(jìn)行操作;但在模板列中,數(shù)據(jù)并沒(méi)有綁定在單元格的Text上,而是綁定在Label控件的文本上,所以應(yīng)當(dāng)通過(guò) e.Row.Cells[3].Controls[1]來(lái)取得Label控件(要記得強(qiáng)制轉(zhuǎn)換)。細(xì)心的朋友會(huì)發(fā)現(xiàn)這里我是取“當(dāng)前行第三個(gè)單元格中第一個(gè)控件”該控件就是顯示數(shù)據(jù)的Label控件,那為什么要取第一個(gè)控件而不是第“零”個(gè)控件呢?答案是,當(dāng)GridView運(yùn)行的時(shí)候會(huì)在模板列中綁定顯示數(shù)據(jù)的Label控件的前后分別加入一個(gè)Literal控件。
下面我們繼續(xù)來(lái)解決未解決的問(wèn)題:(車(chē)延祿)
1.如何實(shí)現(xiàn)刪除時(shí)出現(xiàn)確認(rèn)提示的功能:
思路:把刪除按鈕所在的列轉(zhuǎn)換模板列,此時(shí)我們就會(huì)在“源頁(yè)面”中找到刪除按鈕了,然后在刪除按鈕里加入OnClientClick事件,執(zhí)行return window.confirm('...')腳本函數(shù)。
代碼界面:

運(yùn)行界面:

2.民族列在編輯狀態(tài)下顯示下拉列表
第一步:把民族列變?yōu)槟0辶?br /> 第二步:修改民族列的編輯模板,把原有的文本框刪除,加入下拉列表ddlNation和DataSource控件
第三步:配置DataSource控件,訪問(wèn)民族字典表。
第四步:將ddlNation下拉列表綁定到DataSource控件上
第五步:打開(kāi)“源頁(yè)面”在ddlNation控件中加入屬性:SelectedValue=<%#Bind('Nation')%>,把下拉列表框的選中值設(shè)為當(dāng)前行的民族值。
運(yùn)行效果:

大家思考:如何把性別列變?yōu)閱蜗劝粹o列表形式?
3.在編輯狀態(tài)下加入驗(yàn)證控件:
把在編輯狀態(tài)下需要加入驗(yàn)證控件的列全都轉(zhuǎn)換為模板列,然后在其編輯狀態(tài)模板上加入驗(yàn)證控件,并設(shè)置其驗(yàn)證相應(yīng)的文本框即可。
4.用模板列實(shí)現(xiàn)GridView的嵌套
在GridView模板列的單元格中再加入GridView。這種形式將來(lái)有可能用到。
運(yùn)行效果:
《圖23》
這個(gè)例子使用了三個(gè)GridView進(jìn)行嵌套,顯示出制造商、系列和汽車(chē)型號(hào)之間的關(guān)系。
源代碼如下:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
CellPadding="0" DataKeyNames="Prod_code" DataSourceID="ObjectDataSource1"
ForeColor="#333333" GridLines="Horizontal" onrowdatabound="GridView1_RowDataBound"
Width="100%">
<FooterStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" />
<RowStyle BackColor="#E3EAEB" HorizontalAlign="Center" />
<Columns>
<asp:BoundField DataField="Prod_name" HeaderText="生產(chǎn)廠商"
SortExpression="Prod_name">
<HeaderStyle Width="100px" />
<ItemStyle BackColor="#99CCFF" />
</asp:BoundField>
<asp:TemplateField HeaderText="汽車(chē)信息">
<ItemTemplate>
<asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="False"
CellPadding="0" ForeColor="#333333"
GridLines="Horizontal" ShowHeader="False" Width="100%"
DataKeyNames="Brand_Code" onrowdatabound="GridView2_RowDataBound">
<FooterStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" />
<RowStyle BackColor="#E3EAEB" BorderStyle="Dotted" HorizontalAlign="Left" />
<Columns>
<asp:BoundField DataField="Brand_name" HeaderText="Brand_name"
SortExpression="Brand_name" >
<HeaderStyle Width="100px" />
<ItemStyle BackColor="#FFCC66" Width="100px" />
</asp:BoundField>
<asp:TemplateField>
<ItemTemplate>(車(chē)延祿)
<asp:GridView ID="GridView3" runat="server" AutoGenerateColumns="False"
CellPadding="0" ForeColor="#333333" GridLines="None" ShowHeader="False"
Width="100%">
<FooterStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" />
<RowStyle BackColor="#E3EAEB" />
<Columns>
<asp:BoundField DataField="Name">
<ItemStyle BackColor="#CC99FF" HorizontalAlign="Left" Width="100%" />
</asp:BoundField>
</Columns>
</asp:GridView>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</ItemTemplate>
</asp:TemplateField>
</Columns>
...... </asp:GridView>
后臺(tái)代碼如下:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (e.Row.RowState == DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate)
{
string prodCode = GridView1.DataKeys[e.Row.RowIndex].Value.ToString();
ObjectDataSource sdsBrand = new ObjectDataSource(); ;
sdsBrand.ID = "ObjectDataSource2";
sdsBrand.TypeName = "BrandDA";
sdsBrand.SelectMethod = "selectProd_code";
sdsBrand.SelectParameters.Add("prod_code", prodCode);
e.Row.Cells[1].Controls.Add(sdsBrand);
GridView gvBrand = (GridView)e.Row.Cells[1].FindControl("GridView2");
gvBrand.DataSourceID = sdsBrand.ID;
}
}
}
protected void GridView2_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
if (e.Row.RowState == DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate)
{
GridView gvBrand = e.Row.Parent.Parent as GridView;
ObjectDataSource sdsCar = new ObjectDataSource(); ;
sdsCar.ID = "ObjectDataSource3";
sdsCar.TypeName = "CarDA";
sdsCar.SelectMethod = "selectBrand";
sdsCar.SelectParameters.Add("brand", brandCode);
e.Row.Cells[1].Controls.Add(sdsCar);
GridView gvCar = (GridView)e.Row.Cells[1].FindControl("GridView3");
gvCar.DataSourceID = sdsCar.ID;
}
}
}
五、按鈕列
除了執(zhí)行“命令列”功能之外的其它功能按鈕,這些按鈕在點(diǎn)擊的時(shí)候都會(huì)觸發(fā)RowCommand事件。如果在GridView中有多個(gè)按鈕列,那這多個(gè)按鈕列的按鈕就都過(guò)來(lái)執(zhí)行RowCommand事件中的代碼,如何區(qū)分是哪個(gè)按鈕列的哪個(gè)按鈕被點(diǎn)擊是一個(gè)關(guān)鍵性的問(wèn)題。(車(chē)延祿)
區(qū)分不同列的按鈕:
為區(qū)分不同類(lèi)型的按鈕(即不同列的按鈕),我們?cè)趧?chuàng)建按鈕列的時(shí)候可以為按鈕列設(shè)置CommandName,這樣不同類(lèi)型的按鈕就可以通過(guò)CommandName來(lái)區(qū)分了。
區(qū)分不同行的按鈕:
不同行的按鈕不用我們手動(dòng)區(qū)分,系統(tǒng)會(huì)自動(dòng)為我們區(qū)分,在RowCommand的事件參數(shù)GridViewCommandEventArgs e中有一個(gè)屬性e.CommandArgument,它的值就是行的索引號(hào)。因此我們可以通過(guò)e.CommandArgument判斷是哪一行的按鈕被點(diǎn)擊了。
我們既可以通過(guò)按鈕列的CommandName屬性來(lái)區(qū)分不同列的按鈕,又可以通過(guò)RowCommand事件的GridViewCommandEventArgs參數(shù)取得不同行的按鈕,那二者結(jié)合起來(lái)使用豈不可以隨意控制頁(yè)面中任何地方的按鈕了嗎?
如:
源代碼:
<asp:GridView ID="GridView1" runat="server" DataSourceID="ObjectDataSource1"
onrowcommand="GridView1_RowCommand" DataKeyNames="Ids">
<FooterStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" />
<RowStyle BackColor="#E3EAEB" />
<Columns>
......
<asp:ButtonField CommandName="cmdUP" Text="向上" />
<asp:ButtonField CommandName="cmdDN" Text="向下" />
</Columns>
......
</asp:GridView>
C#代碼:(車(chē)延祿)
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "cmdUP")
{
string pk = GridView1.DataKeys[Convert.ToInt32(e.CommandArgument)].Value.ToString();
Response.Write("主鍵為" + pk + "行的向上按鈕被點(diǎn)擊了");
}
else if (e.CommandName == "cmdDN")
{
string pk = GridView1.DataKeys[Convert.ToInt32(e.CommandArgument)].Value.ToString();
Response.Write("主鍵為" + pk + "行的向下按鈕被點(diǎn)擊了");
}
}