在 ASP.NET 中显示层次数据
Jay Allen、Mark Davis 和 Heidi Housten
Microsoft Corporation
我们要向读者道歉,临时失踪了一阵子。我们本来想保持神秘的,就像 Agatha Christie 消失一个月后一样,但是居然没有出现任何有关我们已经让位的谣言!我们很伤心地宣布,Dan 不得不离开了本小组 — 我们都会想念他的聪明才智和犀利的洞察能力(如果不是尖锐侮辱的话)。谢谢您,Dan。我们发现没有人被我们小组诡计的谣言(当然,没有发现该谣言)吓走之后,就立即又来献丑了!
本月,我们决定清理那些堵塞了收件箱的小问题,还要重点解答第一个重要的 ASP.NET 问题。除了打听我们神秘失踪(对此我们的回答只有“无可奉告”)的消息之外,如果您对 Web 小组还有什么问题,请将这些问题写在电子贺卡的背面发送到该地址:Webtalk@microsoft.com。
![*](http://img.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.gif)
本页内容
![]() | 嵌套性能 |
![]() | 上下文菜单内外 |
![]() | 框架超时 |
![]() | Web 小组短问短答 |
嵌套性能
亲爱的 Web 小组:
我一直都喜欢研究 ASP.NET。一开始,我认为它只是言过其实的自吹自擂,可是现在,当我真正有机会对它进行研究时,发现它能如此轻松地完成很多任务,尤其在显示数据方面更是如此!
但不幸的是,当我尝试嵌套 ASP:repeaters 以显示层次数据时,却卡了壳。我只是要对我拥有的所有 CD 做一个简单列表,这些 CD 是按照演唱者进行分类的。我很容易就能获得演唱者列表,但却不知道如何显示这些可爱的演唱者。
致谢!
Lisa
Web 小组的回答:
针对谁来研究这个问题,我们小组还发生了小小的争吵 — 这是我们第一个有关 ASP.NET 的问题!哈哈!我们等了好几个月了,Lisa!
这个问题实际上引入了很多新的内容,这些内容都不在我们这些小专栏的讨论范围之内,而在 ASP.NET Quickstart Guide 中进行了重点讲述。与这个问题的目标最相关的章节题目为 Data Binding Server Controls。如果真正对您口味的话,您甚至可以下载整个 .NET Framework SDK。
ASP:repeaters 的功能很强大,您几乎可以使用任何内容作为数据源。可以将其与属性、集合、表达式、数据视图,甚至函数调用的结果进行绑定。如果集合支持 ICollection、IEnumerable 或 IlistSource,您还可以对其绑定一个 repeater 控件。当然,这让我们想尽了各种绝佳的办法来解决您的问题,但最终,有一个针对新的 .NET 数据视图的简单方法在简单性和巧妙性方面更胜一筹。请允许我们介绍 Dataview.CreateChildView() 方法。它会根据指定的数据关系返回子数据表的数据视图。
为了更加简单直观地说明该方法,下面是一个示例。首先,我们对于数据输入使用了一个 XML 文件,以便您更加方便地使用,只需剪切粘贴即可。如果您还没有用于测试此示例的 .NET Web 服务器,则可以使用一些免费的 .NET 宿主服务用于测试。您可以在 GotDotNet 网站中找到这样的服务列表。
musicLibrary.xml
<Artists>
<Artist name="The Chipmunks">
<CD id="4550" name="Eponymous" image="Eponymous.jpg" />
<CD id="4557" name="Hibernation" image="Hibernation.jpg" />
<CD id="4577" name="Going Nuts" image="GoingNuts.jpg" />
</Artist>
<Artist name="The Dogs">
<CD id="4640" name="Full Moon" image="FullMoon.jpg" />
<CD id="4637" name="Old Shoes" image="OldShoes.jpg" />
<CD id="4664" name="My House" image="MyHouse.jpg" />
</Artist>
</Artists>
music.aspx
<%@ Page Language="VB" %>
<%@ Import Namespace="System.Data" %>
<html>
<script language="VB" runat="server">
Dim DS As Dataset = New DataSet
Sub Page_Load(Src As Object, E As EventArgs)
DS.ReadXml(Server.MapPath("musicLibrary.xml"))
Dim CSource As DataView
CSource = DS.Tables("Artist").defaultView
MusicList.DataSource = CSource
'a little code to help display what table relations exist
RelationsGrid.DataSource=DS.Relations
DataBind()
End Sub
</script>
<body>
<ASP:Repeater id="MusicList" runat="server">
<ItemTemplate name="ArtistTemplate">
<br /><br /><b><%# Container.DataItem("name") %></b><br />
<ASP:Repeater id="CDList" runat=server
datasource='<%# Container.DataItem.CreateChildView("Artist_CD")%>'>
<ItemTemplate name="CDTemplate">
<img src="<%# Container.DataItem("image") %>"align="absmiddle"/>
<%# Container.DataItem("name") %><br />
</ItemTemplate>
</ASP:Repeater>
</ItemTemplate>
</ASP:Repeater>
<br /><br />
<ASP:DataGrid id="RelationsGrid" runat="server"
Width="300"
BackColor="#ccccff"
BorderColor="black"
ShowFooter="false"
CellPadding=3
CellSpacing="0"
Font-Name="Verdana"
Font-Size="8pt"
HeaderStyle-BackColor="#aaaadd"
MaintainState="false"
/>
</body>
</html>
有些人可能立即会注意到,music.aspx 的第一行为 language=VB。这可不是我们的拼写错误。ASP.NET 引入了 Visual Basic® 的使用,来代替 VBScript。如果您完成了开发周期,还可以对 ASP.NET 页进行编译,以获得改善的性能。下一行也很有意思 — 我们需要为新的 dataView 对象导入命名空间。
下面您会注意到 Page_Load 方法。这又指向了 ASP.NET 另外一个有意思的变化。Page_Load 是服务器上的事件,而不是客户端上的事件!您再也无需在整个页面上到处设置 ASP 了。现在 ASP 是事件驱动的,并且进行了更加恰当的封装。我们可以一直这样讨论下去,讨论 ASP.NET 的性能如何卓越,以及它所带来的变化,但我们还得回答问题啊。如果您还想了解更多信息,请查看我们提供的一些链接。
我们首先声明了 Dataset,然后在 Page_Load 事件中,读取了 XML 文件。我们将 CSource 设置为 Artist 表的 defaultView,将 MusicList.datasource 设置为此视图。如果您仔细查看 <body> 标记,会发现第一个 repeater 服务器控件,它声明为 MusicList。该 repeater 与其他服务器控件一起,使您能够指定应该如何使用 <itemTemplate> 来表示每个元素。因为我们将 Artist 表与此 repeater 进行了绑定,所以会在项模板中输出演唱者姓名。然后,我们添加了另一个 repeater 控件为该演唱者的 CD 提供模板。
我们将父 repeater 的当前数据元素指定为 container.dataitem。为了从 Artist 获取子元素,我们将这个新 repeater 控件的数据源设置为 Container.Dataitem.CreateChildView("Artist_CD")。Artist_CD 指定创建子视图时要使用的关系。在我们的简单示例数据中,只有一个关系。请注意,我们不必亲自定义此关系;Dataset 可以根据 XML 文件中的数据识别此关系。如果您具有复杂的数据或变化的数据,则最好提供一个文档类型定义 (DTD),并指定这些关系。请注意,我们插入了 dataGrid 服务器控件 RelationsGrid,以显示该 DS 数据集中的所有关系。如果您还有一些更加复杂的 XML,则可能有兴趣了解会自动创建哪些关系。如果您不确定要调用哪些关系,这也是一个方便的小工具。
为了显示 CD 的数据,我们为嵌套的 repeater 控件和乐曲提供了另外一个项模板!啊,在 page_load 事件处理程序的最后,我们确实忽略了一个重要内容。该页的 databind 方法是用来呈现此代码的神奇咒语。它为开发人员对于何时发生什么事情提供了更好的控制。如果在绑定页面其余部分之前想要执行一些其他操作,或者您可能只需根据用户输入呈现一个控件的情况下,可以只指定一个特定的控件来绑定数据。
感谢您提出的这个难题,Lisa!既然现在 Visual Studio® .NET 已经启动,我们希望有更多的 .NET 问题。
上下文菜单内外
亲爱的 Web 小组:
我向 IE [Microsoft Internet Explorer] 上下文菜单添加了菜单项。如果我右键单击超链接时,是否能有什么办法获取该超链接地址?我可以添加菜单项,但是无法获取超链接地址。
下面的功能很有用,但还是无法获取所选择超链接的地址。
function test()
{
var parentwin = external.menuArguments;
var doc = parentwin.document;
var sel = doc.selection;
var rng = sel.createRange();
var str = new String(rng.text);
if(str.length == 0)
rng.text = "MY INSERTED TEXT";
else
rng.text = str.toUpperCase();
alert(external.menuArguments.location.href);
}
此致,
Rameswara Reddy,Chittepu
MCP
Web 小组的回答:
您所在公司很棒啊,您的问题也很好!每个月好像都会出现大量有关右键单击上下文菜单的请求。当我们检查存档时,惊奇地发现居然尚未涉猎过添加上下文菜单项的问题。在 MSDN 上有一篇有关 Internet Explorer 浏览器 的非常好的文章,其中也讲述了添加上下文菜单项的问题。从您的示例代码来看,好像您已经发现这篇文章了。您问题答案的关键在于 The Context Menu Event 一节,其中指出了可用于菜单扩展的事件对象。我们对您的代码进行了一些变化,使用了这个对象来获取触发上下文菜单事件处理程序的 srcElement,如下所示。
<SCRIPT LANGUAGE="JavaScript" defer>
var parentwin = external.menuArguments;
var doc = parentwin.document;
var element = parentwin.event.srcElement;
parentwin.alert(element.href)
</SCRIPT>
我们希望那些急切的读者自己阅读这篇文章,了解如何细化调整上下文菜单,以便查看器可以根据单击内容的类型是普通文本、图像、链接还是其他内容来获取不同的选项。我们非常喜欢 Internet Explorer 小组的同事们为我们提供的这个可扩展性和功能。
框架超时
亲爱的 Web 小组:
我制作了一个网站,该网站使用带有标头和内容的框架集。在某些图像上(标头框架中),我使用了 mouseout 事件来触发 setTimeOut 调用。我通过针对内容框架使用 cleartimeout 尝试取消超时,但是无法正常运行。取消 setTimout 并不是每次都能成功。这种情况只会发生在 IE5.0 中,在 5.5 或 6 (在这些版本上运行正常)不会发生这种情况。
下面是一些代码段:
针对标头框架内容:
<td οnmοuseοut="frames[1].timeoutvar=setTimeOut('some javascript',500)"><img></td>
针对内容框架内容:
<script>
var timeoutvar;
</script>
<td οnmοuseοver="cleartimeout(timeoutvar)"><img></td>
您是否能够告诉我如何能够使其在 IE5.0 中得以正常运行?
谢谢,
andre
Web 小组的回答:
您提出的问题是一个非常容易出现的疏漏。当前,您要在标头框架中设置一个要运行的超时,同时要在另一个框架中存储对它的引用。如果它们在同一个框架中运行,有些功能好像就能够运行得更好一些。如果为了简单起见,我们将 Frames[1] 称为 f1,只要将您标头框架的 onmouseout 更改为下列内容即可:
οnmοuseοut="f1.timeoutvar=f1.setTimeOut('some jscript',500)"
您应该会发现此代码的运行更好一些。通常情况下,至少在较早的浏览器版本中,如果您针对目标框架调用方法,而它们也存储在其中的话,性能好像就能够得到改善。当您尝试向另一个框架中的选择框添加选项时,这就尤其重要了。请确保在那个另外的框架中创建选项,以便将其添加到本地选择框中,而不要尝试跨越框架边界。
Web 小组短问短答
Image 对象是什么?
问: 想要了解有关 Microsoft JScript® 中按照下列方式实例化的 Image 对象的更多信息:
var myImg = new Image();
答:Image 对象表示一个 HTML IMG 元素,通常通过将图像预加载到浏览器缓存的方式用于改善用户体验。Image 对象具有的属性与每个 IMG 属性都相对应,因此详细信息请参阅有关 IMG 元素的 MSDN Online 文档。
受限对话
问: Greg 已经将调用文档的 innerHTML 属性值传递到了一个模式对话,并且注意到此数据的限制为 4K。
答: 设置此限制可能是为了避免任何可能的缓冲区溢出。为了规避此问题,我建议您传递文档的 body 对象,让该对话访问 innerHTML 属性。下面是一个示例。
Test.htm 的内容:
<HTML>
<BODY>
<INPUT TYPE="button" VALUE="Launch dialog"
ONCLICK="showModalDialog( 'dialog.htm', document.body );">
</BODY>
</HTML>
Dialog.htm 的内容:
<HTML>
<BODY ONLOAD=" mydiv.innerText = window.dialogArguments.innerHTML;">
<DIV ID="mydiv"></DIV>
</BODY>
</HTML>
对于 JScript 使用二进制
问: Trent 想要从 JScript 创建一个二进制文件。
答: 文件管理通常是使用 FileSystemObject 来执行的。遗憾的是,FileSystemObject 组件没有设计为支持创建二进制文件。另外一种方式是使用 ADODB 2.5 提供的 Stream 对象。Stream 对象有一个 Type 属性,可以设置为 adTypeBinary (1)。使用 Write 方法可将一个字节数组的内容写入流,然后调用 SaveToFile 方法将这些数据保存到文件中。还有一些需要注意的问题。Write 方法接受字节数组,但是 JScript 不支持字节数组。您可以使用 LoadFromFile 方法从文件加载二进制数据,也可以从返回字节数组的任何方法或属性加载二进制数据。您还可以编写一个自定义的 ActiveX® 对象来创建字节数组。有关此用法的示例,请参阅 HOWTO:Use the ADODB.Stream Object to Send Binary Files to the Browser through ASP (Q276488)。
调整对齐
问: Zvi 想要输入框中的文本右对齐。
答: 您可以使用 text-align CSS 属性设置文本框中的文本对齐:
<INPUT TYPE="text" STYLE="text-align:right" VALUE="1,000.00">
Drink Me!
问: Pete 想要利用 Microsoft Windows® 使用的缩略图生成器来显示网页和其他文件中的小图像。
答: 您可以利用 Windows 文件夹提供的 IExtractImage COM 接口。有关此接口的更多信息,请参阅 MSDN Online 文章。您还可在 GotDotNet 网站找到一个 XML Web 服务(缩略图生成器)示例。该 Web 服务可以安装在 .NET Web 服务器上,会在一个指定的 URL 提供网站的缩略图图像。对于这个 Web 服务运行的图像抽取应用程序还提供了源代码,演示了如何使用 IExtractImage 接口。
自己作出选择
问: Bharti 询问,如果 ASP 页上有多个按钮可以选择的话,如何确定单击了哪个按钮。
答: 通过为每个按钮提供不同的名称和值,可以确定用户单击了哪个按钮。按钮的名称和值是作为窗体数据的一部分进行发送的,因此您的 ASP 页可以识别该按钮。下面的示例说明了上述情况:
Test.htm 的内容:
<FORM ACTION="http://localhost/example.asp" METHOD="post">
<INPUT TYPE="SUBMIT" NAME="OK" VALUE="OK">
<INPUT TYPE="SUBMIT" NAME="Cancel" VALUE="Cancel">
</FORM>
Example.asp 的内容:
<%@ language="JScript" %>
<%
if ( Request.Form( "OK" ) == "OK" )
Response.Write( "You clicked OK" );
else
Response.Write( "You clicked Cancel" );
%>
Web 小组
Mark Davis 是 Internet Explorer SDK 小组的软件设计工程师。Mark 生于英国,目前正在接受训练,要攀登西北部一些主要山峰。
Heidi Housten 是瑞典 Microsoft 咨询服务部门的咨询人员,他有时还会花一些时间在 Developer Support 和 MSDN 网站上。她移居瑞典只是为了逃避西雅图的细雨,这只是一个谣言;她实际上是为了八月份的传统龙虾聚会而去的。
Jay Allen,Microsoft Developer Support Internet 客户端小组的支持工程师,一直渴望记事本与 Emacs Lisp 的集成。他的四个孩子大部分时间通常都在阅读数学书籍,学习日语,以及用 Haskell 编程。
Web 小组的有用提示