明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

真正類似于 Excel 的網(wǎng)格控件

[摘要]注 與本文相關(guān)的下載文件的 Visual Basic_ 版本已于 3 月 4 日進(jìn)行了更新。如果您是在 3 月 4 日之前下載源代碼的,則將需要重新下載該文件以獲得 Visual Basic 文件。ASP.NET DataGrid 控件生成一個(gè) HTML 輸出結(jié)果,此結(jié)果看上去確實(shí)像 Micros...
注 與本文相關(guān)的下載文件的 Visual Basic_ 版本已于 3 月 4 日進(jìn)行了更新。如果您是在 3 月 4 日之前下載源代碼的,則將需要重新下載該文件以獲得 Visual Basic 文件。



ASP.NET DataGrid 控件生成一個(gè) HTML 輸出結(jié)果,此結(jié)果看上去確實(shí)像 Microsoft Excel 工作表的 Web 副本。另外,該控件支持如選擇和就地編輯之類的功能,這些功能又進(jìn)一步證實(shí)了這種相似性。特別是,從支持就地編輯功能這一點(diǎn)來看時(shí),這種相似性就最為明顯了。當(dāng)您單擊特殊類型的命令列時(shí),網(wǎng)格會(huì)使用文本框(而非靜態(tài)文本)重繪其內(nèi)容。與此同時(shí),命令列會(huì)更改布局,將編輯鏈接替換為兩個(gè)其他鏈接 — 一個(gè)用來保存所做的更改,另一個(gè)用來取消所做的更改。整個(gè)看上去幾乎與 Excel 名稱框命令欄完全相同。

DataGrid 還使程序員有機(jī)會(huì)在某種程度上自定義所編輯的單元格的布局。這可通過以下方法來實(shí)現(xiàn):使用模板化列來代替綁定列和按鈕列,并在模板化列的標(biāo)記正文中定義編輯模板。

簡單地說,DataGrid 控件只是為就地編輯提供基礎(chǔ)結(jié)構(gòu),并在保存所做的更改時(shí)激發(fā)某些事件。為了能夠完全編輯,網(wǎng)格組件應(yīng)當(dāng)提供三個(gè)您可能希望針對(duì)數(shù)據(jù)源執(zhí)行的基本操作:“插入、“刪除和 “更新”。DataGrid 用于編輯的基礎(chǔ)結(jié)構(gòu)(基本上是 EditCommandColumn 列類)只保證能夠執(zhí)行更新和刪除操作。實(shí)現(xiàn) “刪除” 功能相對(duì)容易些,而且只要求您定義一個(gè)命令名為 “刪除” 的 ButtonColumn 對(duì)象,并等待DeleteCommand 事件激發(fā)。

在本專欄中,您將看到如何擴(kuò)展 DataGrid 控件,使其支持 INSERT 命令。我們將通過創(chuàng)建一個(gè)新類并讓其從 DataGrid 繼承來實(shí)現(xiàn)這一點(diǎn)。我們將使該類盡可能多地實(shí)現(xiàn)樣本代碼,以省去某些重復(fù)編碼。因此,我們將擁有一個(gè)激發(fā)新事件和更具體事件的控件,而且我們可以使用這個(gè)唯一的界面來維護(hù)數(shù)據(jù)庫表。

EditableGrid 控件

新控件應(yīng)當(dāng)具有哪種接口?

的思路是盡可能限制程序員必須編寫的代碼的數(shù)量。該控件將負(fù)責(zé)向其自身的接口中添加任何新行,然后在需要保存所做的更改時(shí)警告用戶。這一原則適用于大多數(shù)操作,而不應(yīng)當(dāng)僅限于 “插入”。在實(shí)現(xiàn)就地編輯和刪除時(shí),總是必須在 SQL 語句周圍放一些相對(duì)標(biāo)準(zhǔn)的和重復(fù)的代碼。特別是在實(shí)現(xiàn)就地編輯功能時(shí),必須重置所編輯項(xiàng)目的索引,并重新加載和重新綁定更新過的數(shù)據(jù)源。所有這些樣本代碼將嵌入到新的 EditableGrid 控件中。因此,該控件提供一個(gè)名為 AddNewRow 的新布爾屬性,以及幾個(gè)自定義事件 — InitRow、UpdateView、“保存數(shù)據(jù)”、“插入數(shù)據(jù)” 和 “刪除數(shù)據(jù)”。

