使用 ASP+ DataGrid 控件来创建主视图/详细资料视图
Microsoft Corporation
2000年8月
簡介
Microsoft? Visual Studio.NET 的下一發行版包括 DataGrid Web 控件 (作為服務器控件的 Active Server Page+ (ASP+) 套件的一部分)。 該控件提供用以根據數據源的內容來表示 HTML 的功能。
DataGrid 控件可以用于若干個只讀匯報情形。該控件設計用于對豐富而完全可定制的數據表格布局的輸出進行簡化。還提供多個機制,用于通過超級鏈接及其對選擇、排序、分頁和原地編輯和其它特性的支持,為輸出添加交互性。這使得該控件在若干的常見 Web 應用方案中很有用,諸如列表、購物車和查詢結果。
DataGrid 還提供一些功能,這些功能具有 ASP+ 架構所特有的所有服務器控件的特點。該控件包含進行與瀏覽器無關的輸出所需的邏輯,同時提供了一個統一的編程模型,從而能夠處理回傳數據,以及對請求之間的狀態進行管理。這樣,開發商就可以針對帶有屬性、方法和事件的對象模型進行編程,而不必處理直接用 HTML 編程所帶來的不一致性和復雜性。
使用 ASP+ 列表綁定控件 介紹 DataGrid 控件以及相關的 DataList 和 Repeater 控件。還介紹了數據綁定、模板和格式化等等多個關鍵的概念。該文以此為基礎寫成,并將重點放在 DataGrid 控件,以揭示如何在您自己的應用程序中利用該控件所提供的功能。
我們要建立什么? 
該文舉出了一序列的示例頁面,彼此結合,從而最終生成一個頁面,該頁面以示例數據庫的 Authors 表和 Titles 表為依據,提供主/詳細資料視圖(該數據庫隨 Microsoft SQL Server?2000 一起發運)。序列中的每個頁面均介紹 DataGrid 控件的一個新的特性或功能。下列圖象是從 pubs 數據庫抽取出來的。
主/詳細資料視圖類似于 Microsoft Access 所介紹的窗體/子窗體概念。也類似于隨 Microsoft Visual InterDev?6.0 一起發運的 DataForm Wizard (數據窗體向導)。主/詳細資料視圖顯示一到多的關系結果,其中視圖的一個部分顯示第一個查詢或主查詢的結果。然后跟蹤一個選擇,以篩選所使用的第二個查詢的結果,從而在視圖的另一部分顯示選擇內容的詳細資料。
圖 1. 完成的頁面
圖 1 將 Author 列表顯示在頁面的上半部分,并將關于所選作者的詳細資料(包括相關書名)顯示在下半部分。 Authors 列表和 Titles 均是用 DataGrid 控件加以表示的。 顯示作者的 DataGrid 舉例說明如何進行選擇、排序、和分頁。顯示書名的 DataGrid 演示如何進行原地編輯、格式化和定制列。
數據訪問
為了使示例自成一體,從 SQL Server 抽取數據并將該數據連同其架構信息一同保留為一個 XML 文件 TitlesDB.xml。下面是該文件的一個片斷。
<root>
<schema id="DocumentElement" targetNamespace=""xmlns="http://www.w3.org/1999/XMLSchema"xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"><element name="Author"><complexType content="elementOnly"><element name="au_id" type="string" minOccurs="1"maxOccurs="1"></element><element name="au_name" type="string" minOccurs="1"maxOccurs="1"></element><element name="address" type="string" minOccurs="0"maxOccurs="1"></element><element name="city" type="string" minOccurs="0"maxOccurs="1"></element><element name="state" type="string" minOccurs="0"maxOccurs="1"></element><element name="zip" type="string" minOccurs="0"maxOccurs="1"></element><element name="phone" type="string" minOccurs="0"maxOccurs="1"></element></complexType><unique name="AuthorConstraint" msdata:PrimaryKey="True"><selector>.</selector><field>au_id</field></unique></element><element name="Title"><complexType content="elementOnly"><element name="title_id" type="string" minOccurs="1"maxOccurs="1"></element><element name="au_id" type="string" minOccurs="1"maxOccurs="1"></element><element name="title" type="string" minOccurs="1"maxOccurs="1"></element><element name="price" msdata:DataType="System.Currency"type="string"minOccurs="1" maxOccurs="1"></element><element name="pubdate" type="timeInstant" minOccurs="1"maxOccurs="1"></element></complexType><unique name="TitleConstraint" msdata:PrimaryKey="True"><selector>.</selector><field>title_id</field></unique><key name="AuthorTitle"><selector>../Author</selector><field>au_id</field></key><keyref refer="AuthorTitle"><selector>.</selector><field>au_id</field></keyref></element>
</schema>
<DocumentElement><Author><au_id>154-00-1300</au_id><au_name>John Doe</au_name><phone>425 705 1234</phone><address>One Microsoft Way</address><city>Redmond</city><state>CA</state><zip>98005</zip></Author><Title><title_id>BU1032</title_id><au_id>213-46-8915</au_id><title>The Busy Executive's Database Guide</title><price>19.99</price><pubdate>1991-06-12T07:00:00</pubdate></Title>
</DocumentElement>
</root>
這些樣例簡化了數據訪問,從而將重點全部放在 DataGrid 的使用上。上面的 XML 被加載進一個 DataSet。 DataSet 為數據提供高速緩存,從而可以進行篩選、排序和編輯等等各種操作。下面的代碼來自 Global.asax,用于加載 DataSet 和將其保存為 Session 狀態。
public void Session_OnStart() {// 將樣例中所用的數據載入會話范圍的 DataSet.FileStream fs = null;DataSet ds = null;try {fs = new FileStream(Server.MapPath("Data//TitlesDB.xml"),FileMode.Open, FileAccess.Read);ds = new DataSet();ds.ReadXml(fs);} finally {if (fs != null) {fs.Close();fs = null;}}Session["AppData"] = ds;
}
在實際的 Web 應用程序中,通常不是使用處于 Session 或 Application 狀態的高速緩存數據,而是通過所存儲的過程、中間層業務對象,或通過調用 Web 服務所揭示的方法來訪問和修改數據。無論采取怎樣的手段來訪問數據,您會發現你依舊以同樣的方式來編程和與控件的對象模型進行進行交互。 www.IBuySpy.com (英文) 網站就是演示這些概念的一個很好的應用示例。
第 1 步: 一個基本的 DataGrid 
序列的第一步展示了一個頁面,其中包含單獨一個 DataGrid 控件,用于顯示來自數據源的一個只讀作者列表。結果類似于 使用 ASP+ 列表綁定控件 (英文) 所取得的效果。
圖 2. 完成第 1 步后的頁面
DataGrid 聲明來自:
Step1.aspx:
<asp:DataGrid id="authorsGrid" runat="server"AutoGenerateColumns="false"BackColor="White"BorderWidth="1px" BorderStyle="Solid" BorderColor="Tan"CellPadding="2" CellSpacing="0"Font-Name="Verdana" Font-Size="8pt"><property name="Columns"><asp:BoundColumn HeaderText="ID" DataField="au_id"><property name="HeaderStyle"><asp:TableItemStyle Width="100px"/></property></asp:BoundColumn><asp:BoundColumn HeaderText="Name" DataField="au_name"><property name="HeaderStyle"><asp:TableItemStyle Width="150px"/></property></asp:BoundColumn><asp:BoundColumn HeaderText="State" DataField="state"><property name="HeaderStyle"><asp:TableItemStyle Width="50px"/></property></asp:BoundColumn></property><property name="HeaderStyle"><asp:TableItemStyle BackColor="DarkRed" ForeColor="White"Font-Bold="true"/></property><property name="ItemStyle"><asp:TableItemStyle ForeColor="DarkSlateBlue"/></property><property name="AlternatingItemstyle"><asp:TableItemStyle BackColor="Beige"/></property></asp:DataGrid>
上面的代碼展示 DataGrid,該控件的各種屬性已經過聲明設定。 DataGrid 控件與其它 Web 控件如 Font、BackColor、ForeColor 和 BorderWidth 共享一組公用的樣式屬性。另外, DataGrid 提供僅適用于表的屬性如 CellPadding。最后, DataGrid 提供附加的樣式屬性,這些樣式屬性影響其中各項目和列如 HeaderStyle、ItemStyle 和 AlternatingItemStyle 的表示。這些樣式屬性用于創建豐富多采且極富魅力的數據視覺效果。
DataGrid 支持從其所綁定的數據源自動生成列的功能。但是,在本例中, AutoGenerateColumns 屬性已被設定為 false。因此必須借助要展示的列集對 Columns 集合進行初始化。從而可以更多地控制表現效果,諸如列的次序和標頭以及與每列對應的樣式。這一步中所定義的列均為 BoundColumns,從而可以通過其 DataField 屬性,綁定到數據源的單獨一個字段。您在以后步驟中可以看到, DataGrid 允許選擇各種各樣類型的列。
下面的類包含支持本頁面的代碼。
Step1Page.cs:
namespace Samples {...public class Step1Page : Page {protected DataGrid authorsGrid;// 檢索存入會話狀態的應用程序數據private DataSet GetSessionData() {return (DataSet)Session["AppData"];}// 檢索 Authors 表private ICollection GetAuthors() {DataSet ds = GetSessionData();DataView dv = ds.Tables["Author"].DefaultView;dv.RowFilter = String.Empty;return dv;}// 將 Authors 表載入 DataGridprivate void LoadAuthorsGrid() {ICollection authors = GetAuthors();authorsGrid.DataSource = authors;authorsGrid.DataBind();}// 超控 OnLoad,以在第一次請求過程中加載數據protected override void OnLoad(EventArgs e) {base.OnLoad(e);if (!IsPostBack) {LoadAuthorsGrid();}}}
}
該類超越 Page 的 OnLoad 方法 (類似于實施 Page_Load),將 Author 列表作為 DataGrid 數據源進行加載。與其它服務器控件一樣, DataGrid 實施顯式的數據綁定模型,其中控件只在 DataBind 方法被調用時才列舉器數據源中的值。這樣設計各個控件,您就可以完全控制在哪個時間點需要數據源,并將無需數據時的數據訪問降至最低,例如大多數的往返過程。這一點會在將來的步驟中得到證明。
代碼還展示從 Session 狀態檢索的數據,數據是在會話啟動過程中存為該狀態的。
第 2 步: 帶有多頁面的主/詳細資料視圖 
創建主/詳細資料視圖的一個方法就是使用多頁面。在該方法中,主查詢中的選定值的詳細資料視圖被顯示在次要頁面上。在關于選定內容的信息中,該次要頁面作為 URL 請求中的一個參數得到傳遞。
圖 3. 第 2 后的頁面,展示詳細資料頁面的浮動視圖
Step2.aspx 包含對 DataGrid 的更改,以使其包含一個名為 DataGrid 聲明的列。
Step2.aspx:
<asp:DataGrid id="authorsGrid" runat="server"AutoGenerateColumns="false"BackColor="White"BorderWidth="1px" BorderStyle="Solid" BorderColor="Tan"CellPadding="2" CellSpacing="0"Font-Name="Verdana" Font-Size="8pt"><property name="Columns"><asp:BoundColumn HeaderText="ID" DataField="au_id"><property name="HeaderStyle"><asp:TableItemStyle Width="100px"/></property></asp:BoundColumn><asp:BoundColumn HeaderText="Name" DataField="au_name"><property name="HeaderStyle"><asp:TableItemStyle Width="150px"/></property></asp:BoundColumn><asp:BoundColumn HeaderText="State" DataField="state"><property name="HeaderStyle"><asp:TableItemStyle Width="50px"/></property></asp:BoundColumn>
   <asp:HyperLinkColumn Text="Details" DataNavigateUrlField="au_id"
        DataNavigateUrlFormatString="Step2a.aspx?AuthorID={0}"/></property><property name="HeaderStyle"><asp:TableItemStyle BackColor="DarkRed" ForeColor="White"Font-Bold="true"/></property><property name="ItemStyle"><asp:TableItemStyle ForeColor="DarkSlateBlue"/></property><property name="AlternatingItemstyle"><asp:TableItemStyle BackColor="Beige"/></property></asp:DataGrid>
DataGrid 聲明與第 1 步中的 DataGrid 聲明幾乎一樣。已將單獨一個 HyperLinkColumn 添加到 Columns 集合。 HyperLinkColumn 用來在 DataGrid 的每行中創建一個可導航的鏈接。該類型的列允許其 Text 和 NavigateUrl 屬性與數據綁定。這樣, Text 屬性是靜態的,而 NavigateUrl 是數據綁定到作者 ID 的(通過設定 DataNavigateUrl 屬性)。另外,指定 DataNavigateUrlFormatString 建立一個以 Author ID 為參數的指向詳細資料頁面的 URL。 因此,每行均包含一個帶有“詳細資料”超級鏈接的附加列,而該鏈接的 URL 以與該行關聯的數據為依據。
HyperLinkColumn 代表著向本來只讀的數據顯示添加交互功能的第一步。
支持該頁面 (Step2Page.cs) 的代碼與第 1 步相同。添加這一列并不添加任何代碼,也不導致任何代碼發生變化,因此此處沒有列出那些代碼。
Step2a.aspx 實施詳細資料頁面,以顯示某一具體作者的詳細資料。
Step2a.aspx:
<asp:Panel id="detailsPanel" runat="server">
<table border="0" cellspacing="0" cellpadding="2" width="100%"style="font-family: verdana; font-size: 8pt"><tr><td width="200"><b>Name:</b></td><td width="100%"><%# DataBinder.Eval(CurrentAuthor, "au_name") %></td></tr><tr><td width="200"><b>ID:</b></td><td width="100%"><%# DataBinder.Eval(CurrentAuthor, "au_id") %></td></tr><tr><td width="200" valign="top"><b>Address:</b></td><td width="100%"><%# DataBinder.Eval(CurrentAuthor, "address") %><br><%# DataBinder.Eval(CurrentAuthor, "city") %>,<%# DataBinder.Eval(CurrentAuthor, "state") %><%# DataBinder.Eval(CurrentAuthor, "zip") %></td></tr><tr><td width="200"><b>Phone:<b></td><td width="100%"><%# DataBinder.Eval(CurrentAuthor, "phone") %></td></tr><tr><td colspan="2"><b>Title(s) Authored</b></td></tr><tr><td colspan="2"><asp:DataGrid id="titlesGrid" runat="server"DataSource='<%# DataBinder.Eval(CurrentAuthor, "AuthorTitle") %>'AutoGenerateColumns="false"ShowFooter="true"BackColor="White"BorderWidth="1px" BorderStyle="Solid" BorderColor="Tan"CellPadding="2" CellSpacing="0"Font-Name="Verdana" Font-Size="8pt"OnItemCreated="OnItemCreatedTitlesGrid"><property name="Columns"><asp:BoundColumn HeaderText="ID" DataField="title_id"><property name="HeaderStyle"><asp:TableItemStyle Width="100px"/></property></asp:BoundColumn><asp:BoundColumn HeaderText="Title" DataField="title"><property name="HeaderStyle"><asp:TableItemStyle Width="250px"/></property></asp:BoundColumn><asp:BoundColumn HeaderText="Published" DataField="pubdate"DataFormatString="{0:MMM yyyy}"><property name="HeaderStyle"><asp:TableItemStyle Width="100px"/></property></asp:BoundColumn><asp:BoundColumn HeaderText="Price" DataField="price"DataFormatString="{0:c}"><property name="HeaderStyle"><asp:TableItemStyle Width="50px"/></property><property name="ItemStyle"><asp:TableItemStyle HorizontalAlign="Right"/></property></asp:BoundColumn></property><property name="HeaderStyle"><asp:TableItemStyle BackColor="DarkRed" ForeColor="White"Font-Bold="true"/></property><property name="FooterStyle"><asp:TableItemStyle BackColor="Tan"/></property><property name="ItemStyle"><asp:TableItemStyle ForeColor="DarkSlateBlue"/></property><property name="AlternatingItemstyle"><asp:TableItemStyle BackColor="Beige"/></property></asp:DataGrid></td></tr>
</table>
</asp:Panel>
本頁包含若干使用 DataBinder.Eval 的數據綁定表達式。這些表達式抽取本頁代碼中所實施的 CurrentAuthor 屬性的各個屬性。
DataGrid 用于顯示選定作者所編著的書名。與第 1 步相同, DataGrid 包含一個針對要顯示的列的定義,以及用于為列、行和總體控制提供可視格式化的樣式屬性設置。
BoundColumns 還允許對數據進行格式化。正如在上面聲明中看到的那樣, DataFormatString 屬性用于對日期和貨幣值進行格式化。格式化對于表示本地化文字和控制非字符串類型的文字呈現來講極其有用。
Step2aPage.cs 包含支持詳細資料頁面的代碼。
Step2aPage.cs:
namespace Samples {...public class Step2aPage : Page {private object currentAuthor;// 返回當前選定的 Author public object CurrentAuthor {get {return currentAuthor;}}// 檢索存入會話狀態的應用程序數據private DataSet GetSessionData() {return (DataSet)Session["AppData"];}// 處理 ItemCreated 事件,以將標尾定制為// 顯示總結信息protected void OnItemCreatedTitlesGrid(object sender,DataGridItemCreatedEventArgs e) {if (e.Item.ItemType == ListItemType.Footer) {int cellCount = e.Item.Cells.Count;for (int i = 0; i < cellCount - 1; i++) {e.Item.Cells.RemoveAt(0);}int itemCount = titlesGrid.Items.Count;string itemCountString;if (itemCount == 0) {itemCountString = "No Titles Found";}else {itemCountString = Int32.ToString(itemCount) +" title(s)";}TableCell summaryCell = e.Item.Cells[0];summaryCell.Text = itemCountString;summaryCell.ColumnSpan = cellCount;summaryCell.HorizontalAlign = HorizontalAlign.Right;}}// 超控 OnLoad,以載入選定作者的詳細資料protected override void OnLoad(EventArgs e) {base.OnLoad(e);string authorID = Request.QueryString["AuthorID"];if (authorID != null) {SelectAuthor(authorID);detailsPanel.DataBind();}}// 根據給定作者 ID 來設置 CurrentAuthor 對象private void SelectAuthor(string authorID) {DataSet ds = GetSessionData();DataView dv = ds.Tables["Author"].DefaultView;dv.RowFilter = "au_id = '" + authorID + "'";currentAuthor = dv[0];}}
}
通過訪問 Request.QueryString 集合,類就超越頁面的 OnLoad 方法來檢索作為 URL 請求中的參數傳來的 Author ID。然后使用該 Author ID 來查詢數據源和設置 CurrentAuthor 屬性。最后,調用 DataBind,從而為所有的數據綁定的表達式求值。
另外,該頁面為 DataGrid 控件的 ItemCreated 事件實施了一個事件處理器。 ItemCreated 事件是 DataGrid 為其高級用途提供的擴展機制之一。該事件允許從行的控件結構內部添加和刪除控件,以及在特殊情況下將其添加到特定行。
DataGrid 每次創建一個項目(或行)時,就引發該事件。這具體有兩種情況:
- 要對控件進行數據綁定,且項目需要從頭創建,則在將項目進行數據綁定之前,引發該事件。
 
- 當要從往返過程中的保存狀態創建項目時,則在將所保存的狀態載入項目及其包含的控件之前引發該事件。
結果是,該事件提供一個掛鉤,用于對項目的現有控件結構進行更改。
在本例中,處理器修改控件的標尾,以顯示所列書名的的一個計數。標尾所包含的列的數目與其它行一樣。摘要需要橫跨整個 DataGrid。因此,處理器僅保留行中的一個單元格,將其余全部刪除,并將僅剩的單元格的 ColumnSpan 重置,以使其橫跨整個列集,設定其 HorizontalAlign 屬性,以使文字右對齊,最后設定其 Text,指示計數。
處理該事件時,只有一條規則需要遵守:您必須進行同樣的結構轉換,無論調用該項目的上下文如何,無論是在數據綁定過程中,還是在往返過程中。
第 3 步: 單頁面中的主/詳細資料視圖 
創建主/詳細資料視圖的另一備選且更常用的方法就是用單頁面顯示這些視圖。
圖 4. 完成第 3 步后的頁面
Authors DataGrid 來自:
Step3.aspx:
<asp:DataGrid id="authorsGrid" runat="server"...
    DataKeyField="au_id"
    OnSelectedIndexChanged="OnSelIndexChangedAuthorsGrid"><property name="Columns">
    <asp:ButtonColumn Text="Select" Command="Select"/>...</property>...    <property name="SelectedItemStyle">
    <asp:TableItemStyle BackColor="PaleGoldenRod" Font-Bold="true"/>
  </property>
</asp:DataGrid> 
與前一步相比,該聲明中有三處變化。
首先,在第 2 步中添加的 HyperLinkColumn 已被刪除。這不再需要,因為整個視圖是在單頁面中實施的。
其次,向列集添加了一個新的列類型 ButtonColumn。該列在每行中生成 LinkButton,用于提交該頁,而不是從該頁進行瀏覽。列的 Command 屬性設定為 Select,對相應的 LinkButton 屬性進行設定。 DataGrid 將 Select 作為一個標準命令,將包含被單擊按鈕的列選定。
當選定內容發生變化時, DataGrid 就引發 SelectedIndexChanged 事件,該事件在代碼中得到處理。 DataKeyField 屬性頁得到設定,從而導致 DataKeys 集合填置與數據源中的每個項目相對應的值。
該頁面還包含針對題為 detailsPanel 的面板內的詳細資料節的 UI 和布局。這是從第 2 步原樣復制的。
Step3Page.cs 表示這一步中的 .aspx 頁面的支持代碼。其中包括來自第 2 步中兩個有代碼支持的文件的代碼組合。來自詳細資料頁面的大部分代碼均為原樣照搬。下面顯示的代碼包含我們所作的更改和補充。
Step3Page.cs:
namespace Samples {...public class Step3Page : Page {private object currentAuthor;// 將 Authors 表載入 DataGrid// 另外還更新選定的作者private void LoadAuthorsGrid() {ICollection authors = GetAuthors();authorsGrid.DataSource = authors;if (authors.Count != 0) {authorsGrid.SelectedIndex = 0;}else {authorsGrid.SelectedIndex = -1;}authorsGrid.DataBind();
            UpdateDetails();}// 處理作者網格中的 SelectedIndexChanged 事件,以// 更新詳細資料protected void OnSelIndexChangedAuthorsGrid(object sender,EventArgs e) {UpdateDetails();}// 根據當前選定的作者來更新// 詳細資料節private void UpdateDetails() {
            UpdateSelection();            if (currentAuthor != null) {
                detailsPanel.Visible = true;
                detailsPanel.DataBind();
            }
            else {
                detailsPanel.Visible = false;
            }
        }        // 更新當前選定的作者
        private void UpdateSelection() {
            int selectedIndex = authorsGrid.SelectedIndex;            currentAuthor = null;
            if (selectedIndex != -1) {
                string authorID =
                       (string)authorsGrid.DataKeys[selectedIndex];                DataSet ds = GetSessionData();
                DataView dv = ds.Tables["Author"].DefaultView;                dv.RowFilter = "au_id = '" + authorID + "'";
                currentAuthor = dv[0];
            }
        }}
}
每次加載 Authors DataGrid 時,就初始化其 SelectedIndex 屬性。一旦得到綁定,即其 DataKeys 集合填置完畢,就通過調用 UpdateDetails,更新詳細資料節。
針對 SelectedIndexChanged 事件的處理器中的詳細資料節也得到更新。注意,此時已借助 DataBind 未被調用以來的保存狀態將 DataKeys 集合填置完畢。
UpdateDetails 方法首先調用 UpdateSelection。 UpdateSelection 使用 Authors DataGrid 的 SelectedIndex 和 DataKeys 屬性來確定選定作者的 ID 和初始化 CurrentAuthor 屬性。然后, UpdateDetails 調用 detailsPanel 控件上的 DataBind,對訪問 CurrentAuthor 的數據綁定表達式進行求值。
除了借助 SelectedIndexChanged 事件進行選擇跟蹤,還可以從本樣例實現另一關鍵概念。注意,改變選擇內容并不需要已將 Authors DataGrid 進行過數據綁定。因此,從不需要重新加載 Authors 列表,從而極大地減少了對 Authors 表的訪問。這是 ASP+ 中所實施的顯式數據綁定模型的一個關鍵益處。
第 4 步: 排序 
DataGrid 支持生成可點擊標頭的功能,此類標頭可以用于讓最終用戶對控件中所展示的數據進行排序。這一步添加了對 Authors 列表進行排序的功能。
圖 5. 完成第 4 步后頁面的屏幕快照
Authors DataGrid 來自:
Step4.aspx
<asp:DataGrid id="authorsGrid" runat="server"...
    AllowSorting="true"
    OnSortCommand="OnSortCommandAuthorsGrid"
    OnItemCreated="OnItemCreatedAuthorsGrid"><property name="Columns"><asp:ButtonColumn Text="Select" Command="Select"/><asp:BoundColumn HeaderText="ID" DataField="au_id"><property name="HeaderStyle"><asp:TableItemStyle Width="100px"/></property></asp:BoundColumn>
    <asp:BoundColumn HeaderText="Name" DataField="au_name"
        SortField="au_name"><property name="HeaderStyle"><asp:TableItemStyle Width="150px"/></property></asp:BoundColumn>
    <asp:BoundColumn HeaderText="State" DataField="state"
        SortField="state"><property name="HeaderStyle"><asp:TableItemStyle Width="75px"/></property></asp:BoundColumn></property>...
