可编辑的 GridView






2.36/5 (4投票s)
一篇关于 GridView 扩展程序的文章,

引言
由于我热衷于学习新事物,所以我想尝试一下控件扩展器。对于这个扩展器,我的灵感来自于一个GridView
控件,它包含需要编辑的记录。
在经典的ASP.NET方式中,我们会将整个页面回发,获取返回的记录,但这次是在文本字段中输入数据进行编辑,再次将整个页面回发以应用更改,然后再次接收网格以获得一组集中的数据,不再有textbox
。这种情况导致相同的数据在网络上传输两次,另外用于编辑的数据表单也会在网络上传输两次。
此扩展器的目标是让用户直接在网格中编辑数据,并且只将受影响的记录发送到服务器,从而节省大量带宽。
构建块
ASP.NET AJAX框架的设置方式允许我们为任何控件编写扩展器。由于框架是可扩展的,我们可以在编辑模式下创建自己的JavaScript对象。
此扩展器中使用的所有自定义对象都仅在JavaScript中实现。可编辑单元格是一个自定义对象(BM.Extenders.TableCell
),它将HTML注入到其附加到的DOM对象中。
废话不多说,我将为您呈现代码。
BM.Extenders.GridView 亮点
GridView
扩展器在初始化时会挂钩到GridView
并注入一个按钮列。这些按钮在它们所在的行切换到编辑模式之前是隐藏的。
同样在初始化期间,会将一个事件处理程序附加到PageRequestManager
的initializeRequest
。此事件处理程序会阻止在记录处于编辑模式时发生更新(计时器滴答、按钮点击等)。
此外,我们可以定义哪些列应该可编辑的索引,从而防止用户修改他们不应该触及的数据。我们还可以选择隐藏一些包含更新所需但对用户无用的值的列(ID、我们查询的where
子句中使用的字段等)。
所有需要做的是设置一些属性,以便在需要时发出警报(保存成功、保存失败、项目已更改等),用于在数据库中实际更新的页面方法,以及——最后但同样重要的是——初始化我们的TableCell
对象(见下文)。
扩展器的初始化函数(为简洁起见,属性访问器已被移除)
BM.Extenders.GridViewBehavior.prototype = {
initialize : function() {
BM.Extenders.GridViewBehavior.callBaseMethod(this, 'initialize');
//hook up to the initialization of the request
Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(onPostBack);
var element = this.get_element();
gridName = element.id;
initialise();
},
dispose : function() {
//remove the event handler
Sys.WebForms.PageRequestManager.getInstance().remove_initializeRequest
(onPostBack);
BM.Extenders.GridViewBehavior.callBaseMethod(this, 'dispose');
},
...
initializeRequest
事件处理程序
function onPostBack(sender, args)
{
if(editing)
{
args.set_cancel(true);
}
}
当处于编辑模式时,我们仅取消请求。
BM.Extenders.TableCell 亮点
我们需要一种方法来知道我们正在编辑哪个单元格。单元格可以通过它所在的行和列来唯一标识。TableCell
对象有两个属性:“row
”和“col
”。在其初始化过程中,会将一个事件处理程序附加到click
事件,并填充这些属性。
cell = $create(BM.Extenders.TableCell, {row: i, col: editableColumns[j]},
{click: edit}, null, rows[i].cells[editableColumns[j]]);
事件处理程序非常简单。它会传递一个指向eventElement
的引用,通过它可以获取属性。首先要做的是检查这是我们要编辑的第一个单元格,是与上一个单元格在同一行的单元格,还是完全是另一行的单元格。供内部使用时,行、列和旧值存储在一个数组中。之后,添加textbox
并显示按钮。
function edit(eventElement)
{
var row = eventElement.get_row();
var col = eventElement.get_col();
//first cell to edit
if(!editing)
{
var cell = getCell(row, col);
setEditing(row, col);
makeEditable(cell);
}else{
//determine which cell was previously being edited
if(evalEditingRow(row, col))
{
//another cell in the same row
//remove textbox from previously edited cell
var cell = getCell(index[0], index[1]);
updateCell(cell);
//push the items back to make room
index.Slide(1,2);
cell = getCell(row, col);
setEditing(row, col);
makeEditable(cell);
}
if(evalDifferentRow(row))
{
//click raised on different row
var cell = getCell(index[0], index[1]);
//remove textbox
updateCell(cell);
var changed = evalCellChanged();
var proceed = false;
//notification removed for brevity
if(!changed || proceed)
{
resetValues();
index.Clear();
var cell = getCell(row, col);
setEditing(row, col);
makeEditable(cell);
}else{
//put textbox back
}
}
}
}
变量“index
”是一个数组,我向其中添加了一个Slide
函数来移动项目并创建空间,以及一个Clear
函数来清空数组。
Array.prototype.Slide = function(start, places)
{
for(i=start; i<=places; i++)
{
this.push(index[i]);
this[i] = "";
}
}
基本上,当前项被添加到数组的末尾(push)并被清空。
Array.prototype.Clear = function()
{
this.length = 0;
}
编辑中调用的其他函数
function getCell(row, col)
{
return $get(gridName).rows[row].cells[col];
}
function makeEditable(cell)
{
index[2] = cell.innerHTML;
cell.innerHTML = "<input type=\"text\" value=\"" + index[2] + "\"/>";
showButtons(index[0]);
}
function updateCell(cell)
{
var text = cell.getElementsByTagName("input")[0].value;
cell.innerHTML = text;
}
function setEditing(row, col)
{
index[0] = row;
index[1] = col;
editing = true;
}
function resetValues()
{
var row = index[0];
for(i=1; i<index.length; i+=2)
{
var cell = getCell(row, index[i]);
cell.innerHTML = index[i+1];
}
}
发送编辑后的数据时,会调用send
函数,并传递对该行的引用。
function send(row)
{
//remove textbox
var cell = getCell(index[0], index[1]);
updateCell(cell);
hideButtons();
removeEditing();
//load values from grid
var row = $get(gridName).rows(row);
var arguments = new Array();
for(i=0; i<buttonColumn; i++)
{
arguments[i] = row.cells[i].innerHTML;
}
//Call server side function
var path = window.location;
Sys.Net.WebServiceProxy.invoke(path, serviceMethod,
false, {args:arguments}, OnCallSaveComplete, OnCallSaveError);
}
首先,会移除textbox
,然后可以将值(单元格文本)放入一个数组中。最后,会调用AJAX框架的Sys.Net.WebServiceProxy.invoke
方法。
send
方法调用的方法
function removeEditing()
{
index.Clear();
editing = false;
}
function hideButtons()
{
var rows = $get(gridName).rows;
for(i=1; i<rows.length; i++)
{
rows[i].cells<buttoncolumn />.getElementsByTagName("span")[0].style.visibility = "hidden";
}
}
关于本地化和定期更新
要本地化文本属性(您应该用用户的语言与他们交流),我们可以利用资源文件并在扩展器的服务器标记中添加meta:resourcekey
元素。这已经过测试,效果很好。
定期更新可以通过计时器等触发。由于GridView
及其扩展器必须位于同一个updatepanel
中(幸运的是),因此每次刷新updatepanel
时都会初始化扩展器。
可能的改进和未来添加
我已经有一些想法可以将其进一步发展。我可以添加对以下方面的支持:
- 掩码
textbox
(一个流行的AJAX扩展器) - 为每列定义的正则表达式,以便在
textbox
的blur事件触发时,可以在用户端验证输入
历史
- 2007年10月2日:初始版本