當(dāng)用戶希望添加新行時(shí),他(或她)只需將 AddNewRow 屬性設(shè)置為 true 并重新綁定該控件。該操作的其余部分在內(nèi)部發(fā)生。(稍后我將描述此過程的細(xì)節(jié)。)新行將在編輯模式下繪制,InitRow 事件將激發(fā),其目的只是使您有機(jī)會(huì)將某些字段設(shè)置為默認(rèn)值。

UpdateView 的角色不與更新操作緊密相關(guān)。DataGrid 控件不緩存數(shù)據(jù)源,因此,無論頁面何時(shí)回發(fā)(以便進(jìn)行分頁、排序、編輯、插入或刪除),您都需要重新綁定。為了簡化編碼并盡可能多地嵌入編碼,添加了這個(gè)新事件。當(dāng)該網(wǎng)格需要設(shè)置其 DataSource 屬性時(shí),“更新視圖” 就會(huì)激發(fā)!案乱晥D” 的典型處理程序?qū)?dāng)前的數(shù)據(jù)源分配給該屬性并調(diào)用 DataBind 方法。

當(dāng)相應(yīng)的 SQL 語句不能進(jìn)一步延遲執(zhí)行時(shí),就會(huì)激發(fā)其他三個(gè)事件 — “保存數(shù)據(jù)”、“插入數(shù)據(jù)” 和 “刪除數(shù)據(jù)”。在處理其中的任何事件時(shí),可設(shè)置和執(zhí)行 “更新”、“插入” 或 “刪除” 語句。您負(fù)責(zé)檢索更新后的數(shù)據(jù),并準(zhǔn)備和執(zhí)行該命令。除了 “插入數(shù)據(jù)”(與 DataGrid 編程接口沒有緊密關(guān)系)以外,“保存數(shù)據(jù)” 和 “刪除數(shù)據(jù)” 也不同于標(biāo)準(zhǔn)的 UpdateCommand 和 DeleteCommand,這是因?yàn)樗鼈兏唧w而且只要求您執(zhí)行 SQL 代碼。新事件基本上由 UpdateCommand 和 DeleteCommand 的處理程序激發(fā),這些處理程序是 EditableGrid 控件在加載時(shí)以靜默方式定義的。這些內(nèi)部處理程序負(fù)責(zé)執(zhí)行所有其他任務(wù)(例如,重置索引)并刷新視圖。后者(即刷新視圖)是通過激發(fā) UpdateView 事件來完成的。

設(shè)置控件

讓我們快速瀏覽 EditableGrid 類。該類的構(gòu)造函數(shù)初始化某些公共屬性和受保護(hù)的屬性,并為基類的幾個(gè)事件設(shè)置默認(rèn)處理程序。

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 類有一個(gè)尚未提到的公共屬性 — AllowFullEditing。該屬性成員支持對(duì)網(wǎng)格的完全編輯功能。如果您將該屬性設(shè)置為 false,則該控件將不提供就地編輯或插入功能。究竟執(zhí)行的是怎樣的處理?該控件自動(dòng)將 AllowPaging 設(shè)置為 true,并為 PageIndexChanged 提供一個(gè)處理程序。這意味著 EditableGrid 還是比 DataGrid 控件好一些,因?yàn)樗鼮槟峁┳詣?dòng)的空閑分頁。

當(dāng) AllowFullEditing 設(shè)置為 true(默認(rèn)值)時(shí),EditableGrid 控件自動(dòng)將兩個(gè)新列追加到網(wǎng)格中。第一列是 EditCommandColumn,它提供就地編輯功能。第二列是 ButtonColumn,它的命令是 DELETE。這兩列都是通過為響應(yīng) Init 事件而運(yùn)行的處理程序來添加的。

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 確定用來觸發(fā)編輯和刪除操作的按鈕的文本。它們?cè)谀J(rèn)時(shí)分別為 “編輯”、“確定”、“取消” 和 “刪除”。

在將該控件插入到任何 ASP.NET 頁中之前,必須按如下方式注冊(cè)它:

<%@Register TagPrefix="expo" Namespace="BWSLib" Assembly="EditableGrid" %>

