收藏自http://herald.seu.edu.cn/blog/dipper/articles/7780.aspx
提供了ASP.net调用javascript程序的方法
客户端数据库操作
Carl Ganz, Jr.
本月,Carl Ganz 将他的注意力转到了客户端处理。“无论您是使用ASP.NET还是传统的ASP,”他说,“都可以执行客户端使用JavaScript或者目前不太常用的VBScript所执行的尽可能多的任务。”当然,窍门就是要知道在何时何处实现客户端编程。在本文中,Carl将重点说明一个简单的ASP.NET数据输入窗体,以向您展示如何使用少量的客户端编程来将其加速。
本月的示例应用程序允许用户在一个 Employee 表中编辑数据。(假定用户的浏览器为 Internet Explorer。)可编辑的字段是姓、名、供应商以及联系人。一个词典列表填充了包含供应商的下拉列表框,一旦用户选择了一个供应商,联系人列表就会填充为该供应商工作的联系人名称。数据在一个网格中显示给用户(请参见 图1 ),在本例中,该网格为 Infragistics WebGrid。当某一行在网格中突出显示时,该行中的数据会填充编辑控件。用户可以添加新行、保存已修改的行,以及删除行。
如果没有客户端编程,用户所遇到的回发数(即便是一个像这样简单的页面)将导致难以接受的缓慢响应时间。例如,单击 Add 按钮会生成一个只将页面置于 Add 模式的回发。如果选择网格中的其他行,页面会创建一个回发,以便用新行的数据更新编辑控件。如果用户选择一个新的供应商,则需要一个回发来检索该供应商经过筛选的联系人列表。使用客户端编程,我们就能够消除到服务器的这些消耗时间和资源的行程。此外,较少的回发意味着较为简单的逻辑流。
对于图 1 中所示的 Web 页,我们可以在以下位置添加客户端代码:
• |
Add 链接 — 这可让我们在最初单击按钮时避免一个回发。 |
• |
Save 链接 — 通过实现一些(如果不是全部的话)数据验证客户端,我们将避免在验证失败时到服务器的行程。(您不能总是在客户端执行验证,有时须访问后端数据库。) |
• |
Delete 链接 — 客户端确认提示可以确定用户是否真地 要进行删除,如果是,一个回发就可以在服务器上执行删除存储过程。 |
• |
行更改事件 — Infragistics WebGrid 的客户端编程模型可让您捕获许多事件,其中之一就是行激活事件。当用户选择一个新行时,控件将由该行的新值填充。 |
• |
供应商/联系人下拉列表框关系 — 当用户更改供应商时,只有那些属于选定供应商的联系人才应该通过客户端代码加载到联系人下拉列表框中。无须到服务器上提取那些与该供应商关联的联系人的列表。 |
第一步是启用适当的控件来触发客户端代码。这是通过每个控件的 Attribute 属性完成的,该属性可以在Page_Load 事件中指定。 清单1 阐释了如何将各种控件链接到其关联的 JavaScript 函数。
清单 1. 将控件链接到 JavaScript。
lnkAdd.Attributes.Add("onClick", "return AddClick();")
lnkDelete.Attributes.Add("onClick", _
"return DeleteClick();")
lnkSave.Attributes.Add("onClick", _
"return SaveClick();")
cmbVendor.Attributes.Add("onChange", "VendorClick();")
UltraWebGrid1.DisplayLayout.ClientSideEvents. _
AfterRowActivateHandler = "AfterRowActivateHandler"
请注意,供应商下拉列表指向 VendorClick(),而链接按钮指向前缀为 return 的函数。这是因为这些 JavaScript 函数会返回一个需要计算的布尔值。如果函数返回假,那么与关联链接按钮相关联的任何服务器端代码将不会执行。例如,如果用户表明他或她不是真的想要删除,或者在尝试保存时其中一个数据验证失败,那么代码执行操作将在客户端停止。
由于许多客户端函数是在应用程序之间重复的(消隐控件、切换禁用/启用等),因此将这个功能放在单独的 JavaScript 文件中十分有意义。该文件可以链接到您的应用程序中,如下所示:
<script language="javascript" src="common.js">
</script>
为了存储当前编辑行的主键值,您可以在页面上创建一个名为 txtRowID 的 HTML 控件。重要的是,不要在可见性属性为“hidden”时创建这个控件。否则,它将在客户端上不可用,这是因为服务器在发送该页面时不会传送它。更确切地说,您应该使该控件可见,但使其隐藏在一个 JavaScript onLoad 事件中(请参见 清单2 ).
清单 2. onLoad 事件。
function PageLoad()
{
var oForm = document.frmEmployee;
var oGrid = igtbl_getGridById('UltraWebGrid1');
var oActiveRow =
igtbl_getActiveRow('UltraWebGrid1');
//If there's at least one row in the grid already,
//force a call to the Infragistics event handler
if (oGrid.Rows.length > 0)
{AfterRowActivateHandler("UltraWebGrid1",
"UltraWebGrid1r_" +
oGrid.getActiveRow().getIndex());
}
Else
{
//Since LinkButtons converts to anchor tags in
//HTML they can't be ref'd with the Elements
//collection. The All collection is needed.
oForm.all['lnkDelete'].disabled = true;
oForm.all['lnkSave'].disabled = true;
ToggleControls(oForm, true);
BlankControls(oForm);
}
oForm.elements['txtRowID'].style.visibility =
"hidden";
}
清单3 显示了 Add/Cancel Add 链接的代码。这段代码可在 Add 模式下将 Add 链接按钮切换到 Cancel,然后在用户按下 Cancel 或 Save 后返回到 Add。
清单 3. JavaScript Add 链接代码。
function AddClick()
{
//Get a ref to form named in HTML form tag id value
var oForm = document.frmEmployee;
//Using Infragistics client-side functions, get
//refs to the grid and the active row. If no row is
//active, the oActiveRow object will be set to null
var oGrid = igtbl_getGridById('UltraWebGrid1');
var oActiveRow=igtbl_getActiveRow('UltraWebGrid1');
//Since link buttons process into HTML anchor tags,
//we need to use the All collection as they won't
//be found in elements
if (oForm.all['lnkAdd'].innerHTML == "Add")
{
//Change the button caption to Cancel
oForm.all['lnkAdd'].innerHTML = "Cancel";
//Disable grid so user can't select other rows
oGrid.Element.disabled = true;
//Enable all controls, blank out control values
ToggleControls(oForm, false);
BlankControls(oForm);
//Disable Delete and Save buttons and set focus
//to the LastName control
oForm.all['lnkDelete'].disabled = true;
oForm.all['lnkSave'].disabled = false;
oForm.elements['txtLastName'].focus();
//Set the hidden txtRowID control to 0 to
//indicate we're in Add mode
oForm.elements['txtRowID'].value = 0;
}
Else
{
//If we're already in Add mode then reverse the
//process when the user presses Cancel
oForm.all['lnkAdd'].innerHTML = "Add";
oGrid.Element.disabled = false;
if (oGrid.Rows.length > 0)
{
ToggleControls(oForm, false);
oForm.all['lnkDelete'].disabled = false;
oForm.all['lnkSave'].disabled = false;
AfterRowActivateHandler("UltraWebGrid1",
"UltraWebGrid1r_" + oActiveRow.getIndex());
}
Else
{
ToggleControls(oForm, true);
oForm.all['lnkDelete'].disabled = true;
oForm.all['lnkSave'].disabled = true;
oForm.elements['txtRowID'].value = 0;
}
}
return false;
}
删除相对比较简单(请参见 清单4 )。这个简单的示例显示了一个 JavaScript 确认框,该确认框显示 OK 和 Cancel 作为仅有的两个选项。确认对话框不会为您提供 Win 窗体的 MessageBox 函数所提供的多种选择。请参阅 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndude/html/dude09252000.asp ,以了解使用showModalDialog 函数为用户提供外观更加专业的消息对话框的方法。
清单 4. 删除代码。
function DeleteClick()
{
if(confirm("Are you sure you want
to delete this data?"))
{
return true;
}
Else
{
return false;
}
}
当用户选择网格中的其他项时,我们需要用基础值填充编辑控件(请参见清单 5)。由于我们使用的是 Infragistics 事件处理程序,网格的 id 和新选定的行将自动为您传入。从那里,我们可以创建一个行对象,并将指定的单个数据值提取到编辑控件。
清单 5. 填充编辑控件。
function AfterRowActivateHandler(gridId, rowId)
{
var oForm = document.frmEmployee;
var oRow = igtbl_getRowById(rowId);
oForm.elements['txtRowID'].value =
oRow.getCellFromKey("ID").getValue();
oForm.elements['txtLastName'].value =
oRow.getCellFromKey("LastName").getValue();
//similar code omitted here, see source
VendorClick();
oForm.elements['cmbContact'].value =
oRow.getCellFromKey("ContactID").getValue();
}
因为供应商选项可以筛选联系人下拉列表,所以一项避免每次供应商更改都产生一个回发的技术是,创建一个跨所有供应商的所有联系人的数组,以便它在客户端可用。这最好在 Web 页的 Page_Load 事件中完成(请参见 清单6 ).
清单 6. 创建联系人数组.
Sub ContactArray()
Dim oDT As DataTable
Dim oDataRow As DataRow
Dim oDataAccessSQLServer As DataAccessSQLServer
Dim oJavaCode As System.Text.StringBuilder
Dim x As Int32
oDataAccessSQLServer = New DataAccessSQLServer
oDT = oDataAccessSQLServer. _
GetDataTable(szConnect, "sp_GetContacts", _
CommandType.StoredProcedure)
oDataAccessSQLServer = Nothing
oJavaCode = New System.Text.StringBuilder
oJavaCode.Append ("var aContacts = new Array();" _
+ ControlChars.CrLf)
For Each oDataRow In oDT.Rows
oJavaCode.Append ("aContacts[" + x.ToString + _
"] = new Array();" + ControlChars.CrLf)
oJavaCode.Append ("aContacts[" + x.ToString + _
"][0] = " + oDataRow("vendorID").ToString + _
";" + ControlChars.CrLf)
oJavaCode.Append ("aContacts[" + x.ToString + _
"][1] = " + oDataRow("ID").ToString + ";" + _
ControlChars.CrLf)
oJavaCode.Append ("aContacts[" + x.ToString + _
"][2] = " + ControlChars.Quote + _
oDataRow("ContactName").ToString + _
ControlChars.Quote + ";" + ControlChars.CrLf)
x += 1
Next
RegisterStartupScript("StartUp", _
"<script language=""Javascript"" > " + _
oJavaCode.ToString + " </Script>")
oDT.Dispose()
oDT = Nothing
oJavaCode = Nothing
End Sub
在清单 6 中,我们创建了一个字符串对象,用于存储 JavaScript 代码 — 这些代码是保存 JavaScript 数组(包含供应商 id、联系人 id 和联系人姓名)所必需的。(Cr/Lf 只是让客户端代码对于调试来说具有更好的可读性。)然后,RegisterStartupScript 函数将代码放到窗体的正文中,以便其他 JavaScript 函数可以看到这个数组。需要联系人数组的 JavaScript 函数是 VendorClick()。 清单7 中所示的代码将在当前供应商选项更改时执行。
清单 7. VendorClick() 函数。
function VendorClick()
{
var oForm = document.frmEmployee;
var oCombo = oForm.elements['cmbContact'];
var iVendor =
GetComboValue(oForm.elements['cmbVendor']);
var i;
//Clear out the existing options
while (oCombo.Options.length)
{
oCombo.options.remove(0);
}
//loop through the entire contacts array
for (i=0; i <= aContacts.length - 1; i++)
{
if (aContacts[i][0] == iVendor)
{
var oOption =
document.createElement("OPTION");
oOption.text = aContacts[i][2];
oOption.value = aContacts[i][1];
oCombo.options.add(oOption);
}
}
}
请记住,联系人的数量越大,传输到客户端的 Web 页就越大。如果您的联系人过多以至于无法选择,则可能需要考虑在每次用户选择新供应商时执行回发。
还有另一种方法可以使您 Web 页的响应时间大大加速,但是有一点危险。本文的示例应用程序在每次删除和保存后都会执行回发,无论是更新行还是将某一行插入 RDBMS 中。但是,由于所有数据都存储在网格控件中,所以您可以 在网格中直接操作数据。然后,当用户完成会话后,他(她)可以按下提交按钮,将所有 更改后的数据写回 RDBMS。对于需要“面向(heads-down)”数据项的应用程序,每行的这些额外秒数会产生一个巨大的累积差,其危险是显而易见的,一次无意的浏览器关闭或系统崩溃,都会使整个会话的数据丢失。提交按钮的代码显示在 清单8 中。
清单 8. 提交网格更改。
Dim oUltraGridRow As _
Infragistics.WebUI.UltraWebGrid.UltraGridRow
Dim oDataAccessSQLServer As DataAccessSQLServer
'other Dims omitted here; see source
For Each oUltraGridRow In UltraWebGrid1.Rows
'The sixth col flags changes
If oUltraGridRow.Cells(5).Value <> 0 Then
szLastName = oUltraGridRow.Cells(1).Value
szFirstName = oUltraGridRow.Cells(2).Value
iContactID = oUltraGridRow.Cells(3).Value
If oUltraGridRow.Cells(5).Value = 1 Then
'Insert data
Else
'Update data
End If
End If
Next
oUltraGridRow = Nothing
oDataAccessSQLServer = New DataAccessSQLServer
UltraWebGrid1.DataSource = _
oDataAccessSQLServer.GetDataTable(szConnect, _
"sp_GetEmployees", CommandType.StoredProcedure, _
txtLastNameFilter.Text)
UltraWebGrid1.DataBind()
oDataAccessSQLServer = Nothing
考虑到用 ASP.NET 开发的 Web 应用程序的数量正在不断增加,使其尽可能地响应用户是十分有意义的,但只有少数几个 JavaScript 可以提供帮助。
【请参见一篇即将发表的由 Dianne Siebold 撰写的文章,其中包含有使用 Janus GridEx 控件的提示( www.janusys.com/janus/library )。读者可能还想查看 www.codeproject.com/cs/database/gridex.asp 上的 CodeProject 的开放源码 DataGrid 控件,并阅读或再次阅读某些 VBD 文章:Rod Stephens 2000 年 5 月的专栏“EditFlex”,关于增强 MSFlexGrid 控件;Youning Lin 2002 年 8 月的关于使用 MSFlexGrid 将数据输出到 Excel 的技巧;Jon Kilburn 2000 年 1 月的关于使用 MSFlexGrid 来创建 XML 编辑器的文章,以及他在 2001 年 3 月对 Janus GridEx 2000 控件的评论;以及 Bill Vaughn 2004 年 1 月的关于在 .NET 的 DataGrid 控件中自动调整大小的文章。— Ed。】
链接到 http://devcenter.infragistics.com
有关 Hardcore Visual Basic 和 Pinnacle Publishing 的详细信息,请访问其网站 http://www.pinpub.com/
注这不是 Microsoft Corporation 的网站。Microsoft 对该网站的内容不承担责任。
本文是从 Hardcore Visual Basic 2004 年 6 月号转载的。版权所有 2004,Pinnacle Publishing, Inc.(除非另行说明)。保留所有权利。Hardcore Visual Basic 是 Pinnacle Publishing, Inc. 独立发行的产品。未经 Pinnacle Publishing, Inc. 事先同意,不得以任何形式使用或复制本文的任何部分(评论文章中的简短引用除外)。要联系 Pinnacle Publishing, Inc.,请致电 1-800-788-1900。