- .NET Web高級開發
- 羅江華 朱永光編著
- 4675字
- 2018-12-29 13:19:41
1.2.3 GridView和DetailsView控件
DataGrid是ASP.NET中最受歡迎的控件之一,但在某些方面,它也成為自己成功的犧牲品:如此豐富的功能,以至于讓ASP.NET開發人員不滿足于此,而是希望它能提供更多功能。DataGrid控件在ASP.NET 2.0中并沒有發生太大變化,只是添加了兩個分別名為GridView和DetailsView的新控件,它們提供了通常要求DataGrid控件所具有的功能,并且還加入了一些屬于它們自己的新功能。
GridView呈現出HTML表的方式與DataGrid一樣,但與DataGrid不同的是,GridView可以完全依靠自己來分頁和排序。GridView還支持比DataGrid種類更為豐富的列類型(在GridView用語中稱為字段類型),并且它們具有更為智能的默認呈現行為,能夠自動呈現Boolean值(例如,通過復選框)。GridView也可以容易地與DetailsView搭配使用,以創建主-從視圖。GridView控件的主要缺陷是:像DataGrid一樣,它通過將信息傳回到服務器來完成它該做的大部分工作。
具體來說GridView可以完成以下一些操作:
· 通過數據源控件自動綁定和顯示數據。
· 通過數據源控件對數據進行選擇、排序、分頁、編輯和刪除。
· 通過自定義GridView控件來改變外觀和行為:指定自定義列和樣式。
· 利用模板創建自定義用戶界面(UI)元素。
· 通過事件將自己的代碼添加到控件的功能中去。
1.通過數據源控件自動綁定和顯示數據
GridView控件提供了兩種方式用于綁定到數據:要么使用DataSourceID屬性進行數據綁定,此方法能夠將GridView控件綁定到數據源控件,同時這種方式比較常見也建議使用,因為它允許GridView控件利用數據源控件的功能并提供了內置的排序、分頁和更新功能;要么使用DataSource屬性進行數據綁定,通過這種方式能夠綁定到包括ADO.NET數據集和數據讀取器在內的各種對象。此方法需要為所有附加功能(如排序、分頁和更新)編寫代碼。當使用DataSourceID屬性綁定到數據源時,GridView控件支持雙向數據綁定。除可以使該控件顯示返回的數據之外,還可以使它自動支持對綁定數據的更新和刪除操作。
2.通過數據源控件對數據進行選擇、排序、分頁、編輯和刪除
GridView控件有一個內置分頁功能,可支持基本的分頁功能。讀者可以使用默認分頁功能或創建自定義的分頁效果。GridView控件支持對其數據源中的項進行分頁。將AllowPaging屬性設置為true就可以啟用分頁。GridView控件可以通過多種方式支持分頁:如果將GridView控件綁定到在界面級別支持分頁功能的數據源控件,則GridView控件將直接利用這一功能。在界面級別分頁意味著GridView控件僅從數據源請求呈現當前頁所需的記錄數。請求的記錄數可能會根據其他因素變化,例如數據源是否支持獲取總記錄數,由PageSize屬性指定的每頁的記錄數,以及在將PageButtonCount屬性設置為Numeric時要顯示的頁導航按鈕的數目。而實際上在.NET Framework所包含的數據源控件中,只有ObjectDataSource控件在界面級別支持分頁;如果將GridView控件綁定到不直接支持分頁功能的數據源控件,或者如果通過DataSource屬性利用代碼將GridView控件綁定到一個數據結構,則GridView控件將按照先從源獲取所有數據記錄來進行分頁,且僅顯示當前頁的記錄,然后丟棄剩余的記錄。但是只有在GridView控件的數據源返回一個實現ICollection接口的集合(包括數據集)時,才支持這種分頁方式。
需要注意的是,如果數據源不直接支持分頁且未能實現ICollection接口,則GridView控件將無法進行分頁。例如讀者正在使用SqlDataSource控件,并將其DataSourceMode屬性設置為DataReader,則GridView控件就無法實現分頁。
對于自定義分頁設置和用戶界面,讀者可以通過多種方式自定義GridView控件的分頁用戶界面。可以通過使用PageSize屬性來設置頁的大小(即每次顯示的項數)。還可以通過設置PageIndex屬性來設置GridView控件的當前頁。可以使用PagerSettings屬性或通過提供頁導航模板來指定更多的自定義行為。分頁模式將AllowPaging屬性設置為true時,PagerSettings屬性則允許自定義由GridView控件自動生成的分頁用戶界面的外觀。GridView控件可顯示允許向前和向后導航的方向控件,以及允許用戶移動到特定頁的數字控件。
對于分頁事件,當GridView控件移動到新的數據頁時,該控件會引發兩個事件:PageIndexChanging事件在GridView控件執行分頁操作之前發生;PageIndexChanged事件在新的數據頁返回到GridView控件之后發生。如果需要可以使用PageIndexChanging事件取消分頁操作或在GridView控件請求新的數據頁之前執行某項任務,也可以使用PageIndexChanged事件在用戶移動到另一個數據頁之后執行某項任務。在默認情況下,GridView控件在只讀模式下顯示數據。但是該控件還支持一種編輯模式,在該模式下控件顯示一個包含可編輯控件(如TextBox或CheckBox控件)的行。還可以對GridView控件進行配置以顯示一個Delete按鈕,用戶可單擊該按鈕來刪除數據源中相應的記錄。同時GridView控件支持在不需要任何編程的情況下通過單個列排序。通過使用排序事件以及提供排序表達式就可以進一步自定義GridView控件的排序功能。
3.實現GridView批量刪除、自定義分頁、定位頁碼
下面以一個完整的自定義分頁程序演示前面的部分理論,其代碼如下:
<table height="20" border="1" align="center" cellpadding="0" cellspacing="0" style="width: 97%"> <tr> <td align="center" style="width: 24%"> <asp:CheckBox ID="cbAll" runat="server" AutoPostBack="True" OnChecked Changed="cbAll_CheckedChanged" Text="Select All /UnSelect All" /> </td> <td align="center" style="width: 16%">ProductName</td> <td width="39%" align="center">UnitPrice</td> <td width="21%" align="center">IsDiscontinued</td> </tr> </table> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" BackColor="White" BorderColor="#E7E7FF" BorderWidth="1px" CellPadding="3" DataKeyNames="ProductID" HorizontalAlign="Center" Width="97%" BorderStyle="None" ShowHeader="False" AllowPaging="True" OnDataBound="GridView1_DataBound" GridLines="Horizontal"> <FooterStyle BackColor="#B5C7DE" ForeColor="#4A3C8C" /> <Columns> <asp:TemplateField > <ItemStyle HorizontalAlign="Center" /> <ItemTemplate> <asp:CheckBox ID="checkAll" runat=server /> </ItemTemplate> </asp:TemplateField> <asp:BoundField DataField="ProductName" HeaderText="ProductName"> <ItemStyle Width="16%" /> </asp:BoundField> <asp:HyperLinkField DataNavigateUrlFields="ProductID" DataNavigateUrl FormatString="Test.aspx?id={0}" DataTextField="UnitPrice" HeaderText="UnitPrice" > <ItemStyle Width="39%" /> </asp:HyperLinkField> <asp:BoundField DataField="Discontinued" HeaderText="Discontinued"> <ItemStyle Width="21%" /> </asp:BoundField> </Columns> <PagerTemplate> </PagerTemplate> <SelectedRowStyle BackColor="#738A9C" ForeColor="#F7F7F7" Font-Bold= "True" /> <PagerStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" HorizontalAlign= "Right" /> <HeaderStyle BackColor="#4A3C8C" Font-Bold="True" ForeColor="#F7F7F7" /> <RowStyle BackColor="#E7E7FF" ForeColor="#4A3C8C" /> <AlternatingRowStyle BackColor="#F7F7F7" /> </asp:GridView> <table height="20" border="1" cellpadding="0" cellspacing="0" bordercolor light="#FFFFFF" bordercolordark="#E6E6E6" bgcolor="#FFFFFF" style="width: 99%"> <tr><td> <asp:Button ID="Button1" runat="server" Text="全選" OnClick="Button1_ Click" /> <asp:Button ID="Button2" runat="server" Text="刪除" OnClick="Button2_ Click" /></td> <td align=right> <asp:LinkButton ID="lnkbtnFrist" runat="server" OnClick="lnkbtnFrist_ Click">首頁</asp:LinkButton> <asp:LinkButton ID="lnkbtnPre" runat="server" OnClick="lnkbtnPre_ Click">上一頁</asp:LinkButton> <asp:Label ID="lblCurrentPage" runat="server"></asp:Label> <asp:LinkButton ID="lnkbtnNext" runat="server" OnClick="lnkbtnNext_ Click">下一頁</asp:LinkButton> <asp:LinkButton ID="lnkbtnLast" runat="server" OnClick="lnkbtnLast_ Click">尾頁</asp:LinkButton> 跳轉到第<asp:DropDownList ID="ddlCurrentPage" runat="server" AutoPostBack ="True" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged"> </asp:DropDownList>頁</td> </tr></table>
最終的顯示效果如圖1-12所示。