在上面的聲明中,可以更改 TagPrefix 屬性的內(nèi)容,但是,除了該控件的命名空間和類名可以修改以外,不能更改任何其他內(nèi)容。下面的代碼顯示如何在 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>
示例頁從一個(gè)名為 Employees 的 Microsoft Access 2000 表讀出數(shù)據(jù)并將生成的 DataSet(數(shù)據(jù)集)存儲(chǔ)在 Cache 對(duì)象中。

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 顯示該頁中控件的輸出結(jié)果。



圖 1. 操作中的 EditableGrid 控件

添加和刪除列時(shí)無需任何額外的代碼?墒,必須要指定 UpdateView 處理程序。但是,正如您可以看到的那樣,這是一段必須在多個(gè)位置與 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();
}

在上面的控件聲明中,還可以看到三個(gè)與數(shù)據(jù)相關(guān)的事件的事件處理程序。讓我們看一下該控件如何處理它們。

Insert 操作

EditableGrid 控件不包括任何用來啟動(dòng) “插入” 操作的用戶界面元素。正如對(duì)于其他重要功能一樣,程序員負(fù)責(zé)將能夠觸發(fā)網(wǎng)格上給定事件的超級(jí)鏈接或按鈕放到頁面中。由于添加新項(xiàng)目不是依賴特定行的操作,因此建議您不要使用特殊的命令列(例如,“編輯” 或 “刪除”)?蛻舳舜a處理頁面級(jí)事件,并設(shè)置 AddNewRow 屬性以響應(yīng)該操作。接著,頁面刷新網(wǎng)格,以反映所做的更改。例如,圖 1 中的 Insert(插入)按鈕鏈接到下面的 on-click 處理程序:

public void OnInsert(Object sender, EventArgs e)
{
grid.AddNewRow = true;
UpdateDataView();
}

“更新數(shù)據(jù)視圖” 是頁面級(jí)過程,它處理網(wǎng)格數(shù)據(jù)綁定并設(shè)置 “數(shù)據(jù)源” 屬性。

設(shè)計(jì)的思路是使用戶不直接將新記錄添加到數(shù)據(jù)源中,而是只聲明其想要實(shí)現(xiàn)的目標(biāo)。因此,在設(shè)置 EditableGrid 控件的 “數(shù)據(jù)源” 屬性時(shí),該控件會(huì)檢查 AddNewRow 的值。在派生的控件中,因?yàn)橛卸喾N訪問器,所以可以很方便地確定何時(shí)讀取或?qū)懭虢o定屬性。這樣做之后,EditableGrid 會(huì)按如下方式重寫 “數(shù)據(jù)源” 屬性:

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...
:
}
}
}

在設(shè)置 “數(shù)據(jù)源” 時(shí),只要 AddNewRow 屬性為 true,就會(huì)追加一個(gè)新行。InsertNewRow 就是用來為插入和行編輯功能設(shè)置網(wǎng)格的內(nèi)部過程。此處進(jìn)行的重要假設(shè)就是網(wǎng)格與 “數(shù)據(jù)視圖” 對(duì)象綁定。利用該代碼,您可以方便地推廣到更普遍的解決方案。

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
}
在創(chuàng)建基礎(chǔ) “數(shù)據(jù)表” 對(duì)象以后,使用 NewRow 方法添加新行,并激發(fā)自定義的 InitRow 事件,以便用戶有機(jī)會(huì)將某些字段設(shè)置為默認(rèn)值。為了實(shí)現(xiàn)其目標(biāo),InitRow 需要將某些數(shù)據(jù)向下傳遞到事件處理程序并接收任何更新。必須安排一個(gè)自定義的事件數(shù)據(jù)結(jié)構(gòu)和一個(gè)自定義的事件處理程序簽名。DataGridInitRowEventArgs 是事件數(shù)據(jù)結(jié)構(gòu)的名稱,而 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 并通過簡單地添加一個(gè) DataRow 成員進(jìn)行擴(kuò)展。

public sealed class DataGridInitRowEventArgs : EventArgs
{
// Represents the row being added. You can use this object
// to set fields to default values.
public DataRow Row;
}

在激發(fā) InitRow 事件之前,該控件用新創(chuàng)建(但仍為空)的 DataRow 對(duì)象的實(shí)時(shí)實(shí)例填充 Row 成員。

DataGridInitRowEventArgs dgire = new DataGridInitRowEventArgs();
dgire.Row = row;
OnInitRow(dgire);

如果需要設(shè)置某些默認(rèn)值,則可以編寫 InitRow 處理程序并設(shè)置 DataRow 對(duì)象的字段。

