緣起:
小喵接觸ASP.NET是從ASP.NET 2.0開始(VS2005),而GridView這個控制項也是從這個時候開始出現,由於有DataSouce的輔助,讓我們在使用上非常的方便。只要拖拉一下,設定一下,資料就可以透過GridView顯示在網頁上了。不過方便的結果,可能用了一段時間,開發了幾套系統,卻還不知道這GridView到底是怎麼運作的(因為通通包裝的好好的)。
這一篇是強迫不使用DataSource(SqlDatasouce,ObjectDataSouce,AccessDataSource,...)的情況下,透過ADO的存取資料庫,以及GridView的各個事件,來理解GridView的一些運作狀況。小喵會在這篇中,寫下有關GridView的資料繫結、編輯、修改、刪除、排序、分頁等功能的程式碼。
準備工作:
此篇範例照慣例,使用北風資料庫當做範例。用最簡單的資料表【Region】來當作範例,當然大家在練習時,可以自己去改掉Connection String與Table名稱。
畫面:
畫面上安排很簡單,就是安排一個GridView,然後隨便選一個樣式(可以分辨編輯實的顏色變化),另外加入一個Templete Fields,用來放置【編輯、刪除、維護、取消】的按鈕。另外再安排一個Button,用來第一次繫結資料。相關程式碼如下:
<asp:Label ID="lblMsg" runat="server" Text=""></asp:Label> <br /> <asp:Button ID="Button1" runat="server" Text="Button" /> <asp:GridView ID="GridView1" runat="server" AllowSorting="True" BackColor="White" BorderColor="#E7E7FF" BorderStyle="None" BorderWidth="1px" CellPadding="3" GridLines="Horizontal" AllowPaging="True" PageSize="3"> <RowStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" /> <Columns> <asp:TemplateField> <EditItemTemplate> <asp:Button ID="btnUpdate" runat="server" Text="維護" CommandName="Update" /> <asp:Button ID="btnCancel" runat="server" Text="取消" CommandName="Cancel" /> </EditItemTemplate> <ItemTemplate> <asp:Button ID="btnEdit" runat="server" Text="編輯" CommandName="Edit" /> <asp:Button ID="btnDel" runat="server" Text="刪除" CommandName="Delete" OnClientClick="return confirm('您確定要刪除此筆資料嗎??');" /> </ItemTemplate> </asp:TemplateField> </Columns> <FooterStyle BackColor="#B5C7DE" ForeColor="#4A3C8C" /> <PagerStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" HorizontalAlign="Right" /> <SelectedRowStyle BackColor="#738A9C" Font-Bold="True" ForeColor="#F7F7F7" /> <HeaderStyle BackColor="#4A3C8C" Font-Bold="True" ForeColor="#F7F7F7" /> <AlternatingRowStyle BackColor="#F7F7F7" /> </asp:GridView>
後置程式碼:
接著就開始來撰寫程式碼的部分。首先,要手動寫程式了,當然要Imports相關的NameSpance
Imports System.Data Imports System.Data.SqlClient
並且設定一下Connection String
Private ConnStr As String = "Data Source=.\sqlexpress;Initial Catalog=Northwind;Integrated Security=True"
接著撰寫【查詢、修改、刪除】資料的Function,用來【讀取、修改、刪除】Region資料表,讀取的Function傳回DataTable。
Private Function GetData() As DataTable Dim Dt As New DataTable Try Using Conn As New SqlConnection(ConnStr) Dim SqlTxt As String = "" SqlTxt += " SELECT TOP 50 * " SqlTxt += " FROM [Region] (NOLOCK) " SqlTxt += " " Dim Cmmd As New SqlCommand(SqlTxt, Conn) Dim Da As New SqlDataAdapter(Cmmd) Da.Fill(Dt) End Using Catch ex As Exception Me.lblMsg.Text = ex.Message End Try Return Dt End Function Private Function DeleteData(ByVal RegionID As Integer) As String Dim rc As String = "" Try Using Conn As New SqlConnection(ConnStr) Conn.Open() Dim SqlTxt As String = "" SqlTxt += " DELETE Region " SqlTxt += " WHERE RegionID = @RegionID " SqlTxt += " " Using Cmmd As New SqlCommand(SqlTxt, Conn) Cmmd.Parameters.AddWithValue("@RegionID", RegionID) Cmmd.ExecuteNonQuery() End Using rc = "Success" End Using Catch ex As Exception rc = "False" Me.lblMsg.Text = ex.Message End Try Return rc End Function Private Function UpdData(ByVal RegionID As Integer, ByVal RegionDescription As String) As String Dim Rc As String = "" Try Using Conn As New SqlConnection(ConnStr) Conn.Open() Dim SqlTxt As String = "" SqlTxt += " UPDATE Region " SqlTxt += " SET RegionDescription = @RegionDescription " SqlTxt += " WHERE RegionID = @RegionID " SqlTxt += " " Using Cmmd As New SqlCommand(SqlTxt, Conn) Cmmd.Parameters.AddWithValue("@RegionDescription", RegionDescription) Cmmd.Parameters.AddWithValue("@RegionID", RegionID) Cmmd.ExecuteNonQuery() Rc = "Success" End Using End Using Catch ex As Exception Rc = "False" Me.lblMsg.Text = ex.Message End Try Return Rc End Function
接著陸續來看查詢、編輯、修改、刪除、分頁、排序的各個程式碼:
查詢:當按鈕按下去後,將資料繫結給GridView,因此先寫一個將資料繫結的Sub GVGetData()來處理,未來會有很多地方都要呼叫這一個Sub作繫結資料處理
'抓取資料並繫結GridView Private Sub GVGetData() Try Dim Dt As DataTable = GetData() Me.GridView1.DataSource = Dt Me.GridView1.DataBind() Catch ex As Exception Me.lblMsg.Text = ex.Message End Try End Sub
接著,按鈕按下去的時候,只需要呼叫他就可以了
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click '呼叫資料繫結 GVGetData() End Sub
接著,再來看分頁如何做。分頁時,要設定GridView的AllowPaging=True,另外,由於這個資料表的資料不多,所以改一下,一頁的筆數預設是10筆,改為3筆(PageSize="3")。此時就會有分頁的功能,不過當換頁時,會觸發【PageIndexChanging】與【PageIndexChanged】這兩個事件。程式碼如下:
Protected Sub GridView1_PageIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles GridView1.PageIndexChanged '顯示PageIndexChanged事件被呼叫到 Response.Write("PageIndexChanged!!") End Sub Protected Sub GridView1_PageIndexChanging(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewPageEventArgs) Handles GridView1.PageIndexChanging '設定分頁停在第幾頁 Me.GridView1.PageIndex = e.NewPageIndex '繫結資料 GVGetData() End Sub
排序:查詢、分頁有了,接著就是排序。排序的時候,會觸發Sorting與Sorted這兩個事件。不過這個部分稍微麻煩,經測試後發現,在Sorting的e.SortDirection並沒有記住這次順排(Ascending)→下次就逆排(Descending)的狀況,每次取得的e.SortDirection通通都是順排(Ascending)。為了達到第一次點順排,再點一次是逆排,因此要透過ViewState來記錄上次的方式,在加上判斷。
另外,本來的繫結資料時,並沒有排序,只有直接把取得的DataTable給GridView。現在要加上排序的功能了,那麼就要拿DataTable的資料來做排序的動作,這個部分要借用【DataView】的【Sort】來設定,而且DataView也可以當作GridView的資料來源。因此我們改寫一下繫結資料的部分先,然後再寫Sorting與Sorted這兩個事件
繫結資料的部分:
透過多型,希望Sorting的時候要繫結資料呼叫GVGetData的另一個型態
'有指定排序的抓資料並繫結GridView Private Sub GVGetData(ByVal pSortDirection As SortDirection, ByVal pSortExpression As String) Try Dim Dt As DataTable = GetData() '設定排序的語法 Dim strSort As String = "" If pSortDirection = SortDirection.Ascending Then '如果是順排(A~Z) strSort = pSortExpression Else '逆排的時候(Z~A),加上DESC strSort = pSortExpression & " DESC" End If '使用DataView來做GridView的資料來源 Dim Dv As DataView = Dt.DefaultView '設定DataView的排序方式 Dv.Sort = strSort Me.GridView1.DataSource = Dv Me.GridView1.DataBind() Catch ex As Exception Me.lblMsg.Text = ex.Message End Try End Sub
接著是Sorting與Sorted的部分
Protected Sub GridView1_Sorted(ByVal sender As Object, ByVal e As System.EventArgs) Handles GridView1.Sorted '顯示Sorted事件備觸發 Response.Write("Sorted!!") End Sub Protected Sub GridView1_Sorting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewSortEventArgs) Handles GridView1.Sorting Dim NowSE As String = CType(ViewState("NowSE"), String) '現在的排序欄位 Dim NowSD As SortDirection = CType(ViewState("NowSD"), SortDirection) '目前的排序方向 If NowSE Is Nothing Then '如果沒有ViewState,指定e.SortExpression與順排當作預設的欄位與方向 NowSE = e.SortExpression NowSD = SortDirection.Ascending Else '有ViewState時 If NowSE <> e.SortExpression Then '如果欄位與本來的不同 '指定目前的欄位為e.SortExpression NowSE = e.SortExpression '指定目前的排序方向為順排 NowSD = SortDirection.Ascending Else '如果欄位與本來相同 If NowSD = SortDirection.Ascending Then '當本來為順排→改為逆排 NowSD = SortDirection.Descending Else '當本來違逆排→改為順排 NowSD = SortDirection.Ascending End If End If End If '將得到的欄位與方向,紀錄回ViewState ViewState("NowSD") = NowSD ViewState("NowSE") = NowSE '呼叫繫結資料,並指定排序欄位與方向 GVGetData(NowSD, NowSE) End Sub
這樣子,排序就OK了。不過小喵發現,這樣子排序配合分頁的切換,由於本來的沒有考慮排序,所以每次分頁一切換,就會弄亂排序的狀況。因此要改一下資料繫結的部分,檢查ViewState中是否有設定排序的欄位與方向。改一下本來的GVGetData()
'抓取資料並繫結GridView Private Sub GVGetData() Try '判斷是否有排序過 If ViewState("NowSE") Is Nothing Then '沒有排序過,直接抓DataTable Dim Dt As DataTable = GetData() Me.GridView1.DataSource = Dt Me.GridView1.DataBind() Else '排序過,因此除了抓資料,還要排序 Dim NowSE As String = CType(ViewState("NowSE"), String) Dim NowSD As SortDirection = CType(ViewState("NowSD"), SortDirection) GVGetData(NowSD, NowSE) End If Catch ex As Exception Me.lblMsg.Text = ex.Message End Try End Sub
這樣無論排序、分頁的部分都可以正常運作了。
接著,就是編輯、修改、刪除的程式碼。這部分小喵發現,在預期的狀況下,修改、刪除的時候,會觸發RowUpdating與RowUpdated / 或者 RowDeleting與RowDeleted。但是實際Step By Step的運作下,令人驚訝的發現,維護後的RowUpdated與修改後的RowDeleted這兩個事件並不會被觸發。這與小喵以往的印象ing:處理中/ed:處理後的理解與期望不同。小喵特別發了一個討論在小舖中【手動寫程式處理GridView的維護,為何沒有觸發RowUpdated事件】,看來GridView沒有使用DataSourceID去繫結DataSource控制項的狀況下,不會觸發這兩個ed的事件
以下為編輯、修改、取消、刪除的程式碼:
刪除資料:
Protected Sub GridView1_RowDeleted(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeletedEventArgs) Handles GridView1.RowDeleted '顯示RowDeleted備觸發→事實上用程式碼並不會觸發此事件!! Response.Write("RowDeleted") 'GVGetData() End Sub Protected Sub GridView1_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles GridView1.RowDeleting Try Dim RegionID As Integer '刪除的Key值 '取得刪除的Key RegionID = CType(Me.GridView1.Rows(e.RowIndex).Cells(1).Text, Integer) '呼叫刪除的Function Dim rc As String = DeleteData(RegionID) If rc = "Success" Then Me.lblMsg.Text = "刪除成功!!" '呼叫繫結資料重新繫結 GVGetData() End If Catch ex As Exception Me.lblMsg.Text = ex.Message End Try End Sub
編輯:
Protected Sub GridView1_RowEditing(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs) Handles GridView1.RowEditing '設定編輯的Index Me.GridView1.EditIndex = e.NewEditIndex '繫結資料 GVGetData() End Sub
取消:
Protected Sub GridView1_RowCancelingEdit(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCancelEditEventArgs) Handles GridView1.RowCancelingEdit '取消編輯模式→設定GridView的EditIndex = -1 Me.GridView1.EditIndex = -1 GVGetData() End Sub
修改:
Protected Sub GridView1_RowUpdated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewUpdatedEventArgs) Handles GridView1.RowUpdated '離開編輯模式 'Me.GridView1.EditIndex = -1 'GVGetData() Response.Write("RowUpdated") End Sub Protected Sub GridView1_RowUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewUpdateEventArgs) Handles GridView1.RowUpdating Try Dim RegionID As Integer Dim RegionDescription As String '取得畫面中的資料 Dim tGvRw As GridViewRow = Me.GridView1.Rows(e.RowIndex) RegionID = CType(CType(tGvRw.Cells(1).Controls(0), TextBox).Text, Integer) RegionDescription = CType(tGvRw.Cells(2).Controls(0), TextBox).Text '進行維護 Dim Rc As String = UpdData(RegionID, RegionDescription) If Rc = "Success" Then Me.lblMsg.Text = "維護成功" '離開編輯模式 Me.GridView1.EditIndex = -1 GVGetData() End If Catch ex As Exception Me.lblMsg.Text = ex.Message End Try End Sub
後記
再經過這樣的練習後,對於GridView的一些運作,會有比較清楚了瞭解。據說這樣的能力在以前DataGrid的時代,是基本的能力,也就是大家都必須這麼寫。自從ASP.NET 2.0開始,GridView搭配DataSource控制項實在太好用了。簡單的使用SqlDataSource只需要拖拉、設定就可以通通達成。進一步需要透過程式處理,也可以寫成Class透過ObjectDataSource來達成。不過,當系統的需求越來越複雜,有時候就需要去透過GridView各項事件去處理一些狀況。透過這樣的練習可以來了解一下各個事件的用法。小喵因此將過程筆記下來,也提供大家參考。
![]() Microsoft MVP ASP/ASP.NET |
![]() topcat |