原文地址:
http://www.codeproject.com/useritems/EditGridviewCells.asp[原文源碼下載][譯者改后源碼下載][翻譯]在GridView中針對鼠標單擊的某一獨立單元格進行編輯
原文發布日期:2007.04.07
作者:
Declan Bright翻譯:
webabcd介紹ASP.NET的GridView控件允許你通過設置它的EditIndex屬性來編輯數據行,此時整個數據行都處于編輯模式。 如果你在EditItemTemplate的一些列中使用了DropDownList控件,那么你也許不希望整個數據行都處于編輯模式。 因為,如果每一個DropDownList控件都有很多選項的話,那么一次加載所有DropDownList控件的所有選項就會導致頁面執行緩慢。
另外,如果你的數據行的編輯模式需要占用更多的空間的話,那么針對每一個獨立的單元格進行編輯要優于針對整個數據行進行編輯。 這里,我將示范如何實現這樣的功能,又如何去處理事件驗證(event validation)。
背景本文基于我之前寫的一篇文章:
GridView和DataList響應單擊數據行和雙擊數據行事件。如果你不知道如何讓GridView響應單擊數據行事件,那么你可以在閱讀本文之前先看看這篇文章。
編輯某一個獨立的GridView單元格。
我所演示的這個GridView有一個不可見的asp:ButtonField控件,它處于GridView的第一列,名為“SingleClick”。 它用于給GridView的數據行增加單擊事件。
<Columns>
<asp:ButtonField Text="SingleClick" CommandName="SingleClick" Visible="False" />
</Columns>
其它每一列的ItemTemplate中有一個可見的Label控件和一個不可見的TextBox或DropDownList控件。 為了方便,我們稱Label為顯示控件,TextBox或DropDownList為編輯控件。
<asp:TemplateField HeaderText="Task">
<ItemTemplate>
<asp:Label ID="DescriptionLabel" runat="server" Text='<%# Eval("Description") %>'></asp:Label>
<asp:TextBox ID="Description" runat="server" Text='<%# Eval("Description") %>' Width="175px" visible="false"></asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
這里的辦法就是用顯示控件來顯示數據,當單元格所包含的顯示控件被單擊的時候,則把顯示控件的Visible屬性設置為false并且把編輯控件的Visible屬性設置為true。 這里不用使用EditItemTemplat。
在RowDataBound事件內循環為每一數據行的每一單元格增加單擊事件。 使用單元格在數據行中的索引作為事件參數,這樣在單元格觸發了單擊事件后我們就可以知道到底是哪個單元格被單擊了。
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
// 從第一個單元格內獲得LinkButton控件
LinkButton _singleClickButton = (LinkButton)e.Row.Cells[0].Controls[0];
// 返回一個字符串,表示對包含目標控件的 ID 和事件參數的回發函數的 JavaScript 調用
string _jsSingle = ClientScript.GetPostBackClientHyperlink(_singleClickButton, "");
// 給每一個可編輯的單元格增加事件
for (int columnIndex = _firstEditCellIndex; columnIndex < e.Row.Cells.Count; columnIndex++)
{
// 增加列索引作為事件參數
string js = _jsSingle.Insert(_jsSingle.Length - 2, columnIndex.ToString());
// 給單元格增加onclick事件
e.Row.Cells[columnIndex].Attributes["onclick"] = js;
// 給單元格增加鼠標經過時指針樣式
e.Row.Cells[columnIndex].Attributes["style"] += "cursor:pointer;cursor:hand;";
}
}
}
在RowCommand事件內讀出命令參數和事件參數。 這會告訴我們被選中的行和列的索引。
int _rowIndex = int.Parse(e.CommandArgument.ToString());
int _columnIndex = int.Parse(Request.Form["__EVENTARGUMENT"]);
因為知道了被選中的行和列的索引,所以可以通過把顯示控件的Visible設置為false,編輯控件的Visible設置為true來把某個獨立的單元格設置為編輯模式。 然后通過清除單元格的屬性來刪除被選中單元格的單擊事件。
// 獲得被選中單元格的顯示控件并設置其不可見
Control _displayControl = _gridView.Rows[_rowIndex].Cells[_columnIndex].Controls[1];
_displayControl.Visible = false;
// 獲得被選中單元格的編輯控件并設置其可見
Control _editControl = _gridView.Rows[_rowIndex].Cells[_columnIndex].Controls[3];
_editControl.Visible = true;
// 清除被選中單元格屬性以刪除click事件
_gridView.Rows[_rowIndex].Cells[_columnIndex].Attributes.Clear();
下面有一些代碼用于回發服務器后設置焦點到編輯控件,如果編輯控件是DropDownList的話,那么它的SelectedValue要設置為顯示控件的值,如果編輯控件是TextBox的話,那么為了做好編輯的準備就要使它的文本被選中。
// 設置焦點到被選中的編輯控件
ClientScript.RegisterStartupScript(GetType(), "SetFocus",
"<script>document.getElementById('" + _editControl.ClientID + "').focus();</script>");
// 如果編輯控件是DropDownList的話
// SelectedValue設置為顯示控件的值
if (_editControl is DropDownList && _displayControl is Label)
{
((DropDownList)_editControl).SelectedValue = ((Label)_displayControl).Text;
}
// 如果編輯控件是TextBox的話則選中文本框內文本
if (_editControl is TextBox)
{
((TextBox)_editControl).Attributes.Add("onfocus", "this.select()");
}
在這個Demo中,我把事件被觸發的歷史記錄也寫到了頁里。
如果GridView處于編輯模式的話,那么要在RowUpdating事件里去查找被選中行的每一個單元格。 如果發現單元格處于編輯模式的話,那么就調用“更新”代碼。 在這個Demo中,數據保存在DataTable里,而這個DataTable則儲存在session中。
// 循環每一列以找到處于編輯模式下的單元格
for (int i = 1; i < _gridView.Columns.Count; i++)
{
// 獲得單元格的編輯控件
Control _editControl = _gridView.Rows[e.RowIndex].Cells[i].Controls[3];
if (_editControl.Visible)
{
. update the data
}
}
為了確保RowUpdating事件在編輯單元格后被激發,要在Page_Load中來觸發這個事件。 編輯了TextBox后,通過按回車鍵或者單擊另一單元格來使頁面做回發處理,下面的這段代碼就是用于確保任何數據的改變都會被更新。
if (this.GridView1.SelectedIndex > -1)
{
this.GridView1.UpdateRow(this.GridView1.SelectedIndex, false);
}
為了驗證而注冊回發和回調數據在RowDataBound中創建的自定義事件必須要在頁中注冊。 通過重寫Render方法來調用ClientScriptManager.RegisterForEventValidation。 通過GridViewRow.UniqueID返回行的唯一ID,按紐的唯一ID通過在行的唯一ID后附加“$ct100”而生成。
protected override void Render(HtmlTextWriter writer)
{
foreach (GridViewRow r in GridView1.Rows)
{
if (r.RowType == DataControlRowType.DataRow)
{
for (int columnIndex = _firstEditCellIndex; columnIndex < r.Cells.Count; columnIndex++)
{
Page.ClientScript.RegisterForEventValidation(r.UniqueID + "$ctl00", columnIndex.ToString());
}
}
}
base.Render(writer);
}
這將防止任何“回發或回調參數無效”的錯誤。
這個Demo中的其它示例使用SQL數據源控件編輯某一獨立的GridView單元格
用SqlDataSouce控件實現這個技術需要對GridView的RowUpdating事件做一些修改。 當更新GridView的行的時候,SqlDataSource控件一般要把值(values)從EditItemTemplate轉移到NewValues集合里。 因為我們沒有使用EditItemTemplate,所以這種情況下值(values)不會自動地轉移到NewValues集合里。
e.NewValues.Add(key, value);
我在App_Data文件夾下使用了一個簡單的SQL Server Express數據庫。 (要使用你自己的數據庫的話,你可以修改web.config里的連接字符串)
使用對象數據源控件編輯某一獨立的GridView單元格
本示例使用了App_Code文件夾內的兩個類:
·Task.cs – 任務對象
·TaskDataAccess.cs – 管理任務對象
Aspx頁的后置代碼與SQL Data Source示例是一樣的。 ObjectDataSource通過TaskDataAccess.cs類里的GetTasks和UpdateTask方法來管理數據。
有著電子數據表樣式的GridView這里有一個與電子數據表的樣式很像的GridView。 (雖然它看起來像一個電子數據表,但是并不是真的有像電子數據表一樣的功能,它仍然是一個GridView。)
這里雖然有一些單擊后改變單元格樣式的附加代碼,但是主要的代碼還是與上面所述是相同的。

用SQL數據源控件實現有著電子數據表樣式的GridView
本示例與上面的基本相同,但是它修改了GridView的RowUpdating事件以使其允許用SqlDataSource控件來工作。
參考 ·
GridView和DataList響應單擊數據行和雙擊數據行事件 ·
ASP.NET 2.0數據教程結論如果你想在GridView中一次只針對一個單元格進行編輯,那么這個方法將會對你有所幫助。
譯者注:事件驗證(EventValidation)。出于安全目的,此功能驗證回發或回調事件的參數是否來源于最初呈現這些事件的服務器控件。如果數據有效并且是預期的,則使用ClientScriptManager.RegisterForEventValidation方法來注冊回發或回調數據以進行驗證。
點擊下載此文件