带插入行的 GridView






4.40/5 (30投票s)
带插入行的GridView。
引言
关于如何为GridView
控件添加插入行,已经有无数的文章。我所见到的几乎所有方法都建议添加一个带有适当模板的FooterRow
。然而,当数据源为空时,这种方法也会失效——此时GridView
只会显示一行空行。本文提出的解决方案解决了这两个问题——HeaderRow
始终显示,并且FooterRow
会自动填充适当的模板/输入元素。此外,该解决方案的独特之处在于,无需对列(使用特殊类型)、页脚行或数据源/SQL语句进行任何特殊或额外的设置——您可以使用Visual Studio来配置它,就像配置DetailsView
控件一样。
使用代码
对于习惯了GridView
的用户来说,使用起来非常简单——只需拖放并进行配置即可。设计时编辑器是相同的,因此GUI也完全一致。唯一需要确保的是,已配置数据源的Insert
命令,并且GridView
上的EditFlag
已打开。或者,您可以使用VS配置GridView
,然后只需将标签更改为InsertableGrid
。
InsertableGrid
(可通过设计器访问)添加了以下附加事件/属性:
bool AllowInsert
- 默认值为true
。一旦清除此属性,其行为将与普通GridView
完全相同。
GridViewUpdatedEventHandler RowInserting
- 该事件在行插入之前触发。如有需要,请在此处填充默认值。与RowUpdating
相同(此处稍微懒惰,未实现新的委托,而是重用了更新的委托)。GridViewUpdatedEventHandler RowInserted
- 该事件在插入完成后触发。在此处根据需要检查并清除ExceptionHandled
,就像您对RowUpdating
所做的一样。
此外,将为FooterRow
生成RowCreated
事件,允许您使用默认值填充某些输入字段。
protected void InsertableGrid1_RowInserted(object sender, GridViewUpdatedEventArgs e) {
if (e.Exception != null) {
Label3.Text = e.Exception.Message;
e.ExceptionHandled = true;
}
}
protected void InsertableGrid1_RowCreated(object sender, GridViewRowEventArgs e) {
if (e.Row.RowType == DataControlRowType.Footer
&& e.Row.DataItem is DataRowView) {
DataRowView r = e.Row.DataItem as DataRowView;
r["schStart"] = DateTime.Now.ToShortDateString();
r["schEnd"] = DateTime.Now.AddDays(2).ToShortDateString();
}
}
兴趣点/实现细节
为了实现此功能,需要完成以下任务:
- 确保空的
GridView
仍然显示标题行。 - 可选地,创建一个新的
FooterRow
并用编辑模式(EditTemplate
)指定的输入元素填充它。 - 创建并处理新的
Insert
命令。生成适当的事件处理程序。 - 为页脚(插入)行提供双向绑定,以便轻松初始化插入行上的数据。
确保标题/页脚行仍然被创建。
这被证明是一项相当艰巨的任务。GridView
在没有数据时坚持创建一个单一单元格的表。为了解决这个问题,我不得不覆盖protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding)
方法,并检查基类的返回值。如果返回值为0(或其他),则意味着GridView
中没有数据,我需要去创建标题/页脚(插入)行。此处使用base.CreateRow
来创建行。
int ret = base.CreateChildControls(dataSource, dataBinding);
if (Columns.Count == 0 || DesignMode || !AllowInsert)
return ret;
DataControlField[] flds = new DataControlField[Columns.Count];
Columns.CopyTo(flds, 0);
if (ret == 0) {
Controls.Clear();
Table t = new Table();
Controls.Add(t);
GridViewRow r = CreateRow(-1, -1, DataControlRowType.Header,
DataControlRowState.Normal);
this.InitializeRow(r, flds);
t.Rows.Add(r);
gvFooterRow = CreateRow(-1, -1, DataControlRowType.Footer,
DataControlRowState.Insert);
this.InitializeRow(gvFooterRow, flds);
t.Rows.Add(gvFooterRow);
}
else
gvFooterRow = base.FooterRow;
填充页脚/插入行与编辑元素
我认为必须为页脚行定义与编辑行相同的模板会很麻烦。这也是大多数解决方案所提供的。我认为自动在页脚行上创建一组输入元素会更有意义。因此,在FooterRow
被识别/创建后,我遍历所有列,并在页脚行上创建相应的单元格。
for (int i = 0; i < Columns.Count; i++) {
DataControlFieldCell cell = (DataControlFieldCell)FooterRow.Cells[i];
DataControlField fld = Columns[i];
if (fld is CommandField) {
CommandField cf = (CommandField)fld;
CommandField ins = new CommandField();
ins.ButtonType = cf.ButtonType;
ins.InsertImageUrl = cf.InsertImageUrl;
ins.InsertText = cf.InsertText;
ins.CancelImageUrl = cf.CancelImageUrl;
ins.CancelText = cf.CancelText;
ins.InsertVisible = true;
ins.ShowInsertButton = true;
ins.Initialize(false, this);
ins.InitializeCell(cell, DataControlCellType.DataCell,
DataControlRowState.Insert, -1);
}
else {
fld.Initialize(base.AllowSorting, this);
fld.InitializeCell(cell, DataControlCellType.DataCell,
DataControlRowState.Edit | DataControlRowState.Insert, -1);
}
}
允许插入/页脚行的双向绑定
我认为新的DetailsView
数据绑定控件缺少一个功能,那就是能够很好地初始化插入数据。在这里,我认为纠正这个错误会很好。因此,我会创建一个虚拟的DataTable
,并用每列的值填充它。然后,我调用OnRowCreated
函数(触发事件),允许应用程序初始化插入数据(参见上面的InsertableGrid1_RowCreated
)。当您有一个BoundField
且难以识别输入控件时,这尤其有用。当然,如果您有模板化列,那么您可以使用Row.FindControl()
函数,因为您会知道控件的名称(就像您使用DetailsView
控件一样)。
OrderedDictionary dict = new OrderedDictionary();
base.ExtractRowValues(dict, FooterRow, true, true);
DataTable tbl = new DataTable();
DataRow row = tbl.Rows.Add();
foreach (string k in dict.Keys) {
tbl.Columns.Add(new DataColumn(k));
row[k] = dict[k];
}
FooterRow.DataItem = new DataView(tbl)[0];
GridViewRowEventArgs args1 = new GridViewRowEventArgs(FooterRow);
this.OnRowCreated(args1);
FooterRow.DataBind();
this.OnRowDataBound(args1);
FooterRow.DataItem = null;
foreach (DataControlField f in Columns)
f.Initialize(this.AllowSorting, this);
待解决的问题
- 说不清楚具体原因,但在设计过程中,
ItemTemplate
/EditTemplate
会以小写形式输出。 - 希望更改设计器,使其在绘制网格时包含插入行,以提供视觉提示。
- 示例ASPX页面可以在我的本地机器上与数据库一起运行。您需要创建表或更改页面。同样,配置与
GridView
完全相同,因此只需拖放并配置,或者配置GridView
然后更改标签。
历史
- 将页脚样式应用于插入行 - 当设置
AllowInsert
标志时,设置ShowFooter
标志。这将强制GridView
将FooterStyle
应用于插入行。 - 允许页面验证取消插入 - 在
OnRowCommand
中添加了对Page.IsValid
的检查。如果验证失败,则避免插入。 - 修复了自增列的崩溃问题 - 修复了页脚行绑定的初始化,以包含自增列。
- 将
RowInserting
事件更改为GridViewUpdateEventHandler
类型 - 以防止崩溃并提高一致性。注意:这可能会影响与先前版本的兼容性。