GridView內容詳解(轉載)

GridView是ASP.NET界面開發中的一個重要的控件,對GridView使用的熟練程度直接影響軟件開發的進度及功能的實現。(車延祿)
GridView的主要新特性:
    1.與DataSource控件結合實現了顯示與數據操作的分離,大大減化了代碼的編寫量;
    2.實現"雙向綁定",無需手動檢索數據。
    2.在列的類型上新增了CheckBoxField和ImageField兩個類型列;
    3.對排序和分頁可以實現異步操作;
    4.對其視圖狀態進行優化,使其運行效率更高;
   
GridView與DataSource控件
    要談GridView就必需要談DataSource,這兩個對象可稱得上是“最佳拍檔”。DataSource控件負責與數據源的交互,而GridView負責數據的顯示。它們之間通過“雙向綁定”聯系起來,即DataSource控件將檢索出來的數據綁定到GridView中顯示,而GridView中修改和刪除的數據直接綁定到DataSource數據源去。這兩個過程由這兩個控件相互配合實現的,無需我們編寫代碼。如果我們只用GridView顯示數據,而不用DataSource控件的話,那你只好編寫代碼實現DataSource要作的工作了。做為用過VS2003的用戶對這個工作并不陌生,但這種代碼實現不但工作量大,而且使用復雜,容量產生大量可讀性差的代碼,當然也是初學者的最大障礙。好在VS2005為我們提供了DataSource控件,使我們不再如此痛苦地Coding了(當然,如果有的情況下DataSource控件還無法完全替代Coding)。
    所以在使用GridView控件的時候我們最好配合DataSource控件使用,不要再去折磨自己了。 下面的案例不做特殊說明均以GridView+DataSource控件實現。

一、GridView外觀設置:
    1、總體外觀設置
     ShowFooter:(bool)是否顯示頁腳
     ShowHeader:(bool)是否顯示頁眉
     GridLines:(enum)None-不顯示格線;Horizontal-顯示水平格線;Virtical-顯示豎直格線;Both-顯示水平和豎直格線
     EmptyDataText:(string)如果數據源中內容為NULL時在GridView中顯示的值
        AlternatingRowStyle:交替項的樣式
        EditRowStyle:編輯項的樣式
        EmptyDataRowStyle:空數據項的樣式
        FooterStyle:頁腳樣式
        HeaderStyle:頁眉樣式
        PagerStyle:分頁樣式
        RowStyle:行樣式
        SelectedRowStyle:選中項樣式
        注:1、使用GridView的時候我們一般可以使用“自動套用樣式”選中一個樣式,然后在此樣式的基礎上修改上面的屬性,從而制作出滿意外觀效果。
            2、上面的樣式可以對GridView進行總體的外觀設置,如果對某一列進行設置的話那可要在GridView右上角“智能菜單中”點擊“編輯列”進行設置。
        如:
            直接套用樣式后的外觀
            


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

    2、對綁定列進行外觀設置
        對上面的圖我們可以看出外觀方面有幾個問題:
            a.頁眉是英文的
            b.所有的數據都是居中對齊
            c.time列的數據中顯示了時分秒的數據
            d.price列中應加上"RMB"的符號
            e.最好對價格列的數據以紅色字體顯示
        上面的這些問題我們可以通地單獨對列進行格式設置來實現
        點擊GridView右上角“智能菜單中”點擊“編輯列”,打開"字段"窗口:
        


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

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


            RowCreated事件:是每一行“創建行”完成時被觸發的事件。
            RowDataBound事件:是每一行“綁定行”完成時被觸發的事件。
        因此在綁定GridView顯示數據過程也就是循環觸發RowCreated事件和RowDataBound事件的過程。
        下面的代碼驗證了我上面的闡述:(車延祿)
        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=>");
        }
        執行結果:
        


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

        在上面的函數中,有幾個需要重點說明的問題:
       1.GridViewRowEventArgs e形參:這是在建造行對象時產生的事件數據,它里面包含正在建造的當前行對象的信息,通過它我們可以取得正在建造的行對象和行對象中的單元格對象,并可以取得或設置這些對象相應的屬性。
        2.e.Row.RowType屬性:(DataControlRowType枚舉類型)它返回正在建造的行對象的類型(如:頁眉、頁腳、數據行、分頁行等)
        3.e.Row.RowState屬性:(DataControlRowState枚舉類型)它返回正在建造的行對象的狀態(如:普通狀態、交替狀態、編輯狀態、選中狀態等)
        下面這段代碼,大家一定不能漏掉
       if (e.Row.RowType == DataControlRowType.DataRow)
        {
            if (e.Row.RowState == DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate)
            {
            }
        }

        它代表我們的操作只對數據行起作用,不對頁眉和頁腳起作用(因為頁眉頁腳的文本中并不包含我們需要的數據)。并且僅當數據行處理普通狀態和交替狀態時起作用,在編輯和選中狀態不是不起作用的(編輯狀態下,數據不是顯示在單元格的Text中,而是顯示在單元格的控件中的)。(車延祿)
       
