65.9K
CodeProject 正在变化。 阅读更多。
Home

带插入行的 GridView

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (30投票s)

2006年10月18日

CPOL

5分钟阅读

viewsIcon

448963

downloadIcon

4887

带插入行的GridView。

Sample Image - grid.gif

引言

关于如何为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标志。这将强制GridViewFooterStyle应用于插入行。
  • 允许页面验证取消插入 - 在OnRowCommand中添加了对Page.IsValid的检查。如果验证失败,则避免插入。
  • 修复了自增列的崩溃问题 - 修复了页脚行绑定的初始化,以包含自增列。
  • RowInserting事件更改为GridViewUpdateEventHandler类型 - 以防止崩溃并提高一致性。注意:这可能会影响与先前版本的兼容性。
© . All rights reserved.