DataGrid 控制項中的合計列

來源:互聯網
上載者:User

查看 Summary.cs

查看 Summary.aspx

ASP.NET DataGrid 控制項呈現一個多列、完全模板化的網格,它是 .NET 架構中所有資料繫結 Web 控制項中最通用和最靈活的控制項。DataGrid 的使用者介面在某種程度上類似於 Microsoft Excel 工作表。儘管 DataGrid 具有進階的編程介面以及完整的屬性集,但是它只產生包含交錯超級連結的 HTML 表,從而提供互操作功能(例如,排序和分頁命令)。

使用 DataGrid 控制項,可以建立簡單的資料繫結列(顯示從資料來源檢索的資料)、模板化列(允許您設計儲存格內容的布局),最後但也很重要的是基於命令的列(允許您向網格中添加特定的功能)。

DataGrid 控制項很適合報告資料,而且其足夠的靈活性允許您構建複雜且具有專業型式的資料表,在這些資料表中,可隨意實現諸如分頁和排序之類的功能。然而,其他功能(例如,深化和主/細)只需要較少的工作。在本月的專欄中,我將討論一個無法由該控制項本身提供、但是頗受許多人歡迎的功能。因此,讓我們研究一下如何自動產生複雜的報表,在這些表中,必須顯示具有部分合計的合計列。

DataGrid 項

可通過設定 DataGrid 控制項的 DataSource 屬性來將實際資料繫結到該控制項的執行個體。此屬性是一般的 Object 類型,並且支援兩個配置方案。通常用實現 ICollection 介面的資料對象來設定它。通常將使用 DataTableDataView 對象。另一種方法是,用另一個物件類型(例如,data reader 對象)來設定它。不過,在這種情況下,必須開啟自訂分頁機制;否則將引發異常。簡單地說,您要麼將 DataGrid 綁定到分頁資料來源(即用來實現枚舉數的集合對象),要麼必須為自己提供分頁。

對於 Web 控制項,資料繫結處於啟用狀態,只有在調用 DataBind 方法時,使用者介面才進行重新整理。在重新整理過程中,該控制項會遍曆資料來源並將一些行複製到它的 Items 集合中。Items 屬性代表當前顯示頁的內容。如果資料來源支援分頁(即,實現 ICollection),則 DataGrid 從 DataSource 選擇適合當前頁的正確行子集。否則,它假設 DataSource 的全部內容都適合當前頁並將它們全部載入到 Items 中。在填充完 Items 之後,就會呈現該控制項的使用者介面。

此處有何教訓?DataGrid 控制項能夠安全而又一致地顯示的全部內容就是綁定資料來源中包含的行。因此,如果您希望插入合計列,以便按照公用鍵對某些記錄進行分組並顯示部分合計,則必須指出將這些合計列直接插入資料來源的方法。

然而,將合計列插入資料來源是不夠的。實際上,必須能夠區分合計列和普通行,並用不同的視覺樣式呈現前者。

在將資料附加到該控制項之前,確保資料來源包含其所需的所有合計列。接著,掛鈎 ItemCreated 事件,檢測每個合計列,然後用不同的布局和樣式繪製它們。讓我們看一看如何在 SQL 查詢的不同行之間插入合計列。我將使用基於 Northwind SQL Server 資料庫的應用程式範例來闡釋我的觀點。該應用程式列出每個客戶已在給定年份發出的所有定單。定單按年份和客戶 ID 進行分組。對於每個客戶,都額外有一行來匯總定單的總數和總額。

資料分組

下面的 SQL 命令選擇所有客戶在給定年份發出的所有定單。只顯示每個定單所有項目價格的總和。

SELECT o.customerid, od.orderid, SUM(od.quantity*od.unitprice) AS price FROM Orders o, [Order Details] odWHERE Year(o.orderdate) = @TheYear AND od.orderid=o.orderidGROUP BY o.customerid, od.orderidORDER BY o.customerid

在 T-SQL 語言中,SELECT 語句的 GROUP BY 子句提供將預定義的合計列添加到結果集的 WITH ROLLUP 子句。當然,這樣的合計列具有所有其他列的布局,但每一列的內容都可以進行某種程度的自訂。下面的語句闡釋如何修改上面的命令,以使其允許使用合計列。

DECLARE @TheYear intSET @TheYear = 1998SELECT CASE GROUPING(o.customerid) WHEN 0 THEN o.customerid ELSE '(Total)' END AS MyCustomerID, CASE GROUPING(od.orderid) WHEN 0 THEN od.orderid ELSE -1 END AS MyOrderID, SUM(od.quantity*od.unitprice) AS priceFROM Orders o, [Order Details] odWHERE Year(orderdate) = @TheYear AND od.orderid=o.orderidGROUP BY o.customerid, od.orderid WITH ROLLUPORDER BY o.customerid, price

如果您將該程式碼片段複製並粘貼到 SQL 查詢分析器中,將看到如所示的內容。

圖 1. WITH ROLLUP 子句將合計列添加到結果集

GROUPING 是一種 T-SQL 彙總函式,它與 ROLLUP 一起工作於 GROUP BY 子句的主體中。使用 GROUPING 運算子將使新列添加到結果集。如果該行已經由 ROLLUP 運算子添加並因此而成為合計列,則新列中將包含值 1。否則,該列將包含值 0。使用 CASE..WHEN..END 語句可以將這個新列與分組列合并在一起。

在上面的樣本中,MyCustomerID 列在所有因其進行分組而建立的行中包含 CustomerID 列的值以及字串 “(Total)”。同樣,當該行代表小計時,MyOrderID 列包含定單 ID 和 –1。

為了對資料進行匯總,SQL Server 還提供了幾個選項,例如,WITH CUBE 運算子和 COMPUTE BY 子句。正如您所想象的那樣,儘管一個選項的功能以某種方式與另一個選項的功能相交叉,但是所有這些選項並不完全等價。特別是,WITH CUBE 針對結果集內組與子組的每個可能的組合都產生一個合計列。而 WITH ROLLUP 按照分組列的指定順序來進行分組。最後,COMPUTE BY(SQL Server 2000 支援它的目的僅在於獲得向後相容性)的工作方式與 WITH ROLLUP 大體相同,不同的是它返回多個結果集,而且在由查詢最佳化工具處理時不如 ROLLUP 效率高。

顯示已分組資料

當綁定到 DataGrid 控制項時,由 SQL 命令返回的結果集看上去如所示。

圖 2. 通過 DataGrid 控制項顯示的結果集

應用程式範例中使用的 DataGrid 控制項按如下方式進行聲明:

<asp:DataGrid id="grid" runat="server" AutoGenerateColumns="false"AllowPaging="true" PageSize="15"Font-Size="xx-small"CellSpacing="0" CellPadding="4" GridLines="both"BorderStyle="solid" BorderColor="skyblue" BorderWidth="1" OnItemCreated="ItemCreated"OnPageIndexChanged="PageIndexChanged"><headerstyle backcolor="skyblue" font-size="9pt" font-bold="true" /><itemstyle backcolor="#eeeeee" /><pagerstyle backcolor="skyblue" font-name="webdings" font-size="10pt" PrevPageText="3" NextPageText="4" /><Columns><asp:BoundColumn DataField="MyCustId" HeaderText="Customer" /><asp:BoundColumn DataField="MyOrderId" HeaderText="Order #" /><asp:BoundColumn DataField="price" HeaderText="Amount" DataFormatString="{0:c}"><itemstyle horizontalalign="right" /></asp:BoundColumn></Columns></asp:DataGrid>

使用 WITH ROLLUP 運算子擷取的資料來源已經包含產生有效報告所必需的全部資訊。您可能已經注意到了,該語句添加一個頂行,其中包含由所有客戶發出的全部定單的合計。在使用 WITH ROLLUP 運算子時,如果您修改了分組行的順序,則所產生行的數量和結構可能會發生顯著變化。這額外的一行是我選擇使用特定文法的結果。如果您不需要這段資訊,只需在綁定之前將它從結果集刪除。或者,可以將該行移到資料集的底部。

下面顯示的代碼闡釋如何執行 rollup 語句。從文字框中讀出的參數是要考慮的年份。結果集臨時儲存在 DataSet 對象中。在這個應用程式範例中,我將在 Session 槽中緩衝 DataSet 對象。在實際環境中,這應該是受重視的選擇。通常,儲存在 Session 中的任何位元組都有一個位於那裡的充分理由。