public void InitRow(Object sender, DataGridInitRowEventArgs e)
{
e.Row["LastName"] = "Esposito";
}

此處,DataSource 額外有一行可模擬新記錄的插入操作。該行已經(jīng)添加到數(shù)據(jù)源的底部。因此,您應(yīng)當(dāng)使網(wǎng)格指向它的最后一頁,而且還要考慮到新行進(jìn)入新頁的可能性。例如,如果表中有八個(gè)記錄,而且您使用的頁面大小為四,則在添加新記錄時(shí)還需要增加一個(gè)新頁。為了使用戶能夠編輯新行的內(nèi)容,只需將 DataGrid 的 EditItemIndex 屬性設(shè)置為新行的頁索引。InsertNewRow 完成的最后一步是設(shè)置內(nèi)部數(shù)據(jù)成員,來跟蹤向表中添加新的未提交行的操作。

簡言之,該代碼將一個(gè)空行對(duì)象添加到網(wǎng)格的數(shù)據(jù)源中。該行代表 “數(shù)據(jù)表” 對(duì)象的待定更改,而且如果用戶取消該操作,則必須拒絕該行。如果用戶移到另一頁或者決定單擊和編輯同一頁上的另一行,也必須拒絕該行。拒絕待定更改是內(nèi)置處理程序(例如,PageIndexChanged、EditCommand 和 CancelCommand)通過內(nèi)部數(shù)據(jù)成員 RejectChanges 完成的事情之一。(有關(guān)更多詳細(xì)信息,請(qǐng)參閱源代碼。)

通過將新行置于編輯模式,即可完成插入階段,從而進(jìn)入更新階段,如下圖所示。



圖 2. 插入新行

對(duì)于 InsertNewRow 的主體,我還需要闡明另外一個(gè)方面。在獲取數(shù)據(jù)源之后,該方法立即確保沒有已添加但尚未提交的行。在設(shè)計(jì)上,最多可以有一個(gè)待定更改,而且如果該代碼重新進(jìn)入有一個(gè)已添加的行的過程,那是由于用戶刷新了該頁面。為了避免反復(fù)添加空白行和未提交的行,程序流會(huì)跳出代碼塊。

Update 操作

請(qǐng)注意,在圖 2 中看不到可在圖 1 中看到的 Delete 列。為了簡化界面,我決定在任何行進(jìn)入編輯模式時(shí)隱藏 Delete 列。并且因?yàn)橛辛艘郧疤岬降膬?nèi)置事件處理程序,所以此行為在 EditableGrid 類中是硬編碼的。

update 操作是由以下三個(gè)事件執(zhí)行的:

&#8226; EditCommand — 開始該操作并以編輯模式呈現(xiàn)行

&#8226; UpdateCommand — 保存所做的更改并還原默認(rèn)用戶界面

&#8226; CancelCommand — 取消所做的更改并還原以前的用戶界面


用于 EditCommand 和 CancelCommand 的典型代碼是能夠方便地嵌入類中的傳統(tǒng) vanilla 代碼。一般來說,此處沒有真正需要在頁面級(jí)解決的內(nèi)容,但是有時(shí)情況也可能與您的特定實(shí)例有所不同。

“更新” 操作的應(yīng)用程序特定的邏輯集中在 “更新命令” 事件中。除了保存任何更改以外,您應(yīng)當(dāng)還原網(wǎng)格的一致狀態(tài)(取消編輯模式、還原 Delete 列和拒絕待定的更改),并確保所做的更改隨后由用于顯示的數(shù)據(jù)源反映出來。如果您像本例中那樣使用緩存數(shù)據(jù),則后面的一點(diǎn)至關(guān)重要。

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 處理程序取消當(dāng)前行的編輯模式,然后打開 Delete 列的可見性標(biāo)志。此時(shí),該表可能有一個(gè)待定的更改。因?yàn)?“更新命令” 事件會(huì)在兩種情況(打算將所做的更改保存到現(xiàn)有的行中時(shí);要插入新行時(shí))下激發(fā),所以條件窗體很有意義。內(nèi)部成員 MustInsertRow 由 InsertNewRow 設(shè)置并由 DataSource 重置,它有助于確定是哪種情況。當(dāng)代碼處理完網(wǎng)格的狀態(tài)之后,它激發(fā)兩個(gè)連續(xù)事件 — 一個(gè)事件讓頁面保存或插入到物理數(shù)據(jù)源中,另一個(gè)事件刷新數(shù)據(jù)綁定。這就解釋了為何 InsertData 和 SaveData 處理程序只能執(zhí)行主要通過 SQL 語句來進(jìn)行的物理更新。

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);
}

