前言: “不只普通民众是傻瓜,软件开发工程师同样也是傻瓜”——微软的软件产品开发指导理念是路人皆知的,当初我踏入软件行业时由于没有洞悉微软的这个本质,导致现在已经是上了贼船难再下了。算了,既然微软已经把我当傻瓜看,如果我连傻瓜式的东西都用不熟,那就真成了傻瓜了。当然,说微软的东西都是傻瓜式的,有点“得了便宜反卖乖”的嫌疑,但从另一方面来考虑的话,就应该赞扬微软技术的简易实用,对于快速开发同时对性能要求不算苛刻的项目来说是非常好的选择。
言归正传,ASP.NET在应用AJAX方面提供了一系列的服务器控件,如截图所示:
这些控件的作用大致可以概括为(具体的用法会在后面一一介绍):
ScriptManager:这是整个AJAX-Extensions的核心控件,也是整个ASP.NET的关键服务器控件。从该控件的名字可以看出,该控件用于管理最终呈现(术语:Render)页面中的脚本资源,也就是由它来添加和删除某些脚本。ScriptManager控件与静态类System.Web.UI. ScriptManager相互配合(如ScriptManager类使用最多的 RegisterClientScriptBlock、RegisterStartupScript等等注册脚本方法),能够灵活有效地控制最终呈现页面上的脚本。当然,ScriptManager核心作用还是在实现AJAX方面。需要注意的是:一个页面上只能存在最多一个ScriptManager控件。
ScriptManagerProxy:这个控件是为了适用有母页面的情况。当母页面已经存在ScriptManager时,因为一个页面上只能存在最多一个ScriptManager控件,这样Content页面就不能存在一个独立的ScriptManager控件,而替代使用ScriptManagerProxy控件,然后通过它访问母版页的ScriptManager,作用相当于对母页面ScriptManager的代理。
UpdatePanel:这个控件也是一个关键控件,该控件定义一个“相对独立的AJAX区域”,每个页面上可以存在任意个UpdatePanel控件,这些UpdatePanel之间可以通过其内部参数设置来决定是否相互影响。该控件具体的使用方法下面马上就会说明,总之,该控件创建一个更新AJAX的区域块,以及该区域块如何更新、何时更新、由谁来更新等等事项。
Timer:这个控件功能单一,独立属性只有Interval(设定触发Tick事件的时间间隔,单位是毫秒),独立事件只有Tick(定时执行事件)。其作用在于定时执行Tick事件,PostBack回服务器。如果该控件是某个UpdatePanel的更新触发器,那么将引起该UpdatePanel的更新,否则将引起整个页面的重载(即重新走一遍页面周期)。
UpdateProgress:与Timer控件地位相似,它也是个辅助控件。作用在于当该控件所在的UpdatePanel控件内已经PostBack之后,服务器返回结果之前的这段时间显示出来,而在未PostBack或服务器已经返回结果之后隐藏。目的在于提示用户:请求已经发送,正在处理。
一,一个简单的使用ScriptManager和UpdatePanel来实现AJAX更新的实例:
前台代码:
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Button ID="aspbtn_trigger" runat="server" Text="点击我更新文本" OnClick="aspbtn_trigger_Click" />
<asp:Label ID="asplbl_Context" runat="server" Text="这是原始文本"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
后台代码:
protected void aspbtn_trigger_Click(object sender, EventArgs e)
{
asplbl_Context.Text = "这是AJAX获取后的文本,用于测试使用UpdatePanel实现Ajax请求";
}
运行页面可以发现,点击按钮后页面并没有刷新一遍,而只有文本更换而已,页面的其他部分完全没有变化。这就是实现Ajax请求的结果。
二,UpdatePanel的比较详细的用法:
注意!!:这里所说的“AJAX区域”就是由UpdatePanel控件内ContentTemplate子标签所包括的范围,这是一个Ajax更新的“相对独立块”
UpdatePanel有以下几个重要的参数设置:
1,UpdateMode属性:参数用于设定本AJAX区域的“更新时机”。UpdateMode有两中选项,Always和Conditional,其中Always表示只要页面任何AJAX区域内发生了PostBack事件,本AJAX区域都将更新,该设定为默认值;Conditional表示只有该AJAX区域内部的PostBack事件才引起的本AJAX区域的更新。注意:不属于任何AJAX区域内的PostBack事件因为会引起整个页面的重新载入,当然会引起各个AJAX区域的更新,但这种更新不是AJAX更新,本页面要讨论的是AJAX局部更新,切记!!
2,RenderMode属性:这个参数功能很简单,用于设定本AJAX区域的样式堆积方式:“内联”还是“块状”。默认使用rendermode="Block",最终解析的AJAX区域用div标签(div为块状元素)包含;而使用rendermode="Inline"则最终解析的AJAX区域用span标签(span为内联元素)包含。
3,ChildrenAsTriggers属性:该参数用于设定本AJAX区域的PostBack事件是否引起该区域的更新,该参数可以看成是当UpdateMode="Conditional"时,对本区域的更新时机设定起到进一步补充作用。默认为true,表示该区域内部的PostBack事件将引起本区域的更新。 但该参数设定为false时,要求UpdateMode="Conditional",否则页面会抛出异常。ASP.NET进行这样限定的原因在于,UpdateMode="Always"表示所有AJAX区域内的PostBack事件都引起区域的更新,这其中当然包括本区域内部的PostBack事件。因此ChildrenAsTriggers="false"与UpdateMode="Always"是不能同时存在的。
4,AsyncPostBackTrigger子元素:这里设定元素(无论该参数是否位于UpdatePanel区域内)的PostBack事件,都会引起本区域的更新;包含两个参数:"ControlID":ASP.NET控件ID、"EventName":引起该控件PostBack的事件名称,如Click、TextChanged
5,PostBackTrigger子元素:这里设定元素的所有的PostBack事件都将被本区域视为非AJAX请求而载入整个页面(注意:这里的元素可以为本AJAX区域内的,亦可以为其他区域(一般很少用到);设定为本区域较常见(如用到FileUpload控件上传附件的时候,触发PostBack元素一定要设为PostBackTrigger,否则文件会丢失))
下面是对上面几个重要参数设置的示例:
前台代码:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<hr style="border: 3 double #987cb9" width="100%" color="#987cb9" size="3" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<p class="title">
这是AJAX区域1,用于测试参数——UpdateMode,该参数用于设定本AJAX区域的“更新时机”。<br />
UpdateMode有两中选项,Always和Conditional,其中Always表示只要页面任何AJAX区域内发生了PostBack事件,本AJAX区域都将更新,该设定为默认值;Conditional表示只有该AJAX区域内部的PostBack事件才引起的本AJAX区域的更新。<br />
注意:不属于任何AJAX区域内的PostBack事件因为会引起整个页面的重新载入,当然会引起各个AJAX区域的更新,但这种更新不是AJAX更新,本页面要讨论的是AJAX局部更新,切记!!
</p>
<p>
关键设置:UpdateMode="Conditional"
</p>
<asp:Button ID="aspbtn_trigger1" runat="server" Text="点击我将引起AJAX区域1的更新" OnClick="aspbtn_trigger1_Click" />
<asp:Label ID="asplbl_Context1" runat="server" Text="这是AJAX区域1的原始文本"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
<hr style="border: 3 double #987cb9" width="100%" color="#987cb9" size="3" />
<asp:UpdatePanel ID="UpdatePanel2" runat="server" RenderMode="Inline">
<ContentTemplate>
<p class="title">
这是AJAX区域2,用于测试RenderMode,该参数用于设定本AJAX区域的样式堆积方式:“内联”还是“块状”<br />
默认使用rendermode="Block",最终解析的AJAX区域有div标签(div为块状元素)包含;而使用rendermode="Inline"则最终解析的AJAX区域有span标签(span为内联元素)包含。
若查看最后生成的页面标签,可以看到区域最终生成<span id="UpdatePanel2">标签,而区域1生成 <div id="UpdatePanel1">
</p>
<p>
关键设置:RenderMode="Inline"
</p>
<asp:Button ID="aspbtn_trigger2" runat="server" Text="这是非AJAX区域1内的按钮,点击我AJAX区域1内将无变化,虽然我调用的方法与aspbtn_trigger1按钮一致"
OnClick="aspbtn_trigger1_Click" />
</ContentTemplate>
</asp:UpdatePanel>
<hr style="border: 3 double #987cb9" width="100%" color="#987cb9" size="3" />
<asp:UpdatePanel ID="UpdatePanel3" runat="server" ChildrenAsTriggers="false" UpdateMode="Conditional">
<ContentTemplate>
<p class="title">
这是AJAX区域3,用于测试ChildrenAsTriggers,该参数用于设定本AJAX区域的PostBack事件是否引起该区域的更新,该参数可以看成是当UpdateMode="Conditional"时,对本区域的更新时机设定起到进一步补充作用<br />
默认为true,表示该区域内部的PostBack事件将引起本区域的更新。 但该参数设定为false时,要求UpdateMode="Conditional",否则页面会抛出异常。ASP.NET进行这样限定的原因在于,UpdateMode="Always"表示所有AJAX区域内的PostBack事件都引起区域的更新,这其中当然包括本区域内部的PostBack事件。因此ChildrenAsTriggers="false"与UpdateMode="Always"是不能同时存在的。
</p>
<p>
关键设置:ChildrenAsTriggers="false" UpdateMode="Conditional"
</p>
<asp:Button ID="aspbtn_trigger3" runat="server" Text="虽然我位于区域3,但是点击我本区域的文本将不会更新"
OnClick="aspbtn_trigger3_Click" />
<asp:Label ID="asplbl_Context3" runat="server" Text="这是AJAX区域3的原始文本"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
<hr style="border: 3 double #987cb9" width="100%" color="#987cb9" size="3" />
<asp:UpdatePanel ID="UpdatePanel4" runat="server">
<ContentTemplate>
<p class="title">
这是AJAX区域4,用于测试Triggers子元素,该元素用于设定本AJAX区域对特殊PostBack事件的“响应方式”,包含两种大类:<br />
1,AsyncPostBackTrigger:这里设定元素(无论该参数是否位于UpdatePanel区域内)的PostBack事件,都会引起本区域的更新;包含两个参数:"ControlID":ASP.NET控件ID、"EventName":引起该控件PostBack的事件名称,如Click、TextChanged、</p>
2,PostBackTrigger:这里设定元素的所有的PostBack事件都将被本区域视为非AJAX请求而载入整个页面(注意:这里的元素可以为本AJAX区域内的,亦可以为其他区域(一般很少用到);设定为本区域较常见(如用到FileUpload控件上传附件的时候,触发PostBack元素一定要设为PostBackTrigger,否则文件会丢失))
<br />
<p>
关键设置:
<asp:AsyncPostBackTrigger ControlID="aspbtn_outer1" EventName="Click" />
<asp:PostBackTrigger ControlID="aspbtn_trigger4" />
</p>
<asp:Button ID="aspbtn_trigger4" runat="server" Text="虽然我位于AJAX区域4,但是点击我将引起整个页面的重载,而不是本区域的AJAX更新,原因在于PostBackTrigger中设定我非AJAX方式Postback"
OnClick="aspbtn_outer1_Click" />
<asp:Label ID="asplbl_Context4" runat="server" Text="这是AJAX区域4的原始文本"></asp:Label>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="aspbtn_outer1" EventName="Click" />
<asp:PostBackTrigger ControlID="aspbtn_trigger4" />
</Triggers>
</asp:UpdatePanel>
<hr style="border: 3 double #987cb9" width="100%" color="#987cb9" size="3" />
<p class="title">
这是非AJAX区域</p>
<asp:Button ID="aspbtn_outer1" runat="server" Text="虽然我位于不属于任何一个AJAX区域,但是我能引起区域4的更新,且不刷新这个页面,因为在区域4的AsyncPostBackTrigger中设定了我作为其更新的发生器"
OnClick="aspbtn_outer1_Click" />
</form>
后台代码:
protected void aspbtn_trigger1_Click(object sender, EventArgs e)
{
asplbl_Context1.Text = "这是AJAX获取后的文本,用于测试区域1内使用UpdatePanel实现Ajax请求,随机数:" + new Random().Next(100).ToString();
}
protected void aspbtn_trigger3_Click(object sender, EventArgs e)
{
asplbl_Context3.Text = "这是AJAX获取后的文本,用于测试区域3内使用UpdatePanel实现Ajax请求,随机数:" + new Random().Next(100).ToString();
}
protected void aspbtn_outer1_Click(object sender, EventArgs e)
{
asplbl_Context4.Text = "这是AJAX获取后的文本,用于测试区域4内使用UpdatePanel实现Ajax请求,随机数:" + new Random().Next(100).ToString();
}
三,使用ScriptManager调用WebService实现AJAX请求:
ScriptManager功能很强大,但许多设置因为几乎没有用过所以我在这里不敢说教了。这里只介绍如何调用Webservice。关于Webservice的基本知识假如读者还不具备的话,那么读者务必要先进行相关的了解才能有效地理解接下来的示例了。
首先创建一个Webservice文件,位置为Resources/WebServiceForAJAX.asmx,代码如下:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class WebServiceForAJAX : System.Web.Services.WebService
{
[WebMethod]
public string GetString()
{
return "随机数" + new Random().Next(100).ToString();
}
[WebMethod]
public int GetInt(int input)
{
return input + 100;
}
[WebMethod]
public double GetDouble(double inputX, double inputY, double inputZ)
{
return inputX + inputY + inputZ;
}
[WebMethod]
public double RuntimeError(string input)
{
return Convert.ToDouble(input);
}
[WebMethod]
public double ThrowError(string input)
{
throw new ArgumentException("这里故意抛出异常,请忽视该异常!");
}
}
然后在页面中添加ScriptManager控件,并进行Webservice服务的引用,如下所示:
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/Resources/WebServiceForAJAX.asmx" />
</Services>
</asp:ScriptManager>
上面两步做好后就可以使用Javascript来直接调用。好吧,不得不承认的确是傻瓜化的。具体的用法如下:
Javascript代码:
function CallWebService_None() {
AJAX_Extensions.Resources.WebServiceForAJAX.GetString(SuccessHandle);
}
function CallWebService_One() {
AJAX_Extensions.Resources.WebServiceForAJAX.GetInt(49, SuccessHandle);
}
function CallWebService_Multitude() {
AJAX_Extensions.Resources.WebServiceForAJAX.GetDouble(0.111, 0.222, 0.333, SuccessHandle);
}
function CallWebService_Error() {
//这里调用GetDouble方法,但是传入的参数都应该为double,这里故意将参数传入不能转换为double的字符串,前台JS获取异常后将转到异常处理方法。
AJAX_Extensions.Resources.WebServiceForAJAX.GetDouble(0.111, 'NotANumber', 0.333, SuccessHandle, ErrorHandle);
}
function CallWebService_RuntimeError() {
//这里调用的webservice方法传入参数符合要求,但是在方法内部试图将字符串转化为数字过程中抛出异常,前台JS获取异常后将转到异常处理方法。
AJAX_Extensions.Resources.WebServiceForAJAX.RuntimeError('input', SuccessHandle, ErrorHandle);
}
function CallWebService_ThrowError() {
//这里调用的webservice方法传入参数符合要求,但是在方法内部主动抛出异常,前台JS获取异常后将转到异常处理方法。
AJAX_Extensions.Resources.WebServiceForAJAX.ThrowError('input', SuccessHandle, ErrorHandle);
}
function CallWebService_userContext() {
var userContext = 0.111;
AJAX_Extensions.Resources.WebServiceForAJAX.GetDouble(userContext, 0.222, 0.333,
function(_returnval, userContext, addedParam1, addedParam2, addedParam3) {
alert("返回值:" + _returnval + ",回调方法附加参数userContext:" + userContext + "、第一个额外参数addedParam1为被置为:" + addedParam1 + "、第二个额外参数addedParam2:" + addedParam2 + "、第三个额外参数addedParam3:" + addedParam3);
}, null, userContext);
}
function SuccessHandle(_val) { //成功回调方法
alert("调用Webservice成功,返回结果为:" + _val.toString());
}
function ErrorHandle(_val) { //异常回调方法
alert("调用Webservice出错,错误信息:" + _val._message); //这里讲异常提示信息用弹出框显示出来
}
页面代码:
<input type="button" id="btn_trigger_none" value="点击我调用Webservice无参数方法" οnclick="CallWebService_None();" />
<input type="button" id="btn_trigger_one" value="点击我调用Webservice一个参数方法" οnclick="CallWebService_One();" />
<input type="button" id="btn_trigger_multitude" value="点击我调用Webservice多个参数方法" οnclick="CallWebService_Multitude();" />
<input type="button" id="btn_trigger_error" value="点击我调用Webservice出错处理" οnclick="CallWebService_Error();" />
<input type="button" id="btn_trigger_runtimeerror" value="点击我调用Webservice产生运行时错误"
οnclick="CallWebService_RuntimeError();" />
<input type="button" id="btn_trigger_throwerror" value="点击我调用Webservice截取主动抛出的错误"
οnclick="CallWebService_ThrowError();" />
<input type="button" id="btn_trigger_userContext" value="点击我调用Webservice使用userContext"
οnclick="CallWebService_userContext();" />
这里对ScriptManager调用Webservice总结如下:
使用ScriptManager控件调用Webservice有以下几个条件:
1,form元素存在,且runat设置为server2,ScriptManager控件存在
3,子元素Services中要将该Webservice文件的地址包括进来,如 asp:ServiceReferencePath="~/Resources/WebServiceForAJAX.asmx"
4,webservice类必须使用[System.Web.Script.Services.ScriptService]特性。
5,被调用的方法必须使用 [WebMethod]特性
6,被调用的方法必须为非静态方法,即不使用static关键字
调用具体Webservice的方法注意事项:
1,Webservice类在JS的对象为包括【命名空间+类名】,如在AJAX_Extensions.Resources.WebServiceForAJAX中AJAX_Extensions.Resources为命名空间,WebServiceForAJAX为Webservice类名
2,JS调用Webservice方法传入的参数依次为【方法本身需要传入的参数(如果有,则必要)+成功回调方法(非必要)+异常回调方法(非必要)+回调函数额外的参数(非必要)】
假如Webservice方法为public double GetDouble(doubleinputX, double inputY, double inputZ),则可以使用如下方法调用:AJAX_Extensions.Resources.WebServiceForAJAX.GetDouble(0.111,0.222,0.333, SuccessHandle, ErrorHandle,userContext);,
其中“0.111,0.222,0.333”分别对应参数“doubleinputX, double inputY, double inputZ”;SuccessHandle为成功回调方法;ErrorHandle为异常回调方法;userContext为回调函数额外的参数
几个概念——
成功回调方法: 当Webservice方法成功调用后执行的方法;
异常回调方法: 当Webservice方法调用出现异常后执行的方法;
回调函数额外的参数:成功回调方法和异常回调方法一般情况下参数都只有一个,当需要添加额外的参数数时,必须在“回调函数额外的参数(非必要)”中添加该参数,然后才能在回调方法中使用,否则所有传入的额外参数除第一个被置为Webservice方法名称外,其他参数都会被置为undefined
四,UpdateProgress控件的使用实例:
前台代码:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Button ID="aspbtn_trigger1" runat="server" Text="点击我进行更新" OnClick="aspbtn_trigger1_Click" />
<asp:Label ID="asplbl_Context1" runat="server" Text="这是原始文本"></asp:Label>
<asp:UpdateProgress ID="UpdateProgress1" runat="server">
<ProgressTemplate>
<img src="Resources/ajax-loader.gif" />
</ProgressTemplate>
</asp:UpdateProgress>
</ContentTemplate>
</asp:UpdatePanel>
</form>
后台代码:
protected void aspbtn_trigger1_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(2000); //这里故意将线程处理时间延长2秒,以模拟服务器正在处理请求的阶段
asplbl_Context1.Text = "这是AJAX获取后的文本,随机数:" + new Random().Next(100).ToString();
}
可以看出,在点击按钮后,因为在后台System.Threading.Thread.Sleep(2000)估计将服务器响应延长2秒,在浏览器发出请求后,UpdateProgress中的内容就显示出来了,而在浏览器接收到响应后又消失了。因此可以得出结论:该控件作用是在服务器响应过程中显示,一般用于对用户的提示作用。
五, Timer控件的使用实例:
前台代码:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<p>
本区域将每个3秒进行一次自动更新,更新目标文本和当前时间
</p>
<asp:Literal ID="aspltl_CurrentTime" runat="server"></asp:Literal>
<br />
<asp:Label ID="asplbl_Context1" runat="server" Text="这是原始文本"></asp:Label>
<asp:UpdateProgress ID="UpdateProgress1" runat="server">
<ProgressTemplate>
<img src="Resources/ajax-loader.gif" />
</ProgressTemplate>
</asp:UpdateProgress>
<asp:Timer runat="server" Interval="3000" OnTick="Unnamed1_Tick">
</asp:Timer>
</ContentTemplate>
</asp:UpdatePanel>
</form>
后台代码:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
aspltl_CurrentTime.Text = "当前时间:" + DateTime.Now.ToString();
}
}
protected void Unnamed1_Tick(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(2000); //这里故意将线程处理时间延长2秒,以模拟服务器正在处理请求的阶段
asplbl_Context1.Text = "这是AJAX获取后的文本,随机数:" + new Random().Next(100).ToString();
aspltl_CurrentTime.Text = "当前时间:" + DateTime.Now.ToString();
}
这里是我为这篇文章配置的简单测试代码,点击下载