二、超鏈接列的使用
    超鏈接列也是開發過程中使用比較多的列的類型,它是在GridView的單元格中顯示超鏈接信息,在點擊超鏈接時跳轉到相應的頁面上去。
    


    如上圖所示,由于我們Car表中的列很多,如果在一個頁面中全顯示出來會使GridView中單元格的數據變形,很影響我們的外觀。我們可以只把幾項主要信息顯示出來,然后在每一行的后面加一個超鏈接列,當點擊超鏈接列時轉到一個詳細頁面,把每一型號汽車的具體信息詳細顯示出來。
    效果如下圖
        汽車列表頁面
        


        汽車詳細信息頁面
        


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

    這里的屬性較多,在這里我們一起來看一下
        HeaderText:該超鏈接列的頁眉文本
        FooterText:該超鏈接列的頁腳文本
        Text:超鏈接列中的鏈接文字,這個屬性用來把該列所有單元格的超鏈接的文字設成相同的。
        DataTextField:指定數據源的一個字段名,把該字段的內容作為超鏈接列的超鏈接文本。如果同時設置了Text屬性和DataTextField屬性,則忽略Text屬性的設置。
        DataTextFormatterString:為綁定顯示的DataTextFied數據設置顯示格式
        DataNavigateUrlField:指定數據源的一個字段名,該字段的內容作為超鏈接列導向的網址,或者把該字段的值作為超鏈接傳遞的參數。
        DataNavigateUrlFormatterString:為 DataNavigateUrlField列設置超鏈接的導航格式。如Default7.aspx?id={0}則該列的超鏈接導航到 Default7.aspx頁面,并且把DataNavigateUrlField中指定的字段值賦給id,作為查詢字符串傳遞至 Default7.aspx頁面
        如果超鏈接列需要向目標頁面傳遞多個字段的值作為參數的話,那么請在DataNavigateUrlField中指定多個字段名并用逗號隔開,然后再在DataNavigateUrlFormatterString屬性中用"&"鏈接多個參數(如:default7.aspx?id={0}&name={1})