在本文中討論的示例代碼中,設(shè)置了幾個(gè)假設(shè),其中一個(gè)就是假設(shè)網(wǎng)格的數(shù)據(jù)源是 “數(shù)據(jù)視圖” 對(duì)象。這間接表示不支持自定義分頁,或者,更確切地講,必須仔細(xì)修改此代碼才能處理這樣的網(wǎng)格。這同樣適用于排序。

所作的第二個(gè)重要假設(shè)是有關(guān)使用 SQL 語句進(jìn)行更新的。

設(shè)計(jì)思路是,無論進(jìn)行了什么樣的更改,總是激發(fā)單個(gè) SQL 語句以應(yīng)用它。此處設(shè)計(jì)的 EditableGrid 不能正確地處理批更新。順便提一句,在我的 Building Web Solutions with ASP.NET and ADO.NET 一書中,在介紹就地編輯時(shí),更詳細(xì)地討論了對(duì)于網(wǎng)格使用批更新的優(yōu)缺點(diǎn)。然而,如果您對(duì)使用批更新技術(shù)感興趣,請(qǐng)給我發(fā)送電子郵件,我將在以后的專欄中介紹此主題。

由于您通過直接的 SQL 語句(或者數(shù)據(jù)源識(shí)別為直接語句的內(nèi)容)進(jìn)行更新,因此,“更新命令” 事件可以成功地命令拒絕任何更改。這就是批更新方案中的主要區(qū)別。

“保存數(shù)據(jù)” 和 “插入數(shù)據(jù)” 事件代表執(zhí)行更新所必需的任務(wù)的子集。在執(zhí)行該命令之后,這些處理程序必須確保網(wǎng)格可以訪問和顯示刷新數(shù)據(jù)。在這種情況下,必須更新數(shù)據(jù)的緩存副本。根據(jù)基礎(chǔ)數(shù)據(jù)庫架構(gòu)(任何觸發(fā)器或任何自動(dòng)編號(hào)的字段),可以決定是完全重新讀取還是基于每個(gè)字段更新緩存數(shù)據(jù)。

讓我們花些時(shí)間來了解如何從網(wǎng)格的編輯模板來檢索更新后的數(shù)據(jù)。我考慮使 EditableGrid 控件具有足夠的智能,以便從單元格提取值,并將它們填充到 DataRow 對(duì)象中以充當(dāng)事件數(shù)據(jù)。這個(gè)方法使得在批方案中更新代碼變得微不足道。之所以決定讓 ASP.NET 頁負(fù)責(zé)提取數(shù)據(jù),是因?yàn)檫@樣您也可以透明地支持編輯模板。下面我將向您展示在不使用任何特殊模板時(shí)所必需的代碼。

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] 表達(dá)式,其中n 是該列的索引(從 0 開始)。請(qǐng)記住,DataGrid 的就地編輯功能允許您通過將 “只讀” 屬性設(shè)置為 true 來將列視為只讀。

Delete 操作

Delete 操作的工作方式與 Insert 和 Update 操作大體相同。自動(dòng)創(chuàng)建的列有一個(gè)命令名 Delete,在單擊該按鈕時(shí)會(huì)導(dǎo)致激發(fā) ± 事件。內(nèi)置的處理程序修復(fù)網(wǎng)格的界面,然后依次先后激發(fā) DeleteData 和 UpdateView。

由于刪除操作的入侵性比插入或更新操作的強(qiáng),因此您可能希望使用某個(gè)客戶端腳本代碼,并要求用戶在繼續(xù)之前確認(rèn)。我在 last month's column 的“對(duì)話欄”部分中討論了該方法。在本月的源代碼中,有一個(gè)如下圖所示的實(shí)際實(shí)現(xiàn)。



圖 3. 在刪除之前確認(rèn)

最終的優(yōu)化

我提到過 EditableGrid 控件支持編輯模板。讓我來證明這一結(jié)論。在示例代碼的第 2 步,我使用該網(wǎng)格中一組稍有不同的列。

<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>
正如您可以看到的那樣,有一個(gè)模板列,它在單個(gè)字段中顯示名和姓。此列的編輯模板(您必須指定能夠編輯該列的編輯模板)由兩個(gè)并排的文本框提供。無需在類中進(jìn)行任何更改,即可獲得圖 4 中所示的示例。



