在GridView中針對鼠標單擊的某一獨立單元格進行編輯
Posted on 2007-07-13 16:35 停留的風 閱讀(5386) 評論(2) 編輯 收藏 所屬分類: .NET技巧特輯 、C#學習歷程原文地址:
http://www.codeproject.com/useritems/EditGridviewCells.asp
[原文源碼下載]
[譯者改后源碼下載]
[翻譯]在GridView中針對鼠標單擊的某一獨立單元格進行編輯
原文發(fā)布日期:2007.04.07
作者:Declan Bright
翻譯:webabcd
介紹
ASP.NET的GridView控件允許你通過設置它的EditIndex屬性來編輯數(shù)據(jù)行,此時整個數(shù)據(jù)行都處于編輯模式。 如果你在EditItemTemplate的一些列中使用了DropDownList控件,那么你也許不希望整個數(shù)據(jù)行都處于編輯模式。 因為,如果每一個DropDownList控件都有很多選項的話,那么一次加載所有DropDownList控件的所有選項就會導致頁面執(zhí)行緩慢。
另外,如果你的數(shù)據(jù)行的編輯模式需要占用更多的空間的話,那么針對每一個獨立的單元格進行編輯要優(yōu)于針對整個數(shù)據(jù)行進行編輯。 這里,我將示范如何實現(xiàn)這樣的功能,又如何去處理事件驗證(event validation)。
背景
本文基于我之前寫的一篇文章:GridView和DataList響應單擊數(shù)據(jù)行和雙擊數(shù)據(jù)行事件。如果你不知道如何讓GridView響應單擊數(shù)據(jù)行事件,那么你可以在閱讀本文之前先看看這篇文章。
編輯某一個獨立的GridView單元格。

我所演示的這個GridView有一個不可見的asp:ButtonField控件,它處于GridView的第一列,名為“SingleClick”。 它用于給GridView的數(shù)據(jù)行增加單擊事件。
其它每一列的ItemTemplate中有一個可見的Label控件和一個不可見的TextBox或DropDownList控件。 為了方便,我們稱Label為顯示控件,TextBox或DropDownList為編輯控件。
這里的辦法就是用顯示控件來顯示數(shù)據(jù),當單元格所包含的顯示控件被單擊的時候,則把顯示控件的Visible屬性設置為false并且把編輯控件的Visible屬性設置為true。 這里不用使用EditItemTemplat。
在RowDataBound事件內(nèi)循環(huán)為每一數(shù)據(jù)行的每一單元格增加單擊事件。 使用單元格在數(shù)據(jù)行中的索引作為事件參數(shù),這樣在單元格觸發(fā)了單擊事件后我們就可以知道到底是哪個單元格被單擊了。
在RowCommand事件內(nèi)讀出命令參數(shù)和事件參數(shù)。 這會告訴我們被選中的行和列的索引。
因為知道了被選中的行和列的索引,所以可以通過把顯示控件的Visible設置為false,編輯控件的Visible設置為true來把某個獨立的單元格設置為編輯模式。 然后通過清除單元格的屬性來刪除被選中單元格的單擊事件。
下面有一些代碼用于回發(fā)服務器后設置焦點到編輯控件,如果編輯控件是DropDownList的話,那么它的SelectedValue要設置為顯示控件的值,如果編輯控件是TextBox的話,那么為了做好編輯的準備就要使它的文本被選中。
在這個Demo中,我把事件被觸發(fā)的歷史記錄也寫到了頁里。
如果GridView處于編輯模式的話,那么要在RowUpdating事件里去查找被選中行的每一個單元格。 如果發(fā)現(xiàn)單元格處于編輯模式的話,那么就調(diào)用“更新”代碼。 在這個Demo中,數(shù)據(jù)保存在DataTable里,而這個DataTable則儲存在session中。
為了確保RowUpdating事件在編輯單元格后被激發(fā),要在Page_Load中來觸發(fā)這個事件。 編輯了TextBox后,通過按回車鍵或者單擊另一單元格來使頁面做回發(fā)處理,下面的這段代碼就是用于確保任何數(shù)據(jù)的改變都會被更新。
為了驗證而注冊回發(fā)和回調(diào)數(shù)據(jù)
在RowDataBound中創(chuàng)建的自定義事件必須要在頁中注冊。 通過重寫Render方法來調(diào)用ClientScriptManager.RegisterForEventValidation。 通過GridViewRow.UniqueID返回行的唯一ID,按紐的唯一ID通過在行的唯一ID后附加“$ct100”而生成。
這將防止任何“回發(fā)或回調(diào)參數(shù)無效”的錯誤。
這個Demo中的其它示例
使用SQL數(shù)據(jù)源控件編輯某一獨立的GridView單元格
用SqlDataSouce控件實現(xiàn)這個技術需要對GridView的RowUpdating事件做一些修改。 當更新GridView的行的時候,SqlDataSource控件一般要把值(values)從EditItemTemplate轉(zhuǎn)移到NewValues集合里。 因為我們沒有使用EditItemTemplate,所以這種情況下值(values)不會自動地轉(zhuǎn)移到NewValues集合里。
e.NewValues.Add(key, value);
我在App_Data文件夾下使用了一個簡單的SQL Server Express數(shù)據(jù)庫。 (要使用你自己的數(shù)據(jù)庫的話,你可以修改web.config里的連接字符串)
使用對象數(shù)據(jù)源控件編輯某一獨立的GridView單元格
本示例使用了App_Code文件夾內(nèi)的兩個類:
·Task.cs – 任務對象
·TaskDataAccess.cs – 管理任務對象
Aspx頁的后置代碼與SQL Data Source示例是一樣的。 ObjectDataSource通過TaskDataAccess.cs類里的GetTasks和UpdateTask方法來管理數(shù)據(jù)。
有著電子數(shù)據(jù)表樣式的GridView
這里有一個與電子數(shù)據(jù)表的樣式很像的GridView。 (雖然它看起來像一個電子數(shù)據(jù)表,但是并不是真的有像電子數(shù)據(jù)表一樣的功能,它仍然是一個GridView。)
這里雖然有一些單擊后改變單元格樣式的附加代碼,但是主要的代碼還是與上面所述是相同的。