三、命令列的使用
    命令列主要提供了GridView中常用的一些修改、更新、刪除的操作,在GridView與DataSource控件結合使用的情況下,我們并不需要為些編寫太多的代碼就可以實現簡單的維護操作,如果在復雜的情況下(如一對多鏈接查詢時),我們仍需要編寫代碼來實現更新與刪除。(車延祿)
    

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

    DataKeyNames屬性用來設置GridView對應的數據源的主鍵列,只有設置這個屬性,在刪除的時候才會把要刪除的主鍵值傳遞給DataSource控件執行刪除功能。
    現在編輯界面如圖:
    

    我們一起來看一下運行結果所存在的問題:
        1.主鍵列Code不應當被編輯
        2.GridView自動產生的文本框太寬,把我們的GridView都給擠變形了。
        3.民族列在瀏覽時一直都顯示民族代號,應當顯示民族名稱
        4.民族列在修改的時候最好使用下拉列表讓用戶選擇相應的民族。
        5.文本輸入框沒有任何驗證,對輸錯的信息在提交的時候會產生異常
        6.刪除時沒有確認提示
        7.刪除時產生外鍵引用的異常
    這里的問題實在是太多了,有的問題很簡單就可以解決,但有的問題我們借助后面的模板列會更簡單一些。
    1、防止某列被編輯,只需要在“編輯列”對應框中將該列的ReadOnly屬性設為true就可以了。
    2、代碼控制編輯狀態下文本框的寬度(這種方式有點復雜,可以用模板列實現)
        我們上面已經分析過GridView每一行的建造過程了,它包含“創建行”和“綁定行”兩步操作,“創建行”的過程中會把一些靜態的東西創建出來,當然也包含文本框的創建。因此我們只需要在RowCreated事件中把編輯列中的文本框的長度改變一下就可以了。
        protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType == DataControlRowType.DataRow)
            {
                //這里可能是VS2005的一個Bug,所以我只好把整型強制轉為DataControlRowState枚舉類型再與e.Row.RowState屬性進行比較
                //4-Normal行的編輯狀態,5-Alternating行處于編輯狀態
                if (e.Row.RowState == (DataControlRowState)4||e.Row.RowState==(DataControlRowState)5)
                {
                    //Width屬性是Unit類型,不能直接賦整型值
                    //Unit.Pixel():按像素設置絕對寬度;Unit.Percent():按百分比設置寬度
                    ((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.瀏覽狀態時顯示民族名稱,只需要將實體中添加一個只讀屬性NationName,然后用它替換Nation字段,綁定到GridView就可以了。
    4.對主表中的數據刪除時級聯刪除相關子表數據
        這種問題用很多種實現方式:
            a.使用數據庫中外鍵的級聯刪除功能
            b.使用存儲過程或觸發器。
            c.在程序代碼中刪除
        前兩種方式實現方式主要是數據庫方面的知識,這里不多說了,下面我們主要看看如何使用程序代碼刪除數據。
        當我們點擊刪除按鈕的時候,GridView會自動把要刪除的行對象傳遞給DataSource控件,然后DataSource調用相關的業務類的方法執行刪除,再然后就發生“外鍵引用異常”......
        整個這個過程是系統自動完成的,那我們要想自己寫代碼進行級聯刪除,并且禁止系統自動調用默認的刪除功能,那如何實現呢?
        下面顯示了GridView中的一部分事件
        


        大家會注意到其中的事件大部分都有兩類:***ed和***ing兩類。如:Deleted和Deleting事件
        ***ed事件:系統執先完操作后觸發,如Deleted事件是系統調用默認刪除功能,刪除成功后自動觸發
        ***ing事件:系統執行操作之前觸發,如Deleting事件是系統調用默認刪除功能之前觸發。
        現在大家應當清楚在點擊刪除按鈕后如何添加自己的刪除處理程序了,答案當然是應當在Deleting中編寫代碼進行級聯刪除。
        主要代碼如下:
        protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
        {
            //取得要刪除行的主鍵的值
            string str = GridView1.DataKeys[e.RowIndex].Value.ToString();
            //使用事務
            using (TransactionScope ts = new TransactionScope())
            {
                //刪除該主鍵值在刪除簡歷表中對應的數據
                new WorkDA().deleteInfo(str);
                //刪除該主鍵值在刪除家庭關系表中對應的數據
                new FamilyDA().deleteInfo(str);
                //刪除該主鍵值在基本信息表中對應的數據
                new InfoDA().delete(str);
                //提交事務
                ts.Complete();
            }
            //取銷事件進一步響影,即該刪除操作不再往上提交至DataSource控件。
            e.Cancel = true;
        }
    問題還沒全部解決,剩下的問題我們留給模板列來解決。(雖然不用模板列也可以解決上面所有的問題,但是那可需要大量的Coding,所以建議大家使用模板列)

 

四、模板列
    關于模板列我們可以用四個字來描述--“無所不能”。很多棘手的問題,在遇到模板列的時候會迎刃而解,因為模板列的定制功能很強大,允許我們建立不同狀態下的模板,所以我們用代碼控制起它來也就更加靈活自如了。(車延祿)
    創建模板列一般有兩種方式:
        在“編輯列”對話框中向“選中列”中添加TemplateField字段
        在“編輯列”對話框中點擊“選中列”中相應的列,再點擊“確定”按鈕上方的“將此字段轉換為TemplateField”文字,這就把現有的列轉換為模板列了。這種轉換方式大家要小心使用,因為一旦轉換為模板列,就沒辦法再轉換回去了。
    當一個綁定列被轉換成模板列后會發生如下變化:
    變化前的代碼:
    


    變化后的代碼:
    


    變化后的設計界面
    


    通過上面的圖我們可以看到,當一個綁定列轉換為模板列后,該列的數據顯示時,會把數據源的數據綁定到模板列的Label控件中去。而不是像綁定列那樣直接把數據綁定到單元格的Text屬性上。
    所以取模板列的單元格中的值與取綁定列的單元格中的值是有區別的:
    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 ="...";
            }
        }
    }
    在綁定列中,數據直接綁定在單元格的Text屬性上,所以可以直接用e.Row.Cells[4].Text進行操作;但在模板列中,數據并沒有綁定在單元格的Text上,而是綁定在Label控件的文本上,所以應當通過 e.Row.Cells[3].Controls[1]來取得Label控件(要記得強制轉換)。細心的朋友會發現這里我是取“當前行第三個單元格中第一個控件”該控件就是顯示數據的Label控件,那為什么要取第一個控件而不是第“零”個控件呢?答案是,當GridView運行的時候會在模板列中綁定顯示數據的Label控件的前后分別加入一個Literal控件。
   
    下面我們繼續來解決未解決的問題:(車延祿)
    1.如何實現刪除時出現確認提示的功能:
        思路:把刪除按鈕所在的列轉換模板列,此時我們就會在“源頁面”中找到刪除按鈕了,然后在刪除按鈕里加入OnClientClick事件,執行return window.confirm('...')腳本函數。
        代碼界面:
        


        運行界面:
        

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

        大家思考:如何把性別列變為單先按鈕列表形式?
    3.在編輯狀態下加入驗證控件:
        把在編輯狀態下需要加入驗證控件的列全都轉換為模板列,然后在其編輯狀態模板上加入驗證控件,并設置其驗證相應的文本框即可。

    4.用模板列實現GridView的嵌套
        在GridView模板列的單元格中再加入GridView。這種形式將來有可能用到。
        運行效果:
        《圖23》
        這個例子使用了三個GridView進行嵌套,顯示出制造商、系列和汽車型號之間的關系。
        源代碼如下:
        <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="生產廠商"
                    SortExpression="Prod_name">
                    <HeaderStyle Width="100px" />
                    <ItemStyle BackColor="#99CCFF" />
                </asp:BoundField>
                <asp:TemplateField HeaderText="汽車信息">
                    <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>
(車延祿)
                                        <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>
      
        后臺代碼如下:
        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;

                    string brandCode = gvBrand.DataKeys[e.Row.RowIndex].Value.ToString();
                    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;
                }
            }
        }
   