圖 4. 使用編輯模板

另一方面,在使用從文本框中檢索更新的文本的方式時(shí),編輯模板需要進(jìn)行少許調(diào)整,F(xiàn)在,可以按名稱檢索模板中的控件。之所以可以并且建議這樣做,是因?yàn)槟揽丶?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];

在本月的源代碼中,您將發(fā)現(xiàn)該類的 C# 源代碼、兩個(gè)示例 ASP.NET 頁以及我用過的 Access 數(shù)據(jù)庫。但是,您將找不到編譯過的程序集。我提供了一個(gè)(非常)簡單的批文件,您可以使用它,針對(duì)與 beta 2 及其更高版本兼容的任何版本的 .NET CLR,將該類編譯成為可執(zhí)行代碼。

注 如果您在使下載正確運(yùn)行時(shí)遇到問題,請(qǐng)檢查下列步驟:

&#8226; 1.將 CS 類編譯成為程序集。這可通過打開一個(gè) DOS 框并運(yùn)行 ZIP 中的 c.bat 批處理文件來完成,或者通過在 Visual Studio 中創(chuàng)建一個(gè)新的類庫項(xiàng)目并將類文件添加到這個(gè)空白項(xiàng)目中來實(shí)現(xiàn)。

&#8226; 2.必須使程序集對(duì)于示例 ASPX 頁可用。下載文件中包括的批處理文件將該 DLL 復(fù)制到 c:\inetpub\wwwroot\bin 文件夾中。如果您恰好有一個(gè)不同的路徑,請(qǐng)對(duì)它進(jìn)行修改。如果您創(chuàng)建一個(gè)虛擬目錄,請(qǐng)確保將該 DLL 復(fù)制到虛擬文件夾的 BIN 子文件夾中,而不要復(fù)制到 Web 服務(wù)器根的 BIN 文件夾中。

&#8226; 3.根據(jù) ASP.NET 安全設(shè)置,在與示例 Microsoft Access 數(shù)據(jù)庫交互時(shí),可能會(huì)遇到難以處理的 Updateable Query 錯(cuò)誤。在這種情況下,請(qǐng)更改示例 .MDB 文件的安全設(shè)置。在“Windows 資源管理器”中選擇該文件,顯示屬性,然后單擊安全選項(xiàng)卡。接著,添加 \ASPNET 用戶并確保它有權(quán)寫入和修改該文件。

&#8226; 4.刷新 ASPX 頁面。


對(duì)話欄:創(chuàng)建自定義模板

在上一個(gè)專欄中,您討論了 Summary 網(wǎng)格組件。是否有機(jī)會(huì)創(chuàng)建或加載匯總行的自定義模板?這將允許在 ASPX 文件中(而非代碼中)規(guī)定設(shè)計(jì)的規(guī)范。

使用匯總行的自定義模板無疑將是可能的。問題只是在于如何實(shí)現(xiàn)?或者,更準(zhǔn)確地說,哪種方法最簡單?我將在下面介紹幾種可能的方法。

&#8226; 使用 pagelet: 可以修改應(yīng)用程序的代碼,使其動(dòng)態(tài)加載 pagelet 控件(又稱用戶控件) — 即 ASCX 文件。ASCX 文件看上去像一個(gè)小型 Web 窗體,而且其中的大部分內(nèi)容都是布局信息。可以使用 Page.LoadControl 來按名稱加載 ASCX 文件,然后將它添加到 DataGridItem 對(duì)象的某個(gè)單元格的 Controls 集合中。

&#8226; 模板列:DataGrid 的所有列都是模板化列。使用 ASP.NET 標(biāo)記和各處的一些代碼定義每一列的 。您能夠執(zhí)行的操作就是將 塊的結(jié)構(gòu)分成兩個(gè)截然不同且互斥的部分 — 一部分用于普通行,另一部分用于匯總行。為了確保一次只顯示一行,可以處理控件的 “可見” 屬性并將該屬性與界定匯總行或普通行的條件綁定。

&#8226; 編寫新控件:從 DataGrid 派生一個(gè)新類并添加一個(gè)新的模板屬性。這允許您使用自定義的子標(biāo)記、按照與輸入列模板幾乎相同的方式輸入?yún)R總行布局。


返回頁首