真正类似于 Excel 的网格控件(二)

翻译|其它|编辑:郝浩|2005-01-04 09:51:00.000|阅读 1432 次

概述:

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>


Update 操作

请注意,在图 2 中看不到可在图 1 中看到的 Delete 列。为了简化界面,我决定在任何行进入编辑模式时隐藏 Delete 列。并且因为有了以前提到的内置事件处理程序,所以此行为在 EditableGrid 类中是硬编码的。

update 操作是由以下三个事件执行的:

• EditCommand — 开始该操作并以编辑模式呈现行

• UpdateCommand — 保存所做的更改并还原默认用户界面

• CancelCommand — 取消所做的更改并还原以前的用户界面


用于 EditCommand 和 CancelCommand 的典型代码是能够方便地嵌入类中的传统 vanilla 代码。一般来说,此处没有真正需要在页面级解决的内容,但是有时情况也可能与您的特定实例有所不同。

“更新” 操作的应用程序特定的逻辑集中在 “更新命令” 事件中。除了保存任何更改以外,您应当还原网格的一致状态(取消编辑模式、还原 Delete 列和拒绝待定的更改),并确保所做的更改随后由用于显示的数据源反映出来。如果您像本例中那样使用缓存数据,则后面的一点至关重要。

public void OnUpdateCommand(Object sender, DataGridCommandEventArgs e)
{
// Clear edit mode
EditItemIndex = -1;

// Show/Hide DELETE column
ToggleDeleteColumn(true);

// Reject changes on the last row
RejectChanges = true;// internal member

// Update or insert data
if (MustInsertRow)// internal member
OnInsertData(e);
else
OnSaveData(e);

// Refresh view
OnUpdateView(EventArgs.Empty);
}
UpdateCommand 处理程序取消当前行的编辑模式,然后打开 Delete 列的可见性标志。此时,该表可能有一个待定的更改。因为 “更新命令” 事件会在两种情况(打算将所做的更改保存到现有的行中时;要插入新行时)下激发,所以条件窗体很有意义。内部成员 MustInsertRow 由 InsertNewRow 设置并由 DataSource 重置,它有助于确定是哪种情况。当代码处理完网格的状态之后,它激发两个连续事件 — 一个事件让页面保存或插入到物理数据源中,另一个事件刷新数据绑定。这就解释了为何 InsertData 和 SaveData 处理程序只能执行主要通过 SQL 语句来进行的物理更新。

InsertData 和 SaveData 事件处理程序的签名与 UpdateCommand 的相同。

public event DataGridCommandEventHandler SaveData;
protected virtual void OnSaveData(DataGridCommandEventArgs e)
{
if (SaveData != null)
SaveData(this, e);
}

public event DataGridCommandEventHandler InsertData;
protected virtual void OnInsertData(DataGridCommandEventArgs e)
{
if (InsertData != null)
InsertData(this, e);
}

在本文中讨论的示例代码中,设置了几个假设,其中一个就是假设网格的数据源是 “数据视图” 对象。这间接表示不支持自定义分页,或者,更确切地讲,必须仔细修改此代码才能处理这样的网格。这同样适用于排序。

所作的第二个重要假设是有关使用 SQL 语句进行更新的。

设计思路是,无论进行了什么样的更改,总是激发单个 SQL 语句以应用它。此处设计的 EditableGrid 不能正确地处理批更新。顺便提一句,在我的 Building Web Solutions with ASP.NET and ADO.NET 一书中,在介绍就地编辑时,更详细地讨论了对于网格使用批更新的优缺点。然而,如果您对使用批更新技术感兴趣,请给我发送电子邮件,我将在以后的专栏中介绍此主题。

由于您通过直接的 SQL 语句(或者数据源识别为直接语句的内容)进行更新,因此,“更新命令” 事件可以成功地命令拒绝任何更改。这就是批更新方案中的主要区别。

“保存数据” 和 “插入数据” 事件代表执行更新所必需的任务的子集。在执行该命令之后,这些处理程序必须确保网格可以访问和显示刷新数据。在这种情况下,必须更新数据的缓存副本。根据基础数据库架构(任何触发器或任何自动编号的字段),可以决定是完全重新读取还是基于每个字段更新缓存数据。

让我们花些时间来了解如何从网格的编辑模板来检索更新后的数据。我考虑使 EditableGrid 控件具有足够的智能,以便从单元格提取值,并将它们填充到 DataRow 对象中以充当事件数据。这个方法使得在批方案中更新代码变得微不足道。之所以决定让 ASP.NET 页负责提取数据,是因为这样您也可以透明地支持编辑模板。下面我将向您展示在不使用任何特殊模板时所必需的代码。

public void SaveData(Object sender, DataGridCommandEventArgs e)
{
StringBuilder sb = new StringBuilder("");
sb.Append("UPDATE Employees SET ");
sb.Append("firstname='{0}',");
sb.Append("lastname='{1}',");
sb.Append("title='{2}',");
sb.Append("country='{3}' ");
sb.Append("WHERE employeeid={4}");
String cmd = sb.ToString();
sb = null;

TextBox fName= (TextBox) e.Item.Cells[1].Controls[0];
TextBox lName= (TextBox) e.Item.Cells[2].Controls[0];
TextBox position = (TextBox) e.Item.Cells[3].Controls[0];
TextBox country= (TextBox) e.Item.Cells[4].Controls[0];

cmd = String.Format(cmd,
fName.Text, lName.Text,
position.Text, country.Text,
grid.DataKeys[e.Item.ItemIndex]);

// Executes the command
OleDbConnection conn = new OleDbConnection(m_connString);
OleDbCommand c = new OleDbCommand(cmd, conn);
c.Connection.Open();
c.ExecuteNonQuery();
c.Connection.Close();

// Re-read from the database and updates the cache
DataFromSourceToMemory();
}
要检索用户在文本框中输入的信息,使用 e.Item.Cells[n].Controls[0] 表达式,其中n 是该列的索引(从 0 开始)。请记住,DataGrid 的就地编辑功能允许您通过将 “只读” 属性设置为 true 来将列视为只读。