用SQL數(shù)據(jù)源控件實現(xiàn)有著電子數(shù)據(jù)表樣式的GridView
本示例與上面的基本相同,但是它修改了GridView的RowUpdating事件以使其允許用SqlDataSource控件來工作。
參考
·GridView和DataList響應單擊數(shù)據(jù)行和雙擊數(shù)據(jù)行事件
·ASP.NET 2.0數(shù)據(jù)教程
結論
如果你想在GridView中一次只針對一個單元格進行編輯,那么這個方法將會對你有所幫助。
譯者注:事件驗證(EventValidation)。出于安全目的,此功能驗證回發(fā)或回調(diào)事件的參數(shù)是否來源于最初呈現(xiàn)這些事件的服務器控件。如果數(shù)據(jù)有效并且是預期的,則使用ClientScriptManager.RegisterForEventValidation方法來注冊回發(fā)或回調(diào)數(shù)據(jù)以進行驗證。
點擊下載此文件
http://www.codeproject.com/useritems/EditGridviewCells.asp
[原文源碼下載]
[譯者改后源碼下載]
[翻譯]在GridView中針對鼠標單擊的某一獨立單元格進行編輯
原文發(fā)布日期:2007.04.07
作者:Declan Bright
翻譯:webabcd
介紹
ASP.NET的GridView控件允許你通過設置它的EditIndex屬性來編輯數(shù)據(jù)行,此時整個數(shù)據(jù)行都處于編輯模式。 如果你在EditItemTemplate的一些列中使用了DropDownList控件,那么你也許不希望整個數(shù)據(jù)行都處于編輯模式。 因為,如果每一個DropDownList控件都有很多選項的話,那么一次加載所有DropDownList控件的所有選項就會導致頁面執(zhí)行緩慢。
另外,如果你的數(shù)據(jù)行的編輯模式需要占用更多的空間的話,那么針對每一個獨立的單元格進行編輯要優(yōu)于針對整個數(shù)據(jù)行進行編輯。 這里,我將示范如何實現(xiàn)這樣的功能,又如何去處理事件驗證(event validation)。
背景
本文基于我之前寫的一篇文章:GridView和DataList響應單擊數(shù)據(jù)行和雙擊數(shù)據(jù)行事件。如果你不知道如何讓GridView響應單擊數(shù)據(jù)行事件,那么你可以在閱讀本文之前先看看這篇文章。
編輯某一個獨立的GridView單元格。

我所演示的這個GridView有一個不可見的asp:ButtonField控件,它處于GridView的第一列,名為“SingleClick”。 它用于給GridView的數(shù)據(jù)行增加單擊事件。
<Columns>
<asp:ButtonField Text="SingleClick" CommandName="SingleClick" Visible="False" />
</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>
<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>
這里的辦法就是用顯示控件來顯示數(shù)據(jù),當單元格所包含的顯示控件被單擊的時候,則把顯示控件的Visible屬性設置為false并且把編輯控件的Visible屬性設置為true。 這里不用使用EditItemTemplat。
在RowDataBound事件內(nèi)循環(huán)為每一數(shù)據(jù)行的每一單元格增加單擊事件。 使用單元格在數(shù)據(jù)行中的索引作為事件參數(shù),這樣在單元格觸發(fā)了單擊事件后我們就可以知道到底是哪個單元格被單擊了。
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
// 從第一個單元格內(nèi)獲得LinkButton控件
LinkButton _singleClickButton = (LinkButton)e.Row.Cells[0].Controls[0];
// 返回一個字符串,表示對包含目標控件的 ID 和事件參數(shù)的回發(fā)函數(shù)的 JavaScript 調(diào)用
string _jsSingle = ClientScript.GetPostBackClientHyperlink(_singleClickButton, "");
// 給每一個可編輯的單元格增加事件
for (int columnIndex = _firstEditCellIndex; columnIndex < e.Row.Cells.Count; columnIndex++)
{
// 增加列索引作為事件參數(shù)
string js = _jsSingle.Insert(_jsSingle.Length - 2, columnIndex.ToString());
// 給單元格增加onclick事件
e.Row.Cells[columnIndex].Attributes["onclick"] = js;
// 給單元格增加鼠標經(jīng)過時指針樣式
e.Row.Cells[columnIndex].Attributes["style"] += "cursor:pointer;cursor:hand;";
}
}
}
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
// 從第一個單元格內(nèi)獲得LinkButton控件
LinkButton _singleClickButton = (LinkButton)e.Row.Cells[0].Controls[0];
// 返回一個字符串,表示對包含目標控件的 ID 和事件參數(shù)的回發(fā)函數(shù)的 JavaScript 調(diào)用
string _jsSingle = ClientScript.GetPostBackClientHyperlink(_singleClickButton, "");
// 給每一個可編輯的單元格增加事件
for (int columnIndex = _firstEditCellIndex; columnIndex < e.Row.Cells.Count; columnIndex++)
{
// 增加列索引作為事件參數(shù)
string js = _jsSingle.Insert(_jsSingle.Length - 2, columnIndex.ToString());
// 給單元格增加onclick事件
e.Row.Cells[columnIndex].Attributes["onclick"] = js;
// 給單元格增加鼠標經(jīng)過時指針樣式
e.Row.Cells[columnIndex].Attributes["style"] += "cursor:pointer;cursor:hand;";
}
}
}
在RowCommand事件內(nèi)讀出命令參數(shù)和事件參數(shù)。 這會告訴我們被選中的行和列的索引。
int _rowIndex = int.Parse(e.CommandArgument.ToString());
int _columnIndex = int.Parse(Request.Form["__EVENTARGUMENT"]);
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();
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();
下面有一些代碼用于回發(fā)服務器后設置焦點到編輯控件,如果編輯控件是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的話則選中文本框內(nèi)文本
if (_editControl is TextBox)
{
((TextBox)_editControl).Attributes.Add("onfocus", "this.select()");
}
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的話則選中文本框內(nèi)文本
if (_editControl is TextBox)
{
((TextBox)_editControl).Attributes.Add("onfocus", "this.select()");
}
在這個Demo中,我把事件被觸發(fā)的歷史記錄也寫到了頁里。
如果GridView處于編輯模式的話,那么要在RowUpdating事件里去查找被選中行的每一個單元格。 如果發(fā)現(xiàn)單元格處于編輯模式的話,那么就調(diào)用“更新”代碼。 在這個Demo中,數(shù)據(jù)保存在DataTable里,而這個DataTable則儲存在session中。
// 循環(huán)每一列以找到處于編輯模式下的單元格
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
}
}
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事件在編輯單元格后被激發(fā),要在Page_Load中來觸發(fā)這個事件。 編輯了TextBox后,通過按回車鍵或者單擊另一單元格來使頁面做回發(fā)處理,下面的這段代碼就是用于確保任何數(shù)據(jù)的改變都會被更新。
if (this.GridView1.SelectedIndex > -1)
{
this.GridView1.UpdateRow(this.GridView1.SelectedIndex, false);
}
{
this.GridView1.UpdateRow(this.GridView1.SelectedIndex, false);
}
為了驗證而注冊回發(fā)和回調(diào)數(shù)據(jù)
在RowDataBound中創(chuàng)建的自定義事件必須要在頁中注冊。 通過重寫Render方法來調(diào)用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);
}
{
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);
}
這將防止任何“回發(fā)或回調(diào)參數(shù)無效”的錯誤。
這個Demo中的其它示例
使用SQL數(shù)據(jù)源控件編輯某一獨立的GridView單元格
用SqlDataSouce控件實現(xiàn)這個技術需要對GridView的RowUpdating事件做一些修改。 當更新GridView的行的時候,SqlDataSource控件一般要把值(values)從EditItemTemplate轉(zhuǎn)移到NewValues集合里。 因為我們沒有使用EditItemTemplate,所以這種情況下值(values)不會自動地轉(zhuǎn)移到NewValues集合里。
e.NewValues.Add(key, value);
我在App_Data文件夾下使用了一個簡單的SQL Server Express數(shù)據(jù)庫。 (要使用你自己的數(shù)據(jù)庫的話,你可以修改web.config里的連接字符串)
使用對象數(shù)據(jù)源控件編輯某一獨立的GridView單元格
本示例使用了App_Code文件夾內(nèi)的兩個類:
·Task.cs – 任務對象
·TaskDataAccess.cs – 管理任務對象
Aspx頁的后置代碼與SQL Data Source示例是一樣的。 ObjectDataSource通過TaskDataAccess.cs類里的GetTasks和UpdateTask方法來管理數(shù)據(jù)。
有著電子數(shù)據(jù)表樣式的GridView
這里有一個與電子數(shù)據(jù)表的樣式很像的GridView。 (雖然它看起來像一個電子數(shù)據(jù)表,但是并不是真的有像電子數(shù)據(jù)表一樣的功能,它仍然是一個GridView。)
這里雖然有一些單擊后改變單元格樣式的附加代碼,但是主要的代碼還是與上面所述是相同的。

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