</asp:DataGrid>
通過將 AllowSorting 設定為 true,啟用排序。然后,對不支持排序的每列的 SortField 屬性進行設定。沒有設定該屬性的列不會生成可點擊標頭。最后,處理 SortCommand 事件,這會在有代碼支持的文件的上下文中加以論述。樣例還為 ItemCreated 事件添加了一個事件處理器,以在視覺上顯示列標頭中的當前排序設置。下面的代碼展示為向 Authors 列表添加排序功能而在有代碼支持的類中進行更改和補充。
Step4Page.cs:
namespace Samples {...public class Step4Page : Page {        // 返回當前的排序方向,該信息是在
        // Page 狀態中維持的
        protected bool SortAscending {
            get {
                object o = State["SortAscending"];
                if (o != null)
                    return (bool)o;
                return true;
            }
            set {
                State["SortAscending"] = value;
            }
        }        // 返回當前的排序字段,該信息是在
        // Page 狀態中維持的
        protected string SortField {
            get {
                object o = State["SortField"];
                if (o != null)
                    return (string)o;
                return "au_name";
            }
            set {
                State["SortField"] = value;
            }
        }// 檢索 Authors 表private ICollection GetAuthors() {DataSet ds = GetSessionData();DataView dv = ds.Tables["Author"].DefaultView;dv.RowFilter = String.Empty;            string sort = SortField;
            if (SortAscending == false) {
                sort += " desc";
            }
            dv.Sort = sort;return dv;}        // 處理 ItemCreated 事件,以用排序圖符對標頭
        // 進行定制
        protected void OnItemCreatedAuthorsGrid(object sender,
                                   DataGridItemCreatedEventArgs e) {
            if (e.Item.ItemType == ListItemType.Header) {
                string sortField = SortField;
                bool ascending = SortAscending;                Label sortGlyph = new Label();
                sortGlyph.Text = ascending ? " 5" : " 6";
                sortGlyph.Font.Name = "Webdings";                TableCell cell = null;
                if (sortField.Equals("au_name")) {
                    cell = e.Item.Cells[2];
                }
                else if (sortField.Equals("state")) {
                    cell = e.Item.Cells[3];
                }                if (cell != null) {
                    cell.Controls.Add(sortGlyph);
                }
            }
        }        // 處理作者網格中的 SortCommand 事件,以更新
        // 排序參數和重新加載新排序的數據
        protected void OnSortCommandAuthorsGrid(object sender,
                                    DataGridSortCommandEventArgs e) {
            string currentSortField = SortField;            SortField = e.SortField;
            if (currentSortField.Equals(e.SortField)) {
                SortAscending = !SortAscending;
            }
            else {
                SortAscending = true;
            }            LoadAuthorsGrid();
        }}
}
頁面將當前字段和排序方向作為名值對,保留在頁面的 State 屬性中,并將其作為 SortField 和 SortAscending 屬性提供出來,此時,頁面負責在往返過程之間維持這些屬性的值。
這些屬性用于 GetAuthors 函數,這里,在返回 Author 列表 之前,會根據屬性的當前值對其進行排序。
用于 SortCommand 事件的事件處理器在 DataGridSortCommandEventArgs 的一個實例中得到傳遞。該對象包含 SortField 屬性的值,而該屬性與標題被單擊的列相關聯。您可以選擇對該值進行任意處理,只要該處理在您的應用程序中合理。例如,該值可以包含單個字段的名稱,例如本樣例中的情形,也可以包含排序信息,從而允許進行多列排序。
實施該方法,對 SortField 和 SortAscending 屬性的值進行更新。最后,該方法調用 LoadAuthorsGrid,對 Authors DataGrid 進行數據綁定。此時情況是, DataGrid 的列需要重新生成,要求您設定數據源和調用 DataBind。
ItemCreated 的事件處理器類似于第 2 步中用于 Titles DataGrid 的事件處理器。此時,處理器修改標頭行的控件結構,具體方法是添加表示上升和下降的圖符,指示當前已排序的列和當前的排序方向。
這里有一個問題需要回答,DataGrid 為何不對其數據進行實際的排序。這主要有兩個原因。首先, ASP+ 的數據源是一般的 ICollection,以便您在選擇數據源時擁有最大的靈活性。數據源并不包含內置的排序語義。其次,也是更重要的一點, 在每次請求過程中,自動排序均會需要數據源的一個活動的實例。這不太適合用于顯式數據綁定結構中,因為這需要您在頁面的往返過程中訪問和加載數據。
第 5 步: 分頁 
DataGrid 提供顯示特殊行的功能,該行包含用于顯示頁碼和“下一步”/“上一步”瀏覽按鈕的 UI。這一步添加了對 Authors 列表進行分頁查看的功能。
圖 6. 完成第 5 步后頁面的屏幕快照
Authors DataGrid 來自:
Step5.aspx:
<asp:DataGrid id="authorsGrid" runat="server"...
    AllowPaging="true" PageSize="5"
    OnPageIndexChanged="OnPageIndexChangedAuthorsGrid">...  <property name="PagerStyle">
    <asp:DataGridPagerStyle BackColor="Tan" HorizontalAlign="Right"
        PageButtonCount="3" Mode="NumericPages"/>
  </property>
