9.1.4 动态生成代码
编写客户端代码的另一种方法是动态生成代码,然后把它们插入到页面中。生成动态客户端代码的第一步是在服务器端代码中,准备好要添加到页面上的客户端代码,再把它们连接到一个字符串变量上。
可以建立的代码没有范围限制,但这里只探讨简单的SayHello示例。下面的Visual Basic 2005示例把SayHello程序放在一个简单的字符串中。
Dim strSayHello As String
strSayHello = "<script>function SayHello(){window.alert(" & _
"'Hello, World');}</script>"
C#的对应代码如下所示。
string strSayHello;
strSayHello = "<script>function SayHello(){window.alert(" +
"'Hello, World');}</script>";
提示:
即使代码是动态生成的,也最好先在页面的Source视图中编写代码的最初静态版本。这将能利用Visual Studio 2005编辑器的所有IntelliSense支持功能以及某些语法检查。它还有助于对静态代码进行某些测试,之后再把代码从页面中剪切出来,集成到服务器端代码中。
下一步是把这个例程从服务器端代码添加到页面中。最简单的方式是使用Response对象的Write方法,如下面的Visual Basic 2005版本所示。
Me.Page.Response.Write(strSayHello)
C#的对应代码如下所示。
this.Page.Response.Write(strSayHello);
但是,这种方法有两个问题,第一个问题是,我们无法控制这些客户端代码会添加到什么地方。例如,使用WebForm的Page_Load事件,脚本块会插入到Web页面开头的HTML和DOCTYPE标记前面(但这些代码仍在Internet Explorer中执行)。
一般情况下,脚本可以放在以下两个地方。
● 窗体的开头,这样,在用户使用与该代码关联的控件时,这些代码肯定已由浏览器处理好了。
● 窗体的结尾,这样代码就会在所有控件都已成功加载后执行。
使用Reponse.Write给页面添加客户端代码的第二个问题是:如何防止控件的不同副本在页面上重复添加脚本块?例如,如果创建了一个添加客户端代码的控件,使用该控件的开发人员就可以在他建立的页面上放置该控件的两个或更多副本。如果控件的两个副本在页面上插入客户端代码,执行这些代码的结果就很难预测,但其结果肯定不是我们所希望的。
第二个问题的解决方式是把页面中的脚本块看做“库”块:该块包含许多有用的例程,可以在页面上控件的所有副本中调用。我们只需确保,把库块添加到页面上后,其他控件就不再添加库了。
控制代码位置和防止代码添加多次的问题是用ClientScriptManager对象的RegisterClient ScriptBlock和RegisterStartUpScript方法解决的,该对象的方法支持大多数客户端脚本操作,在Page对象的ClientScript属性中可以获得页面的ClientScriptManager。
这两个方法的区别是放置代码的位置。
● RegisterClientScriptBlock把代码放在窗体开标记的后面,窗体开标记将开始页面上的窗体。
● RegisterStartUpScript把代码放在窗体闭标记的前面,窗体闭标记将结束页面上的窗体。
提示:
RegisterStartUpScript可以用于添加包含代码的脚本块,但这些代码不在例程中,换句话说,代码在页面加载过程中执行。RegisterStartUpScript代码放在窗体的最后,所以窗体上的控件已加载,可用于客户端代码处理了。所以,添加要在用户执行操作前执行的代码时,应选择RegisterStartUpScript。例如在页面第一次显示时,把光标定位在某个字段中的代码。
注意:
如本节所述,Page的ClientScriptManager对象有许多方法,在生成客户端代码时使用。在ASP.NET 1.x中,一些方法可直接在Page对象上使用,例如RegisterClientScriptBlock和RegisterStartUpScript。但是,这些方法现在已标记为废弃,我们应使用可在ClientScriptManager对象中使用的版本。
这两个Register*方法都接受四个参数。
● 系统类型:对于这个参数,只需传送自定义控件的类型。这个参数允许ASP.NET将不同控件的脚本分隔开。
● 脚本块的键:这个键允许ASP.NET检查脚本块是否已添加。
● 脚本:这个参数连接到一个字符串上。
● 布尔值:其值设置为True时,表示脚本应放在<script>元素中。如果把这个参数设置为False,就需要在代码中包含<script>元素。这个参数是可选的,默认为False。
提示:
如果为组成控件生成脚本块,仍要把自定义控件的类型传送给Register*事件,而不是传送组成控件的类型。因为自定义控件有唯一的类型,但组成控件比较常见,例如文本框、列表框和其他ASP.NET控件。
下面的Visual Basic 2005把SayHello例程的代码放在窗体开头的<script>元素中。
Dim csm As System.Web.UI.ClientScriptManager
csm = Me.Page.ClientScript
csm.RegisterClientScriptBlock(Me.GetType, "PHVBlock", strSayHello, True)
C#的对应代码如下所示。
System.Web.UI.ClientScriptManager csm;
csm = this.Page.ClientScript;
csm.RegisterClientScriptBlock(this.GetType(), "PHVBlock", strSayHello, true);
得到的JavaScript代码如下所示。
<script type="text/javascript">
<!--
function SayHello(){window.alert('Hello, World');}// -->
</script>
提示:
给页面添加代码时,常常要把控件名合并到代码中。在运行期间,可以使用控件的UniqueId属性检索出控件名,如第3章所述。
如果需要客户端代码处理封装Web Part的HTML结构,可参阅第5章中对Web Part的HTML结构的描述。
除了ClientScript对象的RegisterClientScriptBlock和RegisterStartupScriptBlock方法之外,还可以使用RegisterOnSubmitStatement把代码添加到页面上。RegisterOnSubmitStatement会在主页上添加脚本块,该脚本块没有与任何控件关联在一起,但在页面传送回服务器之前执行。下面的Visual Basic 2005示例就把SayHello例程关联到页面的提交事件上。
Dim csm As System.Web.UI.ClientScriptManager
csm = Me.Page.ClientScript
csm.RegisterOnSubmitStatement(“PHVSubmitBlock", strSayHello)
C#的对应代码如下所示。
System.Web.UI.ClientScriptManager csm;
csm = this.Page.ClientScript;
csm.RegisterOnSubmitStatement("PHVSubmitBlock", strSayHello);
添加脚本块时使用的键允许使用Page的IsClientScriptBlockRegistered方法,确定该块是否已添加(类似于Is*Registered方法检查其他Register*方法的结果)。给IsClientScriptBlock Registered方法传送脚本块的键,如果带有该键的客户脚本块已添加到页面上,该方法就返回True。
下面的Visual Basic 2005代码利用IsClientScriptBlockRegistered方法来添加SayHello例程,可避免重复添加该例程。
Dim csm As System.Web.UI.ClientScriptManager
csm = Me.Page.ClientScript
If csm.IsClientScriptBlockRegistered(“PHVBlock") = False Then
csm.RegisterClientScriptBlock(“PHVBlock", strSayHello)
End If
C#的对应代码如下所示。
System.Web.UI.ClientScriptManager csm;
csm = this.Page.ClientScript;
if(csm.IsClientScriptBlockRegistered("PHVBlock") == false)
{
csm.RegisterClientScriptBlock("PHVBlock", strSayHello);
}
使用Register*方法的其他注意事项如下所示。
● Is*Registered方法是可选的。如果带有指定键的项已添加,Register*方法什么都不会添加,也不会引发错误。
● 必须在Render方法之前调用Register*方法,即在PreRender事件中或之前调用。
● 用某个Register方法添加的所有脚本都放在同一个<script>元素中,例如,用RegisterClientScriptBlock添加的所有脚本代码都放在窗体开头的<script>元素中。
● 在RegisterClientScriptBlock、RegisterOnSubmitBlock和RegisterStartupScript中指定的键是彼此独立的。如果用RegisterClientScriptBlock方法添加键为PHVBlock的脚本块,也可以使用其他Register*方法添加带有该键的块。