五、按鈕列   
        除了執行“命令列”功能之外的其它功能按鈕,這些按鈕在點擊的時候都會觸發RowCommand事件。如果在GridView中有多個按鈕列,那這多個按鈕列的按鈕就都過來執行RowCommand事件中的代碼,如何區分是哪個按鈕列的哪個按鈕被點擊是一個關鍵性的問題。(車延祿)
        區分不同列的按鈕:
            為區分不同類型的按鈕(即不同列的按鈕),我們在創建按鈕列的時候可以為按鈕列設置CommandName,這樣不同類型的按鈕就可以通過CommandName來區分了。
        區分不同行的按鈕:
            不同行的按鈕不用我們手動區分,系統會自動為我們區分,在RowCommand的事件參數GridViewCommandEventArgs e中有一個屬性e.CommandArgument,它的值就是行的索引號。因此我們可以通過e.CommandArgument判斷是哪一行的按鈕被點擊了。
        我們既可以通過按鈕列的CommandName屬性來區分不同列的按鈕,又可以通過RowCommand事件的GridViewCommandEventArgs參數取得不同行的按鈕,那二者結合起來使用豈不可以隨意控制頁面中任何地方的按鈕了嗎?
       
        如:
        源代碼:
        <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#代碼:(車延祿)
        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 + "行的向上按鈕被點擊了");
            }
            else if (e.CommandName == "cmdDN")
            {
                string pk = GridView1.DataKeys[Convert.ToInt32(e.CommandArgument)].Value.ToString();
                Response.Write("主鍵為" + pk + "行的向下按鈕被點擊了");
            }
        }