</asp:DataGrid>
與排序類似,您必須設定幾個屬性并實施一個事件處理器,從而啟用分頁功能。啟用分頁功能的具體步驟是,將 AllowPaging 屬性設定為 true,并將 PageSize 屬性設定為一個正整數。 PageSize 屬性確定了要在單頁面中表現的數據源的值。
該樣例還展示設定 PagerStyle 屬性,從而使您可以對分頁 UI 的視覺外觀和布局進行定制。樣例將分頁器一次展示三個頁碼按鈕,方法是將 Mode 設定為 NumericButtons,而將 PageButtonCount 屬性設定為 "3",并使其向右對其。除了這些設置,您還可以選擇“下一步”/“上一步”樣式按鈕,分頁 UI 的位置,其顏色方案,等等。
要支持分頁,就必須對 DataGrid 的 PageIndexChanged 事件進行處理,具體討論見 Step5Page.cs。
Step5Page.cs:
namespace Samples {...public class Step5Page : Page {        // 處理作者網格中的 PageIndexChanged 事件,以
        // 更新數據
        protected void OnPageIndexChangedAuthorsGrid(object sender,
                                  DataGridPageChangedEventArgs e) {
            LoadAuthorsGrid();
        }}
}
PageIndexChangedEvent 的事件處理程序只是重新加載 DataGrid,從而自動呈現數據源中所有項目的分頁視圖。事件處理器是在 DataGridPageChangedEventArgs 的一個實例中傳遞的,該實例包含關于舊的頁面索引和新的頁面索引的信息。如果根據應用程序的狀態,您確定不應更改頁面索引,則可以僅將 DataGrid 的 CurrentPageIndex 屬性設定回舊的頁面索引,跳過重新加載數據。顯式數據綁定模型使這成為可能。這里,直到對 DataBind 進行調用時 DataGrid 的內容才得到刷新。
DataGrid 支持一種備選的分頁模式,您可以通過將 AllowCustomPaging 屬性設定為 true 而啟用該模式。在該模式中,您負責向 DataGrid 告知您的數據源中的值的總數,具體方法是設定 VirtualItemCount 屬性。一旦處于該模式,您的數據源就想必只包含當前頁面的值。在該模式中,僅需從查詢檢索最少數目的值,從而進一步優化您的數據訪問。
第 6 步: 原地編輯 
DataGrid 支持對其行的原地編輯。這一步使用該功能,從而允許在 Titles DataGrid 內部進行編輯。
圖 7. 完成第 6 步后的頁面
Titles DataGrid 來自:
Step6.aspx:
<asp:DataGrid id="titlesGrid" runat="server"...
    OnEditCommand="OnEditCommandTitlesGrid"
    OnUpdateCommand="OnUpdateCommandTitlesGrid"
    OnCancelCommand="OnCancelCommandTitlesGrid"><property name="Columns">
    <asp:BoundColumn HeaderText="ID" DataField="title_id" ReadOnly="true"><property name="HeaderStyle"><asp:TableItemStyle Width="100px"/></property></asp:BoundColumn>
    <asp:BoundColumn HeaderText="Title" DataField="title" ReadOnly="true"><property name="HeaderStyle"><asp:TableItemStyle Width="250px"/></property></asp:BoundColumn>
    <asp:BoundColumn HeaderText="Published" DataField="pubdate"
        DataFormatString="{0:MMM yyyy}" ReadOnly="true"><property name="HeaderStyle"><asp:TableItemStyle Width="100px"/></property></asp:BoundColumn><asp:BoundColumn HeaderText="Price" DataField="price"DataFormatString="{0:c}"><property name="HeaderStyle"><asp:TableItemStyle Width="50px"/></property><property name="ItemStyle"><asp:TableItemStyle HorizontalAlign="Right"/></property></asp:BoundColumn>
     <asp:EditCommandColumn EditText="Edit" CancelText="Cancel"
          UpdateText="OK"/></property>...