Delete 操作

Delete 操作的工作方式与 Insert 和 Update 操作大体相同。自动创建的列有一个命令名 Delete,在单击该按钮时会导致激发 ± 事件。内置的处理程序修复网格的界面,然后依次先后激发 DeleteData 和 UpdateView。

由于删除操作的入侵性比插入或更新操作的强,因此您可能希望使用某个客户端脚本代码,并要求用户在继续之前确认。我在 last month's column 的“对话栏”部分中讨论了该方法。在本月的源代码中,有一个如下图所示的实际实现。


图 3. 在删除之前确认

最终的优化

我提到过 EditableGrid 控件支持编辑模板。让我来证明这一结论。在示例代码的第 2 步,我使用该网格中一组稍有不同的列。

<columns>
<asp:boundcolumn runat="server" headertext="ID"
datafield="employeeid" readonly="true" />
<asp:templatecolumn runat="server" headertext="Name">
<itemtemplate>
<%# DataBinder.Eval(Container.DataItem, "lastname") + ", " +
DataBinder.Eval(Container.DataItem, "firstname") %>
</itemtemplate>
<edititemtemplate>
<asp:textbox runat="server" id="lName"
text='<%#DataBinder.Eval(Container.DataItem, "lastname")%>' />
<asp:textbox runat="server" id="fName"
text='<%#DataBinder.Eval(Container.DataItem, "firstname")%>' />
</edititemtemplate>
</asp:templatecolumn>

<asp:boundcolumn runat="server" headertext="Position"
datafield="title" />
<asp:boundcolumn runat="server" headertext="Country"
datafield="country" />
</columns>
正如您可以看到的那样,有一个模板列,它在单个字段中显示名和姓。此列的编辑模板(您必须指定能够编辑该列的编辑模板)由两个并排的文本框提供。无需在类中进行任何更改,即可获得图 4 中所示的示例。



图 4. 使用编辑模板

另一方面,在使用从文本框中检索更新的文本的方式时,编辑模板需要进行少许调整。现在,可以按名称检索模板中的控件。之所以可以并且建议这样做,是因为您知道控件的 ID。

TextBox fName= (TextBox) e.Item.FindControl("fName");
TextBox lName= (TextBox) e.Item.FindControl("lName");
TextBox position = (TextBox) e.Item.Cells[2].Controls[0];
TextBox country= (TextBox) e.Item.Cells[3].Controls[0];

在本月的源代码中,您将发现该类的 C# 源代码、两个示例 ASP.NET 页以及我用过的 Access 数据库。但是,您将找不到编译过的程序集。我提供了一个(非常)简单的批文件,您可以使用它,针对与 beta 2 及其更高版本兼容的任何版本的 .NET CLR,将该类编译成为可执行代码。

注 如果您在使下载正确运行时遇到问题,请检查下列步骤:

• 1.将 CS 类编译成为程序集。这可通过打开一个 DOS 框并运行 ZIP 中的 c.bat 批处理文件来完成,或者通过在 Visual Studio 中创建一个新的类库项目并将类文件添加到这个空白项目中来实现。

• 2.必须使程序集对于示例 ASPX 页可用。下载文件中包括的批处理文件将该 DLL 复制到 c:\inetpub\wwwroot\bin 文件夹中。如果您恰好有一个不同的路径,请对它进行修改。如果您创建一个虚拟目录,请确保将该 DLL 复制到虚拟文件夹的 BIN 子文件夹中,而不要复制到 Web 服务器根的 BIN 文件夹中。

• 3.根据 ASP.NET 安全设置,在与示例 Microsoft Access 数据库交互时,可能会遇到难以处理的 Updateable Query 错误。在这种情况下,请更改示例 .MDB 文件的安全设置。在“Windows 资源管理器”中选择该文件,显示属性,然后单击安全选项卡。接着,添加 \ASPNET 用户并确保它有权写入和修改该文件。

• 4.刷新 ASPX 页面。


对话栏:创建自定义模板

在上一个专栏中,您讨论了 Summary 网格组件。是否有机会创建或加载汇总行的自定义模板?这将允许在 ASPX 文件中(而非代码中)规定设计的规范。

使用汇总行的自定义模板无疑将是可能的。问题只是在于如何实现?或者,更准确地说,哪种方法最简单?我将在下面介绍几种可能的方法。

• 使用 pagelet: 可以修改应用程序的代码,使其动态加载 pagelet 控件(又称用户控件) — 即 ASCX 文件。ASCX 文件看上去像一个小型 Web 窗体,而且其中的大部分内容都是布局信息。可以使用 Page.LoadControl 来按名称加载 ASCX 文件,然后将它添加到 DataGridItem 对象的某个单元格的 Controls 集合中。

• 模板列:DataGrid 的所有列都是模板化列。使用 ASP.NET 标记和各处的一些代码定义每一列的 。您能够执行的操作就是将 块的结构分成两个截然不同且互斥的部分 — 一部分用于普通行,另一部分用于汇总行。为了确保一次只显示一行,可以处理控件的 “可见” 属性并将该属性与界定汇总行或普通行的条件绑定。

• 编写新控件:从 DataGrid 派生一个新类并添加一个新的模板属性。这允许您使用自定义的子标记、按照与输入列模板几乎相同的方式输入汇总行布局。

 


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com


为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP