[ASP.NET入门随想十二] 代言人
—— 客户端行为
—— 客户端行为
■ 内与外 – property与attribute
近日Google发布除英文外的唯一名称——谷歌,中国用户一片哗然, 暂且不论这个谷歌是不是比胡弋更合适,但Google想借此阐述产品内涵,取悦中国用户之心路人皆知;今年WEB2.0引无数风投竞折腰,40亿美金撒向 大陆都是钱,于是网站们如同川剧变脸,摇身一变,换成一张张博客、书签、圈圈的面庞。冷静思考,在信息过剩,特别是同质化严重的前提下,数据挖掘、用户体 验成为网站关键,有人戏称三分长相(内容),七分打扮(内容聚集和用户体验)。即是如此,人机交互设计从可有可无提升到必不可少、Ajax技术变火也就不 奇怪了。
服务器端处理总是WEB应用系统的核心功能,大多数的信息处理在服务器端完成。但如果把所有功能都放在服务器端,让页面带着几百几千字节的 ViewState在网络中来回穿梭,那可不是恶心,那是相当的恶心。所以我们需要客户端充当服务器端的代言人,通过执行脚本程序来分担一些系统功能,让 用户更爽,让服务器更轻松。
ASP.NET通过控件与用户完成交互,但就客户端浏览器而言,它从未听过,也不和控件打交道,它所熟悉的是HTML标签和客户端脚本程序。要让客户端充 当服务器端的代言人,就必须让二者交流与沟通,即:添加客户端行为、生成客户端脚本、控件行为与客户端行为映射等。
attribute和property是一对有趣的近义词,MSDN中有这么一句话:“在 ASP.NET 服务器控件的标记中,可以使用属性 (attribute) 来设置属性 (property) 值。”属性设置属性?头晕目眩中。简单地理解,property是类的成员,attribute是类的外观。前者是品牌自身特征,后者为代言人特征,前者 可以通过后者来展示自己。反映到控件与对应HTML标签,控件状态是property,标签中对应的字符串是attribute,即浏览器所看到的服务器 端控件的外形。
■ 宣布 – 添加客户端行为
HTTP响应流就是一串字符流,页面生成一个HtmlTextWriter实例,专门收集控件们生成的HTML标记文本,它按控件树的次序,让控件的RenderControl方法检查自己的Visible属性,如果为真就调用控件的Render方法向实例添加标记文本,搞定后调用RenderChildren方法向下递归传递。所以在页面调用控件Render方法之时或之前,可以在动态地修改标签中的Attribute以组装成相应的标记文本。
向控件添加客户端行为,最简单的方法是在.apsx文件中的标签直接声明对应的Attribute,或以编程方式调用控件Attributes 集合的Add方法,即向客户端浏览器宣布:我的代言人是老徐~~
<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e){
// 为btnTest控件添加onmouseover和onmouseout客户端行为
btnTest.Attributes.Add("onmouseover","MakeRed();");
btnTest.Attributes.Add("onmouseout","RestoreColor();");
}
</script>
<html><head runat="server">
<script language="javascript">
var previousColor;
// 改变前景色
function MakeRed(){
previousColor = window.event.srcElement.style.color;
window.event.srcElement.style.color = "#FF0000";
}
// 恢复原前景色
function RestoreColor(){
window.event.srcElement.style.color = previousColor;
}
</script>
</head><body><form id="form1" runat="server">
<asp:button id="btnTest" text=”btnTest” runat="server" />
</form></body></html>
<script runat="server">
protected void Page_Load(object sender, EventArgs e){
// 为btnTest控件添加onmouseover和onmouseout客户端行为
btnTest.Attributes.Add("onmouseover","MakeRed();");
btnTest.Attributes.Add("onmouseout","RestoreColor();");
}
</script>
<html><head runat="server">
<script language="javascript">
var previousColor;
// 改变前景色
function MakeRed(){
previousColor = window.event.srcElement.style.color;
window.event.srcElement.style.color = "#FF0000";
}
// 恢复原前景色
function RestoreColor(){
window.event.srcElement.style.color = previousColor;
}
</script>
</head><body><form id="form1" runat="server">
<asp:button id="btnTest" text=”btnTest” runat="server" />
</form></body></html>
■ 注入 – 动态添加客户端脚本
除了直接在.aspx文件中显式声明外,Page类还提供了一些方法用于动态创建客户端脚本。所谓的动态生成脚本,实际上就是生成对应的脚本字符串,然后使用Page类提供的RegisterClientScriptBlock 、RegisterOnSubmitStatement等方法注入到HtmlTextWriter实例中合适的位置,同时,ASP.NET还提供若干方法用于辅助生成脚本,无非就是加点隐藏输入域、数组之类的东西。
控件也可以在OnPreRender方法调用这些方法通知页面添加所需脚本字符串。之所以不在Render方法中实现,是因为HtmlTextWriter实例在被传递到控件手上时,Page类已经完成在HtmlTextWrite实例中生成对应文本,慢了一步。下例为页面回发添加一个确定对话框。
<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(Object sender, EventArgs e){
String scriptText = "return confirm('要提交吗?')";
//添加页面提交时执行的脚本。
RegisterOnSubmitStatement("ConfirmSubmit", scriptText);
}
</script>
<html><head></head><body><form id="form1" runat="server">
<asp:button id=btnTest text=PostBack runat="server" />
</form></body></html>
<script runat="server">
protected void Page_Load(Object sender, EventArgs e){
String scriptText = "return confirm('要提交吗?')";
//添加页面提交时执行的脚本。
RegisterOnSubmitStatement("ConfirmSubmit", scriptText);
}
</script>
<html><head></head><body><form id="form1" runat="server">
<asp:button id=btnTest text=PostBack runat="server" />
</form></body></html>
■ 携手 – GetPostBackEventReference方法
很多时候我们希望客户端行为能够和相应的服务端行为携起手来,共同完成一个完整的系统功能,让客户端行为能够触发相应的服务器端行为,即两者映射。在 ASP.NET1.1中,本质上是把页面为原子回发单位,客户端行为只能在页面回发之前向隐藏标志域里写入带有自身特征的数据,页面回发后,控件们通过检 查隐藏标志域的数据来触发相应的行为。
客户端与服务端携手典型例子有两个:一是GetPostBackEventReference方法,它可以引发带有特征数据的页面回传;一是大名鼎鼎的验证器构架,除此之外,标准控件也有若干是通过客户端行为来实现。下面我们以前者为例,演示完整的“生成-客户端处理-回发-服务端处理”过程。
public class MyControl : Control, IPostBackEventHandler{
// 定义Number属性
public int Number{
get{
if ( ViewState["Number"] !=null )
return (int) ViewState["Number"];
else
return 50;
}
set { ViewState["Number"] = value; }
}
// 依赖于用户操作的事件处理,请参考《随想十一》
// eventArgument为事件数据,这里由GetPostBackEventReference设置
// 事件数据直接引起该控件状态的变化
public void RaisePostBackEvent(string eventArgument){
if ( eventArgument == "inc" ) Number ++;
if ( eventArgument == "dec" ) Number --;
}
// 生成控件对应的HTML标记文本
// 除生成Number属性文本外,还生成两个引发回发并带着回发事件参数的超链接
// GetPostBackEventReference 方法发出启动回发的客户端脚本,并提供引用
protected override void Render(HtmlTextWriter writer){
writer.Write("控件值:" + Number.ToString() + " " );
writer.Write("<a href=\"javascript:"+
Page.GetPostBackEventReference(this,"inc")+"\">+</a>");
writer.Write(" or ");
writer.Write("<a href=\"javascript:"+
Page.GetPostBackEventReference(this,"dec")+"\">-</a>");
}
}
// 定义Number属性
public int Number{
get{
if ( ViewState["Number"] !=null )
return (int) ViewState["Number"];
else
return 50;
}
set { ViewState["Number"] = value; }
}
// 依赖于用户操作的事件处理,请参考《随想十一》
// eventArgument为事件数据,这里由GetPostBackEventReference设置
// 事件数据直接引起该控件状态的变化
public void RaisePostBackEvent(string eventArgument){
if ( eventArgument == "inc" ) Number ++;
if ( eventArgument == "dec" ) Number --;
}
// 生成控件对应的HTML标记文本
// 除生成Number属性文本外,还生成两个引发回发并带着回发事件参数的超链接
// GetPostBackEventReference 方法发出启动回发的客户端脚本,并提供引用
protected override void Render(HtmlTextWriter writer){
writer.Write("控件值:" + Number.ToString() + " " );
writer.Write("<a href=\"javascript:"+
Page.GetPostBackEventReference(this,"inc")+"\">+</a>");
writer.Write(" or ");
writer.Write("<a href=\"javascript:"+
Page.GetPostBackEventReference(this,"dec")+"\">-</a>");
}
}