</asp:DataGrid>
通過處理控件的 EditCommand、 UpdateCommand 和 CancelCommand 事件來達到在 DataGrid 內進行編輯的目的。
Columns 集合包含一個名為 EditCommandColumn 的新的列。該列自動創建每行右側的按鈕集。為只讀模式中的各行創建 Edit 按鈕,并為編輯模式中的各行創建 Update 和 Cancel 按鈕。列的 EditText、 CancelText 和 UpdateText 屬性用來指定按鈕的文本。注意,也可以將文本設定為 HTML 標記,從而為這些按鈕使用圖象。
最后,各種列的 ReadOnly<./font> 屬性被設定為 true。這就防止對這些列中的數據進行編輯,即使列處于編輯模式。對于本樣例,只有價格字段是可編輯的,因而所有其它 BoundColumns 均被標注為只讀。
上面聲明的事件處理器在下面展示的有代碼支持的類中得到實施。
Step6Page.cs:
namespace Samples {...public class Step6Page : Page {// 更新當前選定的作者并重新加載與選定內容對應的書名// 網格protected void LoadTitlesGrid() {UpdateSelection();titlesGrid.DataBind();}// 處理書名網格中的 CancelCommand 事件,以// 不施用更改就結束編輯protected void OnCancelCommandTitlesGrid(object sender,DataGridCommandEventArgs e) {titlesGrid.EditItemIndex = -1;LoadTitlesGrid();}// 處理書名網格中的 EditCommand 事件,以// 開始編輯某一行protected void OnEditCommandTitlesGrid(object sender,DataGridCommandEventArgs e) {titlesGrid.EditItemIndex = e.Item.ItemIndex;LoadTitlesGrid();}// 處理書名網格中的 UpdateCommand 事件,以施用// 所作的更改并結束編輯protected void OnUpdateCommandTitlesGrid(object sender,DataGridCommandEventArgs e) {TextBox priceText =(TextBox)e.Item.FindControl("Column3Control");string newPrice = priceText.Text.Substring(1);DataSet ds = GetSessionData();DataTable titlesTable = ds.Tables["Title"];string titleID =(string)titlesGrid.DataKeys[e.Item.ItemIndex];DataRow[] rows = titlesTable.Select("title_id = '" +titleID + "'");DataRow editRow = rows[0];editRow.BeginEdit();editRow["price"] = newPrice;editRow.EndEdit();editRow.AcceptChanges();titlesGrid.EditItemIndex = -1;LoadTitlesGrid();}}
}
EditCommand 事件是在單擊 Edit 按鈕時引發的。只需將 DataGrid 的 EditItemIndex 屬性設定為包含被單擊按鈕的項目的索引的屬性。要防止編輯某一個別行,不采取任何動作就返回。另外,要繼續進行編輯,就必須重新加載數據源并調用 DataBind。這通過調用 LoadTitlesGrid 方法來完成。
您必須處理的第二個事件就是 CancelCommand 事件。這是單擊 Cancel 按鈕時引發的。實施十分類似于前一處理器。如想結束編輯,則只需將 EditItemIndex 屬性重置為 ?,并重新加載數據源。如果需要維持行的編輯模式,則僅需不采取任何動作就從函數返回即可。
要處理的最后一個事件就是 UpdateCommand 事件,這是單擊 Update 按鈕時引發的。這里是實際工作發生的地方。從存在于項目中的控件抽取新的值,然后更新數據源。一旦將新的值使用完畢,就將 EditItemIndex 重置為 ?,以返回到只讀模式,并連同其數據源一起重新加載控件。對于前兩個事件,您可以不采取任何動作就從該函數返回,以保持該行的編輯模式狀態。
這一步又再次舉例說明控件中實施的顯式數據綁定模型。在這一實施情形中,只在某行的狀態從只讀模式更改為編輯模式或相反時才需要數據源。注意, DataGrid 自身并不更新其數據源。實質上,數據綁定是單向的。采用本模型的目的在于讓您擁有對數據源更新的控制。在大多數典型的應用程序中,更新需要預處理,并且是由業務對象或已存儲的過程來調用的。另外,單向數據綁定并不要求每個時間都有實時的數據源。
第 7 步: 使用模板 
DataGrid 控件通過使用 TemplateColumns,支持列內模板。可以完全自由地決定與這些列相關聯的單元格的內容。這一步使用 TemplateColumn 來增強首先在前一步中實施的編輯支持。
圖 8. 完成第 7 步后的頁面
Titles DataGrid 來自:
Step7.aspx:
<asp:DataGrid id="titlesGrid" runat="server"...><property name="Columns">...
    <asp:TemplateColumn HeaderText="Price">
      <template name="ItemTemplate">
        <asp:Label runat="server"
            Text='<%# DataBinder.Eval(Container.DataItem, "price",
                                      "{0:c}") %>'/>
      </template><template name="EditItemTemplate"><asp:RequiredFieldValidator runat="server"ControlToValidate="priceText" Display="Dynamic"><img src="Error.gif" height="16" width="16" title="Required"align="absmiddle"></asp:RequiredFieldValidator><asp:RegularExpressionValidator runat="server"ControlToValidate="priceText" Display="Dynamic"ValidationExpression="/$[0-9]+(/.[0-9][0-9]?)?"><img src="Error.gif" height="16" width="16"title="Invalid currency value"align="absmiddle"></asp:RegularExpressionValidator><asp:TextBox id="priceText" runat="server"Text='<%# DataBinder.Eval(Container.DataItem, "price","{0:c}") %>'Width="45px" BorderStyle="Solid" BorderWidth="1px"BorderColor="Black"></asp:TextBox></template><property name="HeaderStyle"><asp:TableItemStyle Width="50px"/></property><property name="ItemStyle"><asp:TableItemStyle HorizontalAlign="Right"/></property></asp:TemplateColumn></property>...