private DataSet PhysicalDataRead(){String strCnn = "SERVER=localhost;DATABASE=northwind;UID=sa;";SqlConnection conn = new SqlConnection(strCnn);// Command text using WITH ROLLUPStringBuilder sb = new StringBuilder("");sb.Append("SELECT ");sb.Append("  CASE GROUPING(o.customerid) WHEN 0 ");sb.Append("    THEN o.customerid ELSE '(Total)' END AS MyCustID, ");sb.Append("  CASE GROUPING(od.orderid) WHEN 0 ");sb.Append("    THEN od.orderid ELSE -1 END AS MyOrderID, ");sb.Append("  SUM(od.quantity*od.unitprice) AS price ");sb.Append("FROM Orders o, [Order Details] od ");sb.Append("WHERE Year(orderdate)=@nYear AND od.orderid=o.orderid ");sb.Append("GROUP BY o.customerid, od.orderid WITH ROLLUP ");sb.Append("ORDER BY o.customerid, price");String strCmd = sb.ToString();sb = null;SqlCommand cmd = new SqlCommand();cmd.CommandText = strCmd;cmd.Connection = conn;   SqlDataAdapter da = new SqlDataAdapter(strCmd, strConn);da.SelectCommand = cmd;// Set the "year" parameterSqlParameter p1 = new SqlParameter("@nYear", SqlDbType.Int);p1.Direction = ParameterDirection.Input;p1.Value = Convert.ToInt32(txtYear.Text);cmd.Parameters.Add(p1);DataSet ds = new DataSet();da.Fill(ds, "Orders");return ds;}

為了使合計列在該網格的頁面中清楚地顯示,需要更改合計列的樣式和布局。這可在 ItemCreated 事件處理常式中完成。設計思路是,通過檢查定單 ID 來檢測合計列,然後修改儲存格的布局和樣式。在結果集內,合計列的特徵是定單 ID 為 –1。值 –1 是來自所使用語句的任意值。

CASE GROUPING(od.orderid) WHEN 0 THEN od.orderid ELSE -1 END AS MyOrderID

如果不針對 orderid 列使用 GROUPING 運算子,則對於合計列來說,該列的值將為 NULL。

修改布局和樣式

DataGrid 允許您修改成分儲存格的樣式和布局,這可通過掛鈎 ItemCreated 事件來完成。該控制項每次處理子項(頁首、頁尾、行、頁導航)時,該事件都會被激發。事件處理常式接收類型為 DataGridItemEventArgs 的參數,您可以從該參數提取所處理項目的類型。

合計列是 DataGrid 行,同樣,它的類型可以是 ItemAlternatingItem。因此,在編寫 ItemCreated 處理常式時,要確保只有在該項的類型正確時才處理相應的儲存格。下面的列表概述所需的代碼。

public void ItemCreated(Object sender, DataGridItemEventArgs e){// Get the type of the newly created itemListItemType itemType = e.Item.ItemType;if (itemType == ListItemType.Item || itemType == ListItemType.AlternatingItem) {// Get the data bound to the current rowDataRowView drv = (DataRowView) e.Item.DataItem;if (drv != null){// Check here the app-specific way to detect whether the // current row is a summary row:}}}

如果所建立的項是 DataGrid 項(或交替項),則可以通過 DataItem 屬性訪問綁定到行的資料。根據 DataGrid 綁定到的對象的類型,DataItem 屬性會指向不同的行對象。如果網格綁定到 DataView,會擷取 DataRowView 對象;如果該源用 DataTable 對象來表示,會擷取 DataRow 對象。在該應用程式範例中,我使用 DataView 對象填充了網格。後來,單行的資料對象成為 DataRowView 對象。

在擁有了資料行對象之後,可以應用一些應用程式特定的規則來確定該行是否為合計列。在該應用程式範例中,合計列的 MyOrderID 欄位設定為 –1。

if ((int) drv["MyOrderID"] == -1){   // Modify style and layout here.    //    --> Set the background color to white and use bold font   e.Item.BackColor = Color.White; e.Item.Font.Bold = true;    }

DataGrid 現在看上去如所示。

圖 3. 以粗體顯示且背景為白色的合計列

DataGrid 行實際上只是表中的一行。同樣,使用它可以很好地進行儲存格刪除以及其他調整。讓我們看一看如何使用跨越所有現有列的單一儲存格來呈現合計列。

if ((int) drv["MyOrderID"] == -1)

圖 4. 具有自訂布局的合計列

在這三個原始儲存格中,前兩個被刪除,第三個(現在包含索引 0)被正確對齊並跨越外部表格的寬度。如果您希望在合計列上顯示一些自訂文本,則需要做好面對其他問題的準備。

假設您需要添加一些文本以對小計進行注釋,而且與此同時,讓小計與單個定單量出現在同一列中。在這種情況下,只需刪除一個儲存格。

e.Item.Cells.RemoveAt(1);         // remove the order # celle.Item.Cells[0].ColumnSpan = 2;      // span the custID celle.Item.Cells[1].HorizontalAlign = HorizontalAlign.Right;e.Item.Cells[0].Text = "Total is";

此代碼的結果如下所示。正如您所看到的那樣,它與您的預期結果不完全相同。合計列的第一個儲存格中並沒有您剛剛設定的文本。這是怎麼回事呢?

圖 5. 具有修改後的自訂布局的合計列

此處需要考慮的重要一點是,ItemAlternatingItem 行均為綁定行。它們的明確文本只是在 OnItemDataBound 事件的過程中設定。您可能已經猜到了,OnItemDataBound 事件會在建立該項之後激發。因此,在處理 ItemCreated 時分配給儲存格的任何文本在後來都由某個事件以靜默方式改寫。可通過設定 DataGrid 的 OnItemDataBound 屬性來掛鈎 OnItemDataBound 事件。

<asp:DataGrid id="grid" runat="server" AutoGenerateColumns="false":OnItemCreated="ItemCreated"OnItemDataBound="ItemDataBound"OnPageIndexChanged="PageIndexChanged">The structure of the code for ItemDataBound is shown below.public void ItemDataBound(Object sender, DataGridItemEventArgs e){DataRowView drv = (DataRowView) e.Item.DataItem;if (drv == null)return;if ((int) drv["MyOrderID"] == -1){if (drv["MyCustomerID"].ToString() == "(Total)"){e.Item.BackColor = Color.Yellow;e.Item.Cells[0].Text = "Orders total";}elsee.Item.Cells[0].Text = "Customer subtotal";}}

最上面的一行是在黃色背景上繪製的,它顯示其他合計列中的另一個文本。最終的 DataGrid 顯示如下。

圖 6. 最終的 DataGrid

小結

以應用程式特定的劑量很好地混合 SQL 代碼和 ASP.NET 技術可以實現有效 Web 資料庫應用程式。DataGrid 控制項是一個前沿工具,可用來為它所提供的編程功能構建完美而又功能強大的 Web 應用程式,而且對於它所支援的自訂層級來說用途更多。

對話方塊列:關鍵任務的確認

我如何能夠顯示一個強制使用者在啟動關鍵任務(例如,記錄刪除)之前進行確認的對話方塊?

假設您需要在使用者單擊某個按鈕後顯示這樣的對話方塊。好,這隻需通過一些用來處理 onclick 事件的用戶端 JavaScript 代碼來獲得。

任何 ASP.NET 控制項都可以求值為一個或多個 HTML 標籤。下壓按鈕映射到"按鈕" 標記。連結按鈕映射到由指令碼驅動的超級連結。使用 ASP.NET 控制項的 Attributes 集合,可以同時為這兩個標記註冊指令碼程式碼片段。例如,如果您有一個按鈕(無論它是 LinkButton 對象還是 Button 對象),則可以定義在該按鈕被單擊之後啟動並執行 JavaScript 代碼,如下所示:

String js = "return confirm('Do you really want to delete the record?');";btn.Attributes["onclick"] = js;這樣一來,HTML 標籤包含一個屬性:<a  ? onclick="return confirm(' ? ')"> 

當使用者單擊該連結時,會運行 onclick 用戶端代碼,而且如果使用者單擊 No,該事件將會自動放棄。此行為嵌入到瀏覽器的邏輯中,它幾乎與 ASP.NET 無關。如果 onclick 用戶端處理常式成功退出,則會執行 __doPostBack 函數,而且頁面會回傳到伺服器。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.