圖1-12
后臺代碼如下部分:
protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { DataBinds(); } } //數據綁定 void DataBinds() { this.GridView1.DataSource = GetDataFromDataBase().Tables[0].Default View; this.GridView1.DataBind(); this.ddlCurrentPage.Items.Clear(); for (int i = 1; i <= this.GridView1.PageCount; i++) { this.ddlCurrentPage.Items.Add(i.ToString()); } this.ddlCurrentPage.SelectedIndex = this.GridView1.PageIndex; } //全選操作 protected void Button1_Click(object sender, EventArgs e)
{
foreach (GridViewRow row in GridView1.Rows) { ((CheckBox)row.Cells[0].FindControl("checkAll")).Checked = true; } } //刪除所選 protected void Button2_Click(object sender, EventArgs e) { for (int rowindex = 0; rowindex < this.GridView1.Rows.Count; rowindex++) { if (((CheckBox)this.GridView1.Rows[rowindex].Cells[0].FindControl("checkAll")).Checked == true) { int id = Convert.ToInt32(this.GridView1.DataKeys[rowindex]. Value); //DeleteProduct(id); } } DataBinds(); } //索引事件 protected void GridView1_PageIndexChanging(object sender, GridView PageEvent Args e) { this.GridView1.PageIndex = e.NewPageIndex; DataBinds(); } //改變CheckBox的狀態 protected void cbAll_CheckedChanged(object sender, EventArgs e) { if (this.cbAll.Checked == true) { foreach (GridViewRow row in GridView1.Rows) { ((CheckBox)row.Cells[0].FindControl("checkAll")).Checked = true; } } else { foreach (GridViewRow row in GridView1.Rows) { ((CheckBox)row.Cells[0].FindControl("checkAll")).Checked = false; } } } //自定義導航到具體的頁 protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e) { this.GridView1.PageIndex = this.ddlCurrentPage.SelectedIndex; DataBinds(); } //首頁 protected void lnkbtnFrist_Click(object sender, EventArgs e) { this.GridView1.PageIndex = 0; DataBinds(); } //前一頁 protected void lnkbtnPre_Click(object sender, EventArgs e) { if (this.GridView1.PageIndex > 0) { this.GridView1.PageIndex = this.GridView1.PageIndex - 1; DataBinds(); } } //下一頁 protected void lnkbtnNext_Click(object sender, EventArgs e) { if (this.GridView1.PageIndex < this.GridView1.PageCount) { this.GridView1.PageIndex = this.GridView1.PageIndex + 1; DataBinds(); } } //最后一頁 protected void lnkbtnLast_Click(object sender, EventArgs e) { this.GridView1.PageIndex = this.GridView1.PageCount; DataBinds(); } protected void GridView1_DataBound(object sender, EventArgs e) { this.lblCurrentPage.Text = string.Format("當前第{0}頁/總共{1}頁", this. GridView1.PageIndex + 1, this.GridView1.PageCount); } //數據源 public DataSet GetDataFromDataBase() { string getConnetionString = string.Empty; getConnetionString Configuration.ConfigurationManager.AppSettings ["ConnectionString"].ToString(); string sql = "select ProductID,ProductName,UnitPrice, Discontinued from dbo.Products"; SqlConnection conn = new SqlConnection(); conn.ConnectionString = getConnetionString; SqlDataAdapter adp = new SqlDataAdapter(sql, conn); DataSet ds = new DataSet(); adp.Fill(ds); return ds; }
數據庫配置文件如下:
<appSettings> <add key="ConnectionString" value="Server=; user id=sa; pwd=123456; database= Northwind"/> </appSettings>
最后整個程序顯示效果如圖1-13所示。

圖1-13
對于DetailsView控件,它的作用在于在表格中顯示數據源的單個記錄,此表格中每個數據行表示記錄中的一個字段,可以從它的關聯數據源中一次顯示、編輯、插入或刪除一條記錄。默認情況下,會將記錄的每個字段顯示在它自己的一行內。DetailsView控件通常用于更新和插入新記錄,并且通常在主/詳細方案中使用,在這些方案中,主控件(最常見的是與GridView控件一起使用)的選中記錄決定要在DetailsView控件中顯示的記錄。即使DetailsView控件的數據源公開了多條記錄,該控件一次也僅顯示一條數據記錄。此控件依賴于數據源控件的功能執行諸如更新、插入和刪除記錄等任務。但是此控件不支持排序。可以自動對其關聯數據源中的數據進行分頁,但前提是數據由支持ICollection接口的對象表示或基礎數據源支持分頁。DetailsView控件提供用于在數據記錄之間導航的用戶界面。若要啟用分頁行為,則需要將AllowPaging屬性設置為true。從關聯的數據源選擇特定的記錄時,可以通過分頁到該記錄進行選擇。由該控件顯示的記錄是當前選擇的記錄。
和GridView控件一樣,DetailsView控件也有兩種數據綁定方式:使用DataSourceID屬性進行數據綁定;使用DataSource屬性進行數據綁定。但要注意的是,當使用DataSourceID屬性綁定到數據源時,DetailsView控件支持雙向數據綁定。除可以使該控件顯示數據之外,還可以使它自動支持對綁定數據的插入、更新和刪除操作。此時若要使DetailsView控件支持編輯操作,綁定數據源必須支持對數據的更新操作,需要同時滿足才能達到此目的。
1.如何在DetailsView控件中進行分頁
如果DetailsView控件被綁定到某個數據源控件或任何實現了ICollection接口的數據結構(包括數據集),則此控件將從數據源獲取所有記錄,顯示當前頁的記錄,并丟棄其余的記錄。當用戶移到另一頁時,控件會重復此過程,顯示另一條記錄。對于有些數據源(如ObjectDataSource控件)提供更高級的分頁功能。DetailsView控件在分頁時可以利用這些數據源的更加高級的功能,從而獲得更好的性能和更大的靈活性。同樣如果數據源未實現ICollection接口,DetailsView控件將無法分頁。例如如果使用SqlDataSource控件,并將其DataSourceMode屬性設置為DataReader,則DetailsView控件是無法實現分頁功能的。
下面演示一個配置為提供分頁的DetailsView控件:
<h3>DetailsView Example Demo</h3> <table cellspacing="10"> <tr> <td valign="top"> <asp:DetailsView ID="EmployeesDetailsView" DataSourceID="EmployeesSqlDataSource" AutoGenerateRows="False" AllowPaging="True" DataKeyNames="EmployeeID" runat="server"> <HeaderStyle forecolor="White" backcolor="Blue" /> <Fields> <asp:BoundField Datafield="EmployeeID" HeaderText="Employee ID" ReadOnly="True"/> <asp:BoundField Datafield="Title" HeaderText="Title"/> <asp:BoundField Datafield="BirthDate" HeaderText="BirthDay"/> </Fields> <PagerSettings Mode="NextPreviousFirstLast" FirstPageText="<<" LastPageText=">>" PageButtonCount="1" Position="Top"/> </asp:DetailsView> </td> </tr> </table> <asp:SqlDataSource ID="EmployeesSqlDataSource" SelectCommand="SELECT [EmployeeID], [Title], [BirthDate] FROM [Employees]" connectionstring="<%$ ConnectionStrings:NorthwindConnectionString %>" RunAt="server"/>
2.使用DetailsView控件修改數據
通過將AutoGenerateEditButton、AutoGenerateInsertButton和AutoGenerateDeleteButton屬性中的一個或多個設置為true,就可以啟用DetailsView控件的內置編輯功能。DetailsView控件將自動添加此功能,使用戶能夠編輯或刪除當前綁定的記錄以及插入新記錄,但前提是DetailsView控件的數據源支持編輯。DetailsView控件需要提供一個用戶界面,使用戶能夠修改綁定記錄的內容。通常在一個可編輯視圖中會顯示一個附加行,其中包含“編輯”、“插入”和“刪除”命令按鈕。默認情況下,這一行會添加到DetailsView控件的底部。當用戶單擊某個命令按鈕時,DetailsView控件會重新顯示該行,并在其中顯示可讓用戶修改該行內容的控件。編輯按鈕將被替換為可讓用戶保存更改或取消編輯行的按鈕。
DetailsView控件使用文本框顯示BoundField中的數據,以及那些在將AutoGenerateRows屬性設置為true時自動顯示的數據。布爾型數據使用復選框顯示。通過使用TemplateField,當然也可以自定義在編輯模式中顯示的輸入控件。當DetailsView控件執行插入操作時,將使用Values字典集合傳遞要插入到數據源中的值。對于更新或刪除操作,DetailsView控件使用這三個字典集合將值傳遞到數據源:Keys字典、NewValues字典和OldValues字典。讀者可以使用傳遞到插入、更新或刪除事件的事件參數訪問各個字典,這些事件由DetailsView控件引發。
Keys字典中包含一些字段的名稱和值,這些字段唯一地標識了包含要更新或刪除的記錄,此外該字典總是包含編輯記錄之前鍵字段的原始值。若要指定哪些字段放置在Keys字典中,可將DataKeyNames屬性設置為表示數據的主鍵的字段名稱列表,各字段名稱之間用逗號分隔。Keys集合中會自動填充與在DataKeyNames屬性中指定的字段關聯的值。Values和NewValues字典分別包含所插入或編輯的記錄中的輸入控件的當前值。OldValues字典包含除鍵字段以外的任何字段的原始值,鍵字段包含在Keys字典中。鍵字段的新值包含在NewValues字典中。
下面演示一個使用GridView控件和DetailsView控件顯示數據,并將DetailsView控件配置為允許修改數據:
<h3>Northwind Employees</h3> <table cellspacing="10"> <tr> <td valign="top"> <asp:DropDownList ID="EmployeesDropDownList" DataSourceID="EmployeesSqlDataSource" DataValueField="EmployeeID" DataTextField="FullName" AutoPostBack="True" Size="10" OnSelectedIndexChanged="EmployeesDropDownList_OnSelectedIndex Changed" RunAt="Server" /> </td> <td valign="top"> <asp:DetailsView ID="EmployeeDetailsView" DataSourceID="EmployeeDetailsSqlDataSource" AutoGenerateRows="False" AutoGenerateInsertbutton="True" AutoGenerateEditbutton="True" AutoGenerateDeletebutton="True" DataKeyNames="EmployeeID" OnItemUpdated="EmployeeDetailsView_ItemUpdated" OnItemDeleted="EmployeeDetailsView_ItemDeleted" RunAt="server"> <HeaderStyle backcolor="Navy" forecolor="White"/> <RowStyle backcolor="White"/> <AlternatingRowStyle backcolor="LightGray"/> <EditRowStyle backcolor="LightCyan"/> <Fields> <asp:BoundField DataField="EmployeeID" HeaderText="Employee ID" InsertVisible="False" ReadOnly="True"/> <asp:BoundField DataField="FirstName" HeaderText="First Name"/> <asp:BoundField DataField="LastName" HeaderText="Last Name"/> <asp:BoundField DataField="Address" HeaderText="Address"/> <asp:BoundField DataField="City" HeaderText="City"/> <asp:BoundField DataField="Region" HeaderText="Region"/> <asp:BoundField DataField="PostalCode" HeaderText="Postal Code"/> </Fields> </asp:DetailsView> </td> </tr> </table> <asp:SqlDataSource ID="EmployeesSqlDataSource" SelectCommand="SELECT EmployeeID, LastName + ', ' + FirstName AS FullName FROM Employees" Connectionstring="<%$ ConnectionStrings:NorthwindConnectionString %>" RunAt="server"> </asp:SqlDataSource> <asp:SqlDataSource ID="EmployeeDetailsSqlDataSource" SelectCommand="SELECT EmployeeID, LastName, FirstName, Address, City, Region, PostalCode FROM Employees WHERE EmployeeID = @EmpID" InsertCommand="INSERT INTO Employees(LastName, FirstName, Address, City, Region, PostalCode) VALUES (@LastName, @FirstName, @Address, @City, @Region, @Postal Code); SELECT @EmpID = SCOPE_IDENTITY()" UpdateCommand="UPDATE Employees SET LastName=@LastName, FirstName= @FirstName, Address=@Address,City=@City, Region=@Region, PostalCode=@PostalCode WHERE EmployeeID=@EmployeeID" DeleteCommand="DELETE Employees WHERE EmployeeID=@EmployeeID" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" OnInserted="EmployeeDetailsSqlDataSource_OnInserted" RunAt="server"> <SelectParameters> <asp:ControlParameter ControlID="EmployeesDropDownList" Property Name="SelectedValue" Name="EmpID" Type="Int32" DefaultValue="0" /> </SelectParameters> <InsertParameters> <asp:Parameter Name="LastName" Type="String" /> <asp:Parameter Name="FirstName" Type="String" /> <asp:Parameter Name="Address" Type="String" /> <asp:Parameter Name="City" Type="String" /> <asp:Parameter Name="Region" Type="String" /> <asp:Parameter Name="PostalCode" Type="String" /> <asp:Parameter Name="EmpID" /> </InsertParameters> <UpdateParameters> <asp:Parameter Name="LastName" Type="String" /> <asp:Parameter Name="FirstName" Type="String" /> <asp:Parameter Name="Address" Type="String" /> <asp:Parameter Name="City" Type="String" /> <asp:Parameter Name="Region" Type="String" /> <asp:Parameter Name="PostalCode" Type="String" /> <asp:Parameter Name="EmployeeID" Type="Int32" DefaultValue="0" /> </UpdateParameters> <DeleteParameters> <asp:Parameter Name="EmployeeID" Type="Int32" DefaultValue="0" /> </DeleteParameters> </asp:SqlDataSource>
后臺代碼如下:
public void EmployeesDropDownList_OnSelectedIndexChanged(Object sender, EventArgs e) { EmployeeDetailsView.DataBind(); } public void EmployeeDetailsView_ItemUpdated(Object sender, DetailsView UpdatedEventArgs e) { EmployeesDropDownList.DataBind(); EmployeesDropDownList.SelectedValue = e.Keys["EmployeeID"].ToString(); EmployeeDetailsView.DataBind(); } public void EmployeeDetailsView_ItemDeleted(Object sender, DetailsView DeletedEventArgs e) { EmployeesDropDownList.DataBind(); } public void EmployeeDetailsSqlDataSource_OnInserted(Object sender, SqlData SourceStatusEventArgs e) { System.Data.Common.DbCommand command = e.Command; EmployeesDropDownList.DataBind(); EmployeesDropDownList.SelectedValue = command.Parameters["@EmpID"].Value.ToString(); EmployeeDetailsView.DataBind(); }
運行效果如圖1-14所示。

圖1-14
要注意GridView和DetailsView控件中用于定義字段類型的元素。這些元素實際上等效于DataGrid控件中的元素。表1-4列出了受支持的字段類型。特別重要的是ImageField和DropDownListField,它們都可以有效地削減目前開發人員為在DataGrid中包含圖像和數據綁定下拉列表而編寫的大部分代碼。
表1-4 GridView和DetailsView字段類型