</asp:DataGrid>
控件的聲明展示將 TemplateColumn 添加到 Columns 集合,以替代價格字段的 BoundColumn。 TemplateColumns 是 DataGrid 的另一擴展機制。可以將其用于對 DataGrid 所創建的表格式布局內所表現的 UI 進行完全定制。 TemplateColumn 還提供模板屬性,比如 ItemTemplate 和 EditItemTemplate,從而您可以指定應當用于與列相關聯的單元格內的控件。
在本例中, ItemTemplate 包含一個 Label 控件,其 Text 屬性綁定到價格字段。這實質上在模仿 BoundColumn 的功能。
有意思的是, EditItemTemplate 模板用于處于編輯模式中的單元格。樣例將一個 TextBox 放入已綁定到價格字段的模板中。這樣就提供了與 BoundColumn 相同的功能。
模板包含各種各樣的增強性能。首先,該模板允許對 TextBox 進行更大的控制,從而您可以創建在視覺上更有魅力的編輯 UI。其次,允許您放置驗證控件,從而可以進行原地驗證。在本樣例中, RequiredFieldValidator 確保 TextBox 總是包含一個值,而 RegularExpressionValidator 確保文本包含一個有效的貨幣值。該功能的最令人興奮的一個方面就是自動進行客戶機端驗證。 ASP+ 驗證控件自動進行客戶機端驗證,并開啟功能豐富的瀏覽器客戶機上的錯誤指示器,從而無須往返過程和回落到為下級客戶機進行服務器端驗證。
Step7Page.cs:
namespace Samples {...public class Step7Page : Page {// 處理書名網格中的 UpdateCommand 事件,以施用// 所作的更改并結束編輯 (當頁面處于有效狀態時)protected void OnUpdateCommandTitlesGrid(object sender,DataGridCommandEventArgs e) {
            if (IsValid) {TextBox priceText =(TextBox)e.Item.FindControl("priceText");string newPrice = priceText.Text.Substring(1);DataSet ds = GetSessionData();DataTable titlesTable = ds.Tables["Title"];string titleID =(string)titlesGrid.DataKeys[e.Item.ItemIndex];DataRow[] rows = titlesTable.Select("title_id = '" +titleID + "'");DataRow editRow = rows[0];editRow.BeginEdit();editRow["price"] = newPrice;editRow.EndEdit();editRow.AcceptChanges();titlesGrid.EditItemIndex = -1;LoadTitlesGrid();}}}
}
對支持代碼的唯一變更與繼續對數據源進行更新之前檢查頁面的有效性有關。驗證控件會自動更新頁面的 IsValid 屬性。
檢查有效性是在 UpdateCommand 事件處理器中完成的。如第 6 步中所述,不采取任何動作就從事件處理器返回,會保持行的編輯模式。因此,當,且僅當頁面處于有效狀態時,才會執行所有的更新邏輯。
證實控件還會在無效時自動顯示其錯誤消息,且無需編寫任何補充代碼。
第 8 步: 定制列 
正如到目前您所看到的, DataGrid 控件支持標準的列集,諸如 BoundColumn、 ButtonColumn 和 TemplateColumn。該控件還允許您用自己的列類型對控件進行擴展。這些新的列提供了高度的靈活性。這一步實施一個名為 ImageColumn 的定制列,該列用于在單元格中,為帶有圖象 URL 數據綁定的每行顯示一個圖象。
圖 9. 完成第 8 步后的頁面
Titles DataGrid 以及 ImageColumn 的聲明來自:
Step8.aspx:Step8.aspx:
<%@ Register TagPrefix="s" Namespace="Samples"%>
...
<asp:DataGrid id="titlesGrid" runat="server"...><property name="Columns">
    <s:ImageColumn ImageField="title_id"
       ImageFormatString="images/Title-{0}.gif"/></property>...
