真正類似于 Excel 的網格控件
發表時間:2024-02-22 來源:明輝站整理相關軟件相關文章人氣:
[摘要]注 與本文相關的下載文件的 Visual Basic_ 版本已于 3 月 4 日進行了更新。如果您是在 3 月 4 日之前下載源代碼的,則將需要重新下載該文件以獲得 Visual Basic 文件。ASP.NET DataGrid 控件生成一個 HTML 輸出結果,此結果看上去確實像 Micros...
注 與本文相關的下載文件的 Visual Basic_ 版本已于 3 月 4 日進行了更新。如果您是在 3 月 4 日之前下載源代碼的,則將需要重新下載該文件以獲得 Visual Basic 文件。
ASP.NET DataGrid 控件生成一個 HTML 輸出結果,此結果看上去確實像 Microsoft Excel 工作表的 Web 副本。另外,該控件支持如選擇和就地編輯之類的功能,這些功能又進一步證實了這種相似性。特別是,從支持就地編輯功能這一點來看時,這種相似性就最為明顯了。當您單擊特殊類型的命令列時,網格會使用文本框(而非靜態文本)重繪其內容。與此同時,命令列會更改布局,將編輯鏈接替換為兩個其他鏈接 — 一個用來保存所做的更改,另一個用來取消所做的更改。整個看上去幾乎與 Excel 名稱框命令欄完全相同。
DataGrid 還使程序員有機會在某種程度上自定義所編輯的單元格的布局。這可通過以下方法來實現:使用模板化列來代替綁定列和按鈕列,并在模板化列的標記正文中定義編輯模板。
簡單地說,DataGrid 控件只是為就地編輯提供基礎結構,并在保存所做的更改時激發某些事件。為了能夠完全編輯,網格組件應當提供三個您可能希望針對數據源執行的基本操作:“插入、“刪除和 “更新”。DataGrid 用于編輯的基礎結構(基本上是 EditCommandColumn 列類)只保證能夠執行更新和刪除操作。實現 “刪除” 功能相對容易些,而且只要求您定義一個命令名為 “刪除” 的 ButtonColumn 對象,并等待DeleteCommand 事件激發。
在本專欄中,您將看到如何擴展 DataGrid 控件,使其支持 INSERT 命令。我們將通過創建一個新類并讓其從 DataGrid 繼承來實現這一點。我們將使該類盡可能多地實現樣本代碼,以省去某些重復編碼。因此,我們將擁有一個激發新事件和更具體事件的控件,而且我們可以使用這個唯一的界面來維護數據庫表。
EditableGrid 控件
新控件應當具有哪種接口?
的思路是盡可能限制程序員必須編寫的代碼的數量。該控件將負責向其自身的接口中添加任何新行,然后在需要保存所做的更改時警告用戶。這一原則適用于大多數操作,而不應當僅限于 “插入”。在實現就地編輯和刪除時,總是必須在 SQL 語句周圍放一些相對標準的和重復的代碼。特別是在實現就地編輯功能時,必須重置所編輯項目的索引,并重新加載和重新綁定更新過的數據源。所有這些樣本代碼將嵌入到新的 EditableGrid 控件中。因此,該控件提供一個名為 AddNewRow 的新布爾屬性,以及幾個自定義事件 — InitRow、UpdateView、“保存數據”、“插入數據” 和 “刪除數據”。
當用戶希望添加新行時,他(或她)只需將 AddNewRow 屬性設置為 true 并重新綁定該控件。該操作的其余部分在內部發生。(稍后我將描述此過程的細節。)新行將在編輯模式下繪制,InitRow 事件將激發,其目的只是使您有機會將某些字段設置為默認值。
UpdateView 的角色不與更新操作緊密相關。DataGrid 控件不緩存數據源,因此,無論頁面何時回發(以便進行分頁、排序、編輯、插入或刪除),您都需要重新綁定。為了簡化編碼并盡可能多地嵌入編碼,添加了這個新事件。當該網格需要設置其 DataSource 屬性時,“更新視圖” 就會激發。“更新視圖” 的典型處理程序將當前的數據源分配給該屬性并調用 DataBind 方法。
當相應的 SQL 語句不能進一步延遲執行時,就會激發其他三個事件 — “保存數據”、“插入數據” 和 “刪除數據”。在處理其中的任何事件時,可設置和執行 “更新”、“插入” 或 “刪除” 語句。您負責檢索更新后的數據,并準備和執行該命令。除了 “插入數據”(與 DataGrid 編程接口沒有緊密關系)以外,“保存數據” 和 “刪除數據” 也不同于標準的 UpdateCommand 和 DeleteCommand,這是因為它們更具體而且只要求您執行 SQL 代碼。新事件基本上由 UpdateCommand 和 DeleteCommand 的處理程序激發,這些處理程序是 EditableGrid 控件在加載時以靜默方式定義的。這些內部處理程序負責執行所有其他任務(例如,重置索引)并刷新視圖。后者(即刷新視圖)是通過激發 UpdateView 事件來完成的。
設置控件
讓我們快速瀏覽 EditableGrid 類。該類的構造函數初始化某些公共屬性和受保護的屬性,并為基類的幾個事件設置默認處理程序。
namespace BWSLib
{
public class EditableGrid : DataGrid
{
public EditableGrid()
{
AllowFullEditing = true;
AddNewRow = false;
AllowPaging = true;
RejectChanges = false; // internal use
MustInsertRow = false; // internal use
// Handlers
Init += new EventHandler(OnInit);
PageIndexChanged += new
DataGridPageChangedEventHandler(OnPageIndexChanged);
ItemCreated += new DataGridItemEventHandler(OnItemCreated);
CancelCommand += new DataGridCommandEventHandler(OnCancelCommand);
EditCommand += new DataGridCommandEventHandler(OnEditCommand);
UpdateCommand += new DataGridCommandEventHandler(OnUpdateCommand);
DeleteCommand += new DataGridCommandEventHandler(OnDeleteCommand);
}
:
}
}
EditableGrid 類有一個尚未提到的公共屬性 — AllowFullEditing。該屬性成員支持對網格的完全編輯功能。如果您將該屬性設置為 false,則該控件將不提供就地編輯或插入功能。究竟執行的是怎樣的處理?該控件自動將 AllowPaging 設置為 true,并為 PageIndexChanged 提供一個處理程序。這意味著 EditableGrid 還是比 DataGrid 控件好一些,因為它為您提供自動的空閑分頁。
當 AllowFullEditing 設置為 true(默認值)時,EditableGrid 控件自動將兩個新列追加到網格中。第一列是 EditCommandColumn,它提供就地編輯功能。第二列是 ButtonColumn,它的命令是 DELETE。這兩列都是通過為響應 Init 事件而運行的處理程序來添加的。
public void OnInit(Object sender, EventArgs e)
{
if (AllowFullEditing)
AddWorkerColumns();
}
private void AddWorkerColumns()
{
// Edit column
EditCommandColumn editColumn = new EditCommandColumn();
editColumn.EditText = EditColumnText;
editColumn.UpdateText = EditColumnUpdateText;
editColumn.CancelText = EditColumnCancelText;
Columns.Add(editColumn);
// Delete column
ButtonColumn deleteColumn = new ButtonColumn();
deleteColumn.CommandName = "delete";
deleteColumn.Text = DeleteColumnText;
Columns.Add(deleteColumn);
}
EditColumnText、EditColumnUpdateText、EditColumnCancelText 和 DeleteColumnText 確定用來觸發編輯和刪除操作的按鈕的文本。它們在默認時分別為 “編輯”、“確定”、“取消” 和 “刪除”。
在將該控件插入到任何 ASP.NET 頁中之前,必須按如下方式注冊它:
<%@Register TagPrefix="expo" Namespace="BWSLib" Assembly="EditableGrid" %>
在上面的聲明中,可以更改 TagPrefix 屬性的內容,但是,除了該控件的命名空間和類名可以修改以外,不能更改任何其他內容。下面的代碼顯示如何在 ASPX 頁中聲明該控件:
<expo:EditableGrid id="grid" runat="server"
AutoGenerateColumns="false"
Font-Name="verdana" Font-Size="xx-small"
CellSpacing="0" CellPadding="3"
BorderStyle="solid" BorderColor="skyblue" BorderWidth="1"
GridLines="both"
PageSize="4"
DataKeyField="employeeid"
OnInitRow="InitRow"
OnUpdateView="UpdateView"
OnSaveData="SaveData"
OnInsertData="InsertData"
OnDeleteData="DeleteData">
:
<columns>
:
</columns>
</expo:EditableGrid>
示例頁從一個名為 Employees 的 Microsoft Access 2000 表讀出數據并將生成的 DataSet(數據集)存儲在 Cache 對象中。
private DataSet PhysicalDataRead()
{
OleDbDataAdapter da = new OleDbDataAdapter(
"SELECT * FROM Employees",
"PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=c:\\myemployees.mdb;");
DataSet ds = new DataSet();
da.Fill(ds, "Employees");
return ds;
}
圖 1 顯示該頁中控件的輸出結果。
圖 1. 操作中的 EditableGrid 控件
添加和刪除列時無需任何額外的代碼。可是,必須要指定 UpdateView 處理程序。但是,正如您可以看到的那樣,這是一段必須在多個位置與 DataGrid 控件一起使用的代碼。然而,由于有 UpdateView 事件,所以只需指定一次。
public void UpdateView(Object sender, EventArgs e)
{
UpdateDataView();
}
private void UpdateDataView()
{
DataSet ds = (DataSet) Cache["MyData"];
DataView dv = ds.Tables["Employees"].DefaultView;
grid.DataSource = dv;
grid.DataBind();
}
在上面的控件聲明中,還可以看到三個與數據相關的事件的事件處理程序。讓我們看一下該控件如何處理它們。
Insert 操作
EditableGrid 控件不包括任何用來啟動 “插入” 操作的用戶界面元素。正如對于其他重要功能一樣,程序員負責將能夠觸發網格上給定事件的超級鏈接或按鈕放到頁面中。由于添加新項目不是依賴特定行的操作,因此建議您不要使用特殊的命令列(例如,“編輯” 或 “刪除”)。客戶端代碼處理頁面級事件,并設置 AddNewRow 屬性以響應該操作。接著,頁面刷新網格,以反映所做的更改。例如,圖 1 中的 Insert(插入)按鈕鏈接到下面的 on-click 處理程序:
public void OnInsert(Object sender, EventArgs e)
{
grid.AddNewRow = true;
UpdateDataView();
}
“更新數據視圖” 是頁面級過程,它處理網格數據綁定并設置 “數據源” 屬性。
設計的思路是使用戶不直接將新記錄添加到數據源中,而是只聲明其想要實現的目標。因此,在設置 EditableGrid 控件的 “數據源” 屬性時,該控件會檢查 AddNewRow 的值。在派生的控件中,因為有多種訪問器,所以可以很方便地確定何時讀取或寫入給定屬性。這樣做之后,EditableGrid 會按如下方式重寫 “數據源” 屬性:
public override object DataSource
{
get {return base.DataSource;}
set
{
// Standard behavior
base.DataSource = value;
// Customized behavior
if (AllowFullEditing)
{
if (AddNewRow)
{
AddNewRow = false;
InsertNewRow();
}
// More code here...
:
}
}
}
在設置 “數據源” 時,只要 AddNewRow 屬性為 true,就會追加一個新行。InsertNewRow 就是用來為插入和行編輯功能設置網格的內部過程。此處進行的重要假設就是網格與 “數據視圖” 對象綁定。利用該代碼,您可以方便地推廣到更普遍的解決方案。
private void InsertNewRow()
{
// Get the underlying DataTable object
DataTable dt = ((DataView) DataSource).Table;
// If any pending changes, stop here to avoid inserting over
// and over again if user refreshes...
DataTable tableOfPendingChanges = dt.GetChanges(DataRowState.Added);
if (tableOfPendingChanges!= null)
return;
// Add the new row
DataRow row = dt.NewRow();
dt.Rows.Add(row);
// Initialize the row (fire the InitRow event)
DataGridInitRowEventArgs dgire = new DataGridInitRowEventArgs();
dgire.Row = row;
OnInitRow(dgire);
// Goto to last page and turn edit mode for the last item
int nNewItemIndex = SetIndexesToLastPage(dt);
EditItemIndex = nNewItemIndex;
// Tracks that a new row has just been added (internal member)
MustInsertRow = true; // means the table has pending changes
}
在創建基礎 “數據表” 對象以后,使用 NewRow 方法添加新行,并激發自定義的 InitRow 事件,以便用戶有機會將某些字段設置為默認值。為了實現其目標,InitRow 需要將某些數據向下傳遞到事件處理程序并接收任何更新。必須安排一個自定義的事件數據結構和一個自定義的事件處理程序簽名。DataGridInitRowEventArgs 是事件數據結構的名稱,而 DataGridInitRowEventHandler 是要使用的委托。
public delegate void DataGridInitRowEventHandler(
Object sender, DataGridInitRowEventArgs e);
public event DataGridInitRowEventHandler InitRow;
private void OnInitRow(DataGridInitRowEventArgs e)
{
if (InitRow != null)
InitRow(this, e);
}
DataGridInitRowEventArgs 類繼承于 EventArgs 并通過簡單地添加一個 DataRow 成員進行擴展。
public sealed class DataGridInitRowEventArgs : EventArgs
{
// Represents the row being added. You can use this object
// to set fields to default values.
public DataRow Row;
}
在激發 InitRow 事件之前,該控件用新創建(但仍為空)的 DataRow 對象的實時實例填充 Row 成員。
DataGridInitRowEventArgs dgire = new DataGridInitRowEventArgs();
dgire.Row = row;
OnInitRow(dgire);
如果需要設置某些默認值,則可以編寫 InitRow 處理程序并設置 DataRow 對象的字段。
public void InitRow(Object sender, DataGridInitRowEventArgs e)
{
e.Row["LastName"] = "Esposito";
}
此處,DataSource 額外有一行可模擬新記錄的插入操作。該行已經添加到數據源的底部。因此,您應當使網格指向它的最后一頁,而且還要考慮到新行進入新頁的可能性。例如,如果表中有八個記錄,而且您使用的頁面大小為四,則在添加新記錄時還需要增加一個新頁。為了使用戶能夠編輯新行的內容,只需將 DataGrid 的 EditItemIndex 屬性設置為新行的頁索引。InsertNewRow 完成的最后一步是設置內部數據成員,來跟蹤向表中添加新的未提交行的操作。
簡言之,該代碼將一個空行對象添加到網格的數據源中。該行代表 “數據表” 對象的待定更改,而且如果用戶取消該操作,則必須拒絕該行。如果用戶移到另一頁或者決定單擊和編輯同一頁上的另一行,也必須拒絕該行。拒絕待定更改是內置處理程序(例如,PageIndexChanged、EditCommand 和 CancelCommand)通過內部數據成員 RejectChanges 完成的事情之一。(有關更多詳細信息,請參閱源代碼。)
通過將新行置于編輯模式,即可完成插入階段,從而進入更新階段,如下圖所示。
圖 2. 插入新行
對于 InsertNewRow 的主體,我還需要闡明另外一個方面。在獲取數據源之后,該方法立即確保沒有已添加但尚未提交的行。在設計上,最多可以有一個待定更改,而且如果該代碼重新進入有一個已添加的行的過程,那是由于用戶刷新了該頁面。為了避免反復添加空白行和未提交的行,程序流會跳出代碼塊。
Update 操作
請注意,在圖 2 中看不到可在圖 1 中看到的 Delete 列。為了簡化界面,我決定在任何行進入編輯模式時隱藏 Delete 列。并且因為有了以前提到的內置事件處理程序,所以此行為在 EditableGrid 類中是硬編碼的。
update 操作是由以下三個事件執行的:
• EditCommand — 開始該操作并以編輯模式呈現行
• UpdateCommand — 保存所做的更改并還原默認用戶界面
• CancelCommand — 取消所做的更改并還原以前的用戶界面
用于 EditCommand 和 CancelCommand 的典型代碼是能夠方便地嵌入類中的傳統 vanilla 代碼。一般來說,此處沒有真正需要在頁面級解決的內容,但是有時情況也可能與您的特定實例有所不同。
“更新” 操作的應用程序特定的邏輯集中在 “更新命令” 事件中。除了保存任何更改以外,您應當還原網格的一致狀態(取消編輯模式、還原 Delete 列和拒絕待定的更改),并確保所做的更改隨后由用于顯示的數據源反映出來。如果您像本例中那樣使用緩存數據,則后面的一點至關重要。
public void OnUpdateCommand(Object sender, DataGridCommandEventArgs e)
{
// Clear edit mode
EditItemIndex = -1;
// Show/Hide DELETE column
ToggleDeleteColumn(true);
// Reject changes on the last row
RejectChanges = true; // internal member
// Update or insert data
if (MustInsertRow) // internal member
OnInsertData(e);
else
OnSaveData(e);
// Refresh view
OnUpdateView(EventArgs.Empty);
}
UpdateCommand 處理程序取消當前行的編輯模式,然后打開 Delete 列的可見性標志。此時,該表可能有一個待定的更改。因為 “更新命令” 事件會在兩種情況(打算將所做的更改保存到現有的行中時;要插入新行時)下激發,所以條件窗體很有意義。內部成員 MustInsertRow 由 InsertNewRow 設置并由 DataSource 重置,它有助于確定是哪種情況。當代碼處理完網格的狀態之后,它激發兩個連續事件 — 一個事件讓頁面保存或插入到物理數據源中,另一個事件刷新數據綁定。這就解釋了為何 InsertData 和 SaveData 處理程序只能執行主要通過 SQL 語句來進行的物理更新。
InsertData 和 SaveData 事件處理程序的簽名與 UpdateCommand 的相同。
public event DataGridCommandEventHandler SaveData;
protected virtual void OnSaveData(DataGridCommandEventArgs e)
{
if (SaveData != null)
SaveData(this, e);
}
public event DataGridCommandEventHandler InsertData;
protected virtual void OnInsertData(DataGridCommandEventArgs e)
{
if (InsertData != null)
InsertData(this, e);
}
在本文中討論的示例代碼中,設置了幾個假設,其中一個就是假設網格的數據源是 “數據視圖” 對象。這間接表示不支持自定義分頁,或者,更確切地講,必須仔細修改此代碼才能處理這樣的網格。這同樣適用于排序。
所作的第二個重要假設是有關使用 SQL 語句進行更新的。
設計思路是,無論進行了什么樣的更改,總是激發單個 SQL 語句以應用它。此處設計的 EditableGrid 不能正確地處理批更新。順便提一句,在我的 Building Web Solutions with ASP.NET and ADO.NET 一書中,在介紹就地編輯時,更詳細地討論了對于網格使用批更新的優缺點。然而,如果您對使用批更新技術感興趣,請給我發送電子郵件,我將在以后的專欄中介紹此主題。
由于您通過直接的 SQL 語句(或者數據源識別為直接語句的內容)進行更新,因此,“更新命令” 事件可以成功地命令拒絕任何更改。這就是批更新方案中的主要區別。
“保存數據” 和 “插入數據” 事件代表執行更新所必需的任務的子集。在執行該命令之后,這些處理程序必須確保網格可以訪問和顯示刷新數據。在這種情況下,必須更新數據的緩存副本。根據基礎數據庫架構(任何觸發器或任何自動編號的字段),可以決定是完全重新讀取還是基于每個字段更新緩存數據。
讓我們花些時間來了解如何從網格的編輯模板來檢索更新后的數據。我考慮使 EditableGrid 控件具有足夠的智能,以便從單元格提取值,并將它們填充到 DataRow 對象中以充當事件數據。這個方法使得在批方案中更新代碼變得微不足道。之所以決定讓 ASP.NET 頁負責提取數據,是因為這樣您也可以透明地支持編輯模板。下面我將向您展示在不使用任何特殊模板時所必需的代碼。
public void SaveData(Object sender, DataGridCommandEventArgs e)
{
StringBuilder sb = new StringBuilder("");
sb.Append("UPDATE Employees SET ");
sb.Append("firstname='{0}',");
sb.Append("lastname='{1}',");
sb.Append("title='{2}',");
sb.Append("country='{3}' ");
sb.Append("WHERE employeeid={4}");
String cmd = sb.ToString();
sb = null;
TextBox fName = (TextBox) e.Item.Cells[1].Controls[0];
TextBox lName = (TextBox) e.Item.Cells[2].Controls[0];
TextBox position = (TextBox) e.Item.Cells[3].Controls[0];
TextBox country = (TextBox) e.Item.Cells[4].Controls[0];
cmd = String.Format(cmd,
fName.Text, lName.Text,
position.Text, country.Text,
grid.DataKeys[e.Item.ItemIndex]);
// Executes the command
OleDbConnection conn = new OleDbConnection(m_connString);
OleDbCommand c = new OleDbCommand(cmd, conn);
c.Connection.Open();
c.ExecuteNonQuery();
c.Connection.Close();
// Re-read from the database and updates the cache
DataFromSourceToMemory();
}
要檢索用戶在文本框中輸入的信息,使用 e.Item.Cells[n].Controls[0] 表達式,其中n 是該列的索引(從 0 開始)。請記住,DataGrid 的就地編輯功能允許您通過將 “只讀” 屬性設置為 true 來將列視為只讀。
Delete 操作
Delete 操作的工作方式與 Insert 和 Update 操作大體相同。自動創建的列有一個命令名 Delete,在單擊該按鈕時會導致激發 ± 事件。內置的處理程序修復網格的界面,然后依次先后激發 DeleteData 和 UpdateView。
由于刪除操作的入侵性比插入或更新操作的強,因此您可能希望使用某個客戶端腳本代碼,并要求用戶在繼續之前確認。我在 last month's column 的“對話欄”部分中討論了該方法。在本月的源代碼中,有一個如下圖所示的實際實現。
圖 3. 在刪除之前確認
最終的優化
我提到過 EditableGrid 控件支持編輯模板。讓我來證明這一結論。在示例代碼的第 2 步,我使用該網格中一組稍有不同的列。
<columns>
<asp:boundcolumn runat="server" headertext="ID"
datafield="employeeid" readonly="true" />
<asp:templatecolumn runat="server" headertext="Name">
<itemtemplate>
<%# DataBinder.Eval(Container.DataItem, "lastname") + ", " +
DataBinder.Eval(Container.DataItem, "firstname") %>
</itemtemplate>
<edititemtemplate>
<asp:textbox runat="server" id="lName"
text='<%#DataBinder.Eval(Container.DataItem, "lastname")%>' />
<asp:textbox runat="server" id="fName"
text='<%#DataBinder.Eval(Container.DataItem, "firstname")%>' />
</edititemtemplate>
</asp:templatecolumn>
<asp:boundcolumn runat="server" headertext="Position"
datafield="title" />
<asp:boundcolumn runat="server" headertext="Country"
datafield="country" />
</columns>
正如您可以看到的那樣,有一個模板列,它在單個字段中顯示名和姓。此列的編輯模板(您必須指定能夠編輯該列的編輯模板)由兩個并排的文本框提供。無需在類中進行任何更改,即可獲得圖 4 中所示的示例。
圖 4. 使用編輯模板
另一方面,在使用從文本框中檢索更新的文本的方式時,編輯模板需要進行少許調整。現在,可以按名稱檢索模板中的控件。之所以可以并且建議這樣做,是因為您知道控件的 ID。
TextBox fName = (TextBox) e.Item.FindControl("fName");
TextBox lName = (TextBox) e.Item.FindControl("lName");
TextBox position = (TextBox) e.Item.Cells[2].Controls[0];
TextBox country = (TextBox) e.Item.Cells[3].Controls[0];
在本月的源代碼中,您將發現該類的 C# 源代碼、兩個示例 ASP.NET 頁以及我用過的 Access 數據庫。但是,您將找不到編譯過的程序集。我提供了一個(非常)簡單的批文件,您可以使用它,針對與 beta 2 及其更高版本兼容的任何版本的 .NET CLR,將該類編譯成為可執行代碼。
注 如果您在使下載正確運行時遇到問題,請檢查下列步驟:
• 1.將 CS 類編譯成為程序集。這可通過打開一個 DOS 框并運行 ZIP 中的 c.bat 批處理文件來完成,或者通過在 Visual Studio 中創建一個新的類庫項目并將類文件添加到這個空白項目中來實現。
• 2.必須使程序集對于示例 ASPX 頁可用。下載文件中包括的批處理文件將該 DLL 復制到 c:\inetpub\wwwroot\bin 文件夾中。如果您恰好有一個不同的路徑,請對它進行修改。如果您創建一個虛擬目錄,請確保將該 DLL 復制到虛擬文件夾的 BIN 子文件夾中,而不要復制到 Web 服務器根的 BIN 文件夾中。
• 3.根據 ASP.NET 安全設置,在與示例 Microsoft Access 數據庫交互時,可能會遇到難以處理的 Updateable Query 錯誤。在這種情況下,請更改示例 .MDB 文件的安全設置。在“Windows 資源管理器”中選擇該文件,顯示屬性,然后單擊安全選項卡。接著,添加 \ASPNET 用戶并確保它有權寫入和修改該文件。
• 4.刷新 ASPX 頁面。
對話欄:創建自定義模板
在上一個專欄中,您討論了 Summary 網格組件。是否有機會創建或加載匯總行的自定義模板?這將允許在 ASPX 文件中(而非代碼中)規定設計的規范。
使用匯總行的自定義模板無疑將是可能的。問題只是在于如何實現?或者,更準確地說,哪種方法最簡單?我將在下面介紹幾種可能的方法。
• 使用 pagelet: 可以修改應用程序的代碼,使其動態加載 pagelet 控件(又稱用戶控件) — 即 ASCX 文件。ASCX 文件看上去像一個小型 Web 窗體,而且其中的大部分內容都是布局信息。可以使用 Page.LoadControl 來按名稱加載 ASCX 文件,然后將它添加到 DataGridItem 對象的某個單元格的 Controls 集合中。
• 模板列:DataGrid 的所有列都是模板化列。使用 ASP.NET 標記和各處的一些代碼定義每一列的 。您能夠執行的操作就是將 塊的結構分成兩個截然不同且互斥的部分 — 一部分用于普通行,另一部分用于匯總行。為了確保一次只顯示一行,可以處理控件的 “可見” 屬性并將該屬性與界定匯總行或普通行的條件綁定。
• 編寫新控件:從 DataGrid 派生一個新類并添加一個新的模板屬性。這允許您使用自定義的子標記、按照與輸入列模板幾乎相同的方式輸入匯總行布局。
返回頁首