</asp:DataGrid>
本頁面包含一個寄存器指令,用于映射 <s:ImageColumn> 標記,以表示 Samples.ImageColumn 類。
DataGrid 的聲明展示添加到 DataGrid 的 Columns 集合的 ImageColumn。 還展示 ImageField 和 ImageFormatString 集,因而圖象是以包含與特定行相關聯的標題 ID 的 URL 為依據的。其工作原理與第 2 步中所使用的 HyperLinkColumn 十分類似。
ImageColumn.cs:
namespace Samples {...public class ImageColumn : Column {private PropertyDescriptor imageFieldDesc;public ImageColumn() {}public string ImageField {get {object o = State["ImageField"];return (o != null) ? (string)o : String.Empty;}set {State["ImageField"] = value;}}public string ImageFormatString {get {object o = State["ImageFormatString"];return (o != null) ? (string)o : String.Empty;}set {State["ImageFormatString"] = value;}}// 在與該列相關聯的單元格中創建// 控件public override void InitializeCell(TableCell cell,int columnIndex,ListItemType itemType) {base.InitializeCell(cell, columnIndex, itemType);if ((itemType == ListItemType.Item) ||(itemType == ListItemType.AlternatingItem) ||(itemType == ListItemType.SelectedItem) ||(itemType == ListItemType.EditItem)) {Image image = new Image();image.ID = Column.GetColumnControlID(columnIndex, -1);cell.Controls.Add(image);if (ImageField.Length != 0) {image.AddOnDataBind(newEventHandler(this.OnDataBindColumn));}}}// 將數據載入在 InitializeCell 中創建的控件private void OnDataBindColumn(object sender, EventArgs e) {Image boundImage = (Image)sender;DataGridItem item = (DataGridItem)boundImage.NamingContainer;object dataItem = item.DataItem;if (IsFirstDataBind()) {string imageField = ImageField;imageFieldDesc =TypeDescriptor.GetProperties(dataItem)[imageField];if (imageFieldDesc == null) {throw new Exception("Invalid property: '" +imageField + "'");}OnFirstDataBindHandled();}object data = imageFieldDesc.GetValue(dataItem);if (data != null) {string format = ImageFormatString;if (format.Length != 0) {boundImage.ImageUrl = String.Format(format, data);}else {boundImage.ImageUrl = data.ToString();}}}}
}
DataGrid 控件所使用的每一列均由抽象的 Column 類派生而來。 列類型實施各種各樣的屬性 (諸如 HeaderText) 以及所有列類型所公用的樣式。
ImageColumn 類用于添加針對其具體功能的屬性,諸如 ImageField 和 ImageFormatString 屬性。實施這些屬性,是通過將各值存入列的 State 中實現的。列的狀態,在 DataGrid 控件的往返過程中自動得到保持。
每列所超控的最為重要的虛擬方法就是 InitializeCell 方法。 DataGrid 促使每列初始化與該列相關聯的單元格。實施本方法時,列創建其所需要的控件,并將它們添加為單元格的子單元格。所創建的控件可能會隨 itemType 參數的不同而不同,該參數指示包含單元格的列的 ItemType 屬性。 Column 類自身包含用于產生標頭和標尾的邏輯。 ImageColumn 在進行自我實施時,只是創建一個 Image 控件。它還向 Image 的 DataBind 屬性添加一個事件處理器。
一旦將列創建完畢, DataGrid 就對其進行數據綁定。在該進程中,允許列借助與列相關聯的數據對其在 InitializeCell 中創建的控件進行定制。 ImageColumn 檢索其所綁定的字段的值,使用用戶所指定的格式生成一個 URL,然后使用結果字符串來設定圖象的 ImageUrl 屬性。
結論 
DataGrid 控件簡化了多個常見 Web 應用情形,其中包括只讀報表到交互式應用程序 UI。該控件優于傳統的 ASP 編程。它將轉換對象模型操作和數據綁定所需的邏輯封裝進與瀏覽器無關的 HTML 表現功能。還將處理回傳數據以及轉換客戶機事件的詳細資料封狀進服務器事件。
該控件設計用于無須作出太多開發努力就可以表現您的數據。隨著應用要求的改變,以及您開始使用 DataGrid 的各種功能,您可以逐步添加其它功能。
現在 Microsoft??.NET SDK 和 Framework 中就已提供該控件。 SDK 還包含補充文檔和多個樣例,可用于快速啟動;兩者均實施本文中所提供的材料。
總結
以上是生活随笔為你收集整理的使用 ASP+ DataGrid 控件来创建主视图/详细资料视图的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 常见 Datagrid 错误
- 下一篇: 为 ASP.NET Datagrid 创
