ASP.NET AJAX UpdatePanel 控件实现剖析

使用ASP.NET AJAX框架我们可以搭建快速响应、具有丰富的用户体验的AJAX Web应用程序,而该框架的UpdatePanel控件则提供了一种非常简单的方式来实现Web页面的局部更新,我们不需要在每次回发的时候都加载整个页面。

 

那这个控件是如何实现这种局部刷新的哪,透过其实现机制我们可以更清楚其优缺点,便于我们确定其使用场合。本文将重点阐述ASP.NET AJAX控件UpdatePanel的实现机制。

 

1. ASP.NET AJAX 简介 ASP.NET AJAX是微软在ASP.NET 2.0之上对AJAX技术的一个封装,为Web应用程序提供完整的AJAX解决方案。ASP.NET AJAX有两种编程模型:部分更新和远程服务。

 

部分更新使得用户可以用传统的ASP.NET 2.0应用程序的方式来搭建AJAX应用,具体就是使用UpdatePanel控件来实现无闪烁页面更新。而远程服务则是直接通过前端JavaScript来调用的服务器端服务,前段获取数据后,进行页面更新,这就要求服务器端代码必须分解为特定于应用程序的服务,这是与传统的ASP.NET应用程序完全不同的体系结构。

 

部分更新着重于对现有应用程序进行渐进式增强,帮助用户逐渐转换到纯粹的AJAX应用。本文主要对部分更新编程模型中核心控件UpdatePanel的实现进行剖析,讲述其背后的故事。

 

ASP.NET AJAX框架分为客户端以及服务器端两个部分,基于客户端的 Microsoft AJAX Library包含了对浏览器兼容性、网络访问以及客户端控件组件等支持, 而服务器端则包括了服务器控件,Web 服务等等。

 

见下图:

 

 

Microsoft Ajax Library就是ASP.NET AJAX的客户端脚本库,其中MicrosoftAjax.js包含了ASP.NET AJAX的核心内容,包括跨浏览器的支持、基于面向对象对JavaScript的扩展以及网络访问等等。MicrosoftAjaxWebForm.js文件则是完全服务于ASP.NET AJAX页面局部更新这样一个功能的,在该文件中定义了一个客户端对象PageRequestManager,该对象将会负责客户端异步回送的全过程。

 

2. ScriptManager 和 UpdatePanel ScriptManager和UpdatePanel是ASP.NET AJAX服务器端中最重要的两个控件,ScriptManager控件用来管理ASP.NET页面中的客户端脚本,生成及注册所需要的客户端脚本,通过UpdatePanel控件可以更新页面的指定部分而无需加载整个页面。

 

看个例子:

<form id="form1" runat="server">
   <asp:ScriptManager ID="ScriptManager1" runat="server">
   </asp:ScriptManager>
   <div>
      <asp:UpdatePanel ID="UpdatePanel1" runat="server">
           <ContentTemplate>
               <%= DateTime.Now %>
                <br />
               <asp:Button ID="Button1" runat="server" Text="Button" />
           </ContentTemplate>
       </asp:UpdatePanel>
   </div>
</form>

 

构建如上代码所示的页面,在Runtime点击UpdatePanel中的Button控件,则不会引起整个页面刷新,只是用来显示当前时间的Label得到更新。

这是如何实现的哪?

 

3. ASP.NET AJAX部分呈现剖析

 

3.1 先从客户端讲起

 

看一下上面的示例代码在客户端的HTML代码, 这里只列出核心部分,其他全部隐去。

<script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('ScriptManager1', document.getElementById('form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], [], [], 90);
//]]>
</script>

<div id="UpdatePanel1">
     7/25/2008 4:54:36 PM
     <br />
     <input type="submit" name="Button1" value="Button" id="Button1" />
</div>

 

看一下上面的两句JavaScript代码,第一句代码中的_initialize 方法是客户端PageRequestManager对象上的静态方法,它会创建一个 PageRequestManager 类的全局实例,并将其初始化。在这个初始化函数中,ageRequestManager对象注册了当前表单对象的submit事件,以及window对象的load和unload事件。

 

而第二句代码则是通过PageRequestManager的getInstance方法来检索其唯一实例, 得到该实例后调用_updateControls方法来注册UpdatePanel以及其Trigger控件。

 

我们可以从MicrosoftAjaxWebForm.js文件中得到_updateControls方法的声明:

function Sys$WebForms$PageRequestManager$_updateControls(updatePanelIDs, asyncPostBackControlIDs, postBackControlIDs, asyncPostBackTimeout) {}

由其中第一个参数代表了当前页面上所有的UpdatePanel控件的ID集合,如果该UpdatePanel的ChildrenAsTrigger为True的话,应在ID前添加字符't',否则添加字符'f';而第二个参数是所有引发异步回送的控件ID;第三个参数是所有引发同步回送的控件ID;第四个参数设定了异步回送的Timeout时间,单位为秒。于PageRequestManager对象注册了当前表单的submit时间,所以每当页面有提交动作的时候,PageRequestManager对象就会介入,看一下PageRequestManager对象页面提交处理函数_onFormSubmit(evt)。

 

如果需要执行一次异步回送的话,会中止原有的普通浏览器会回发,代之使用XMLHttpRequest进行AJAX回发。在封装这个请求的时候,当前页面的所有字段以及视图状态都会被打包在请求中,另外还设置了这次Request的HTTP头:request.get_headers()['X-MicrosoftAjax'] = 'Delta=true';

 

在服务器端将会根据这个HTTP头标记来判定是否为一次AJAX异步回发。

 

_onFormSubmit(evt)函数代码:

function Sys$WebForms$PageRequestManager$_onFormSubmit(evt) {
        var continueSubmit = true;
        var isCrossPost = this._isCrossPost;
        this._isCrossPost = false;
        if (this._onsubmit) {
            continueSubmit = this._onsubmit();
        }
        if (continueSubmit) {
            for (var i = 0; i < this._onSubmitStatements.length; i++) {
                if (!this._onSubmitStatements[i]()) {
                    continueSubmit = false;
                    break;
                }
            }
        }
        if (!continueSubmit) {
            if (evt) {
                evt.preventDefault();
            }
            return;
        }
        var form = this._form;
        if (isCrossPost) {
            return;
        }
        if (this._activeDefaultButton && !this._activeDefaultButtonClicked) {
            this._onFormElementActive(this._activeDefaultButton, 0, 0);
        }
        if (!this._postBackSettings.async) {
            return;
        }
        var formBody = new Sys.StringBuilder();
        formBody.append(encodeURIComponent(this._scriptManagerID) + '=' + encodeURIComponent(this._postBackSettings.panelID) + '&');
        var count = form.elements.length;
        for (var i = 0; i < count; i++) {
            var element = form.elements[i];
            var name = element.name;
            if (typeof(name) === "undefined" || (name === null) || (name.length === 0)) {
                continue;
            }
            var tagName = element.tagName;
            if (tagName === 'INPUT') {
                var type = element.type;
                if ((type === 'text') ||
                    (type === 'password') ||
                    (type === 'hidden') ||
                    (((type === 'checkbox') || (type === 'radio')) && element.checked)) {
                    formBody.append(encodeURIComponent(name));
                    formBody.append('=');
                    formBody.append(encodeURIComponent(element.value));
                    formBody.append('&');
                }
            }
            else if (tagName === 'SELECT') {
                var optionCount = element.options.length;
                for (var j = 0; j < optionCount; j++) {
                    var option = element.options[j];
                    if (option.selected) {
                        formBody.append(encodeURIComponent(name));
                        formBody.append('=');
                        formBody.append(encodeURIComponent(option.value));
                        formBody.append('&');
                    }
                }
            }
            else if (tagName === 'TEXTAREA') {
                formBody.append(encodeURIComponent(name));
                formBody.append('=');
                formBody.append(encodeURIComponent(element.value));
                formBody.append('&');
            }
        }
        if (this._additionalInput) {
            formBody.append(this._additionalInput);
            this._additionalInput = null;
        }

        var request = new Sys.Net.WebRequest();
        var action = form.action;
        if (Sys.Browser.agent === Sys.Browser.InternetExplorer) {
            var queryIndex = action.indexOf('?');
            if (queryIndex !== -1) {
                var path = action.substr(0, queryIndex);
                if (path.indexOf("%") === -1) {
                    action = encodeURI(path) + action.substr(queryIndex);
                }
            }
            else if (action.indexOf("%") === -1) {
                action = encodeURI(action);
            }
        }
        request.set_url(action);
        request.get_headers()['X-MicrosoftAjax'] = 'Delta=true';
        request.get_headers()['Cache-Control'] = 'no-cache';
        request.set_timeout(this._asyncPostBackTimeout);
        request.add_completed(Function.createDelegate(this, this._onFormSubmitCompleted));
        request.set_body(formBody.toString());
        var handler = this._get_eventHandlerList().getHandler("initializeRequest");
        if (handler) {
            var eventArgs = new Sys.WebForms.InitializeRequestEventArgs(request, this._postBackSettings.sourceElement);
            handler(this, eventArgs);
            continueSubmit = !eventArgs.get_cancel();
        }
        if (!continueSubmit) {
            if (evt) {
                evt.preventDefault();
            }
            return;
        }
        this._scrollPosition = this._getScrollPosition();
        this.abortPostBack();
        handler = this._get_eventHandlerList().getHandler("beginRequest");
        if (handler) {
            var eventArgs = new Sys.WebForms.BeginRequestEventArgs(request, this._postBackSettings.sourceElement);
            handler(this, eventArgs);
        }

        if (this._originalDoCallback) {
            this._cancelPendingCallbacks();
        }
        this._request = request;
        request.invoke();
        if (evt) {
            evt.preventDefault();
        }
    }

我们可以发现AJAX回发所提交的数据量其实跟普通回发过程中提交的数据量是一样的,并且还附加了一些额外信息。

 

3.2 服务器端的处理

 

AJAX回发请求到达服务器之后,当前页面的生命周期跟普通回发引起的请求是一样的,页面的Init、Load和Render等等事件都会被触发,差别只是在于AJAX回发使用了不同的呈现画法。

 

AJAX回发引起的请求生命周期:

 

从上图我们可以看到,页面的生命周期与普通回发是一样的,同样页面上的控件也会经历相应的生命周期。

先了解一下ScriptManager控件在服务器端的处理:

 

- OnInit:在Init事件中,ScriptManager控件会注册页面的InitComplete, PreRenderComplete以及PreRender事件,另外还会根据本次请求的HTTP头来设定一个标记以确定本次回发是否为Ajax异步更新所引起的回发。

 

见下面的代码:

protected internal override void OnInit(EventArgs e)
{
    base.OnInit(e);
    if (this.EnableHistory)
    {
        this.RegisterAsyncPostBackControl(this);
    }
    if (!base.DesignMode)
    {
        IPage iPage = this.IPage;
        if (GetCurrent(this.Page) != null)
        {
            throw new InvalidOperationException(AtlasWeb.ScriptManager_OnlyOneScriptManager);
        }
        iPage.Items[typeof(IScriptManager)] = this;
        iPage.Items[typeof(ScriptManager)] = this;
        iPage.InitComplete += new EventHandler(this.OnPageInitComplete);
        iPage.PreRenderComplete += new EventHandler(this.OnPagePreRenderComplete);
        if (iPage.IsPostBack)
        {
            this._isInAsyncPostBack = PageRequestManager.IsAsyncPostBackRequest(iPage.Request.Headers);
            if (this.EnableHistory)
            {
                this._isNavigating = iPage.Request["__EVENTTARGET"] == this.UniqueID;
            }
        }
        this.PageRequestManager.OnInit();
        iPage.PreRender += new EventHandler(this.ScriptControlManager.OnPagePreRender);
    }
}

 

- OnPagePreRenderComplete,在PagePreRenderComplete事件中,ScriptManager控件会注册脚本文件以及Services代理脚本,MicrosoftAjax.js和MicrosoftAjaxWebForm.js就是在这个阶段被注册到客户端的。

 

见下面的代码:

private void OnPagePreRenderComplete(object sender, EventArgs e)
{
    if (!this.IsInAsyncPostBack)
    {
        if (this.SupportsPartialRendering)
        {
            this.IPage.ClientScript.GetPostBackEventReference(new PostBackOptions(this, null, null, false, false, false, false, true, null));
        }
        this.RegisterGlobalizationScriptBlock();
        this.RegisterScripts();
        this.RegisterServices();
        if (this.EnableHistory)
        {
            JavaScriptSerializer serializer = JavaScriptSerializer.CreateInstance();
            string[] strArray = new string[] { "\r\nSys.Application.setServerId(", serializer.Serialize(this.ClientID), ", ", serializer.Serialize(this.UniqueID), ");\r\n", ((this._initialState != null) && (this._initialState.Count != 0)) ? ("  Sys.Application.setServerState('" + this.GetStateString() + "');\r\n") : "\r\n" };
            string script = string.Concat(strArray);
            RegisterStartupScript(this, typeof(ScriptManager), "HistoryStartup", script, true);
        }
    }
    else
    {
        this.RegisterScripts();
        if (this.EnableHistory)
        {
            if ((this._initialState != null) && (this._initialState.Count == 0))
            {
                this._initialState = null;
            }
            if (this._newPointCreated)
            {
                this.RegisterDataItem(this, "'" + this.GetStateString() + "'", true);
            }
        }
    }
}

 

- OnPreRender,在PreRender事件中如果判定本次回发为AJAX回发,则会调用PageRequestManager对象的OnPreRender方法。而PageRequestManager对象则会调用Page对象的SetRenderMethodDelegate方法来代理Page的画法,PageRequestManager对象会真正负责本次AJAX回发最终的HTML代码。

见下面的代码:

 

public class ScriptManager : Control,
{
   protected internal override void OnPreRender(EventArgs e)
   {
       base.OnPreRender(e);
       if (this.IsInAsyncPostBack)
       {
           this.PageRequestManager.OnPreRender();
       }
   }
}

internal sealed class PageRequestManager
{
   internal void OnPreRender()
   {
        this._owner.IPage.SetRenderMethodDelegate(new RenderMethod(this.RenderPageCallback));
   }
}

 

PageRequestManager的RenderPageCallback方法最终处理了AJAX回发所需要的HTML代码,在这个方法中会遍历页面上所有涉及到的UpdatePanel控件,得到其更新后的HTML代码后,与隐藏字段还有一些额外信息一起打包,然后传递给客户端。

 

见下面的代码:

private void RenderPageCallback(HtmlTextWriter writer, Control pageControl)
{
    this.ProcessUpdatePanels();
    IHttpResponse response = this._owner.IPage.Response;
    response.ContentType = "text/plain";
    response.Cache.SetNoServerCaching();
    IHtmlForm form = this._owner.IPage.Form;
    form.SetRenderMethodDelegate(new RenderMethod(this.RenderFormCallback));
    this._updatePanelWriter = writer;
    ParserStringWriter writer2 = new ParserStringWriter();
    ParserHtmlTextWriter writer3 = new ParserHtmlTextWriter(writer2);
    writer2.ParseWrites = true;
    form.RenderControl(writer3);
    writer2.ParseWrites = false;
    foreach (KeyValuePair<string, string> pair in writer2.HiddenFields)
    {
        if (ControlUtil.IsBuiltInHiddenField(pair.Key))
        {
            EncodeString(writer, "hiddenField", pair.Key, pair.Value);
        }
    }
    EncodeString(writer, "asyncPostBackControlIDs", string.Empty, this.GetAsyncPostBackControlIDs(false));
    EncodeString(writer, "postBackControlIDs", string.Empty, this.GetPostBackControlIDs(false));
    EncodeString(writer, "updatePanelIDs", string.Empty, this.GetAllUpdatePanelIDs());
    EncodeString(writer, "childUpdatePanelIDs", string.Empty, this.GetChildUpdatePanelIDs());
    EncodeString(writer, "panelsToRefreshIDs", string.Empty, this.GetRefreshingUpdatePanelIDs());
    EncodeString(writer, "asyncPostBackTimeout", string.Empty, this._owner.AsyncPostBackTimeout.ToString(CultureInfo.InvariantCulture));
    if (writer3.FormAction != null)
    {
        EncodeString(writer, "formAction", string.Empty, writer3.FormAction);
    }
    if (this._owner.IPage.Header != null)
    {
        string title = this._owner.IPage.Title;
        if (!string.IsNullOrEmpty(title))
        {
            EncodeString(writer, "pageTitle", string.Empty, title);
        }
    }
    this.RenderDataItems(writer);
    this.ProcessScriptRegistration(writer);
    this.ProcessFocus(writer);
}

 

3.3 客户端更新

当服务器端相应完毕后,客户端会得到响应信息,然后调用客户端对象PageRequestManager的_onFormSubmitCompleted方法来进行页面局部更新,最终会调用_updatePanel方法来更新UpdatePanel控件。

 

参见_onFormSubmitCompleted方法的代码:

 

function Sys$WebForms$PageRequestManager$_onFormSubmitCompleted(sender, eventArgs) {
        this._processingRequest = true;
        var delimitByLengthDelimiter = '|';
        if (sender.get_timedOut()) {
            this._endPostBack(this._createPageRequestManagerTimeoutError(), sender);
            return;
        }
        if (sender.get_aborted()) {
            this._endPostBack(null, sender);
            return;
        }
        if (!this._request || sender.get_webRequest() !== this._request) {
            return;
        }
        var errorMessage;
        var delta = [];
        if (sender.get_statusCode() !== 200) {
            this._endPostBack(this._createPageRequestManagerServerError(sender.get_statusCode()), sender);
            return;
        }
        var reply = sender.get_responseData();
        var delimiterIndex, len, type, id, content;
        var replyIndex = 0;
        var parserErrorDetails = null;
        while (replyIndex < reply.length) {
            delimiterIndex = reply.indexOf(delimitByLengthDelimiter, replyIndex);
            if (delimiterIndex === -1) {
                parserErrorDetails = this._findText(reply, replyIndex);
                break;
            }
            len = parseInt(reply.substring(replyIndex, delimiterIndex), 10);
            if ((len % 1) !== 0) {
                parserErrorDetails = this._findText(reply, replyIndex);
                break;
            }
            replyIndex = delimiterIndex + 1;
            delimiterIndex = reply.indexOf(delimitByLengthDelimiter, replyIndex);
            if (delimiterIndex === -1) {
                parserErrorDetails = this._findText(reply, replyIndex);
                break;
            }
            type = reply.substring(replyIndex, delimiterIndex);
            replyIndex = delimiterIndex + 1;
            delimiterIndex = reply.indexOf(delimitByLengthDelimiter, replyIndex);
            if (delimiterIndex === -1) {
                parserErrorDetails = this._findText(reply, replyIndex);
                break;
            }
            id = reply.substring(replyIndex, delimiterIndex);
            replyIndex = delimiterIndex + 1;
            if ((replyIndex + len) >= reply.length) {
                parserErrorDetails = this._findText(reply, reply.length);
                break;
            }
            content = reply.substr(replyIndex, len);
            replyIndex += len;
            if (reply.charAt(replyIndex) !== delimitByLengthDelimiter) {
                parserErrorDetails = this._findText(reply, replyIndex);
                break;
            }
            replyIndex++;
            Array.add(delta, {type: type, id: id, content: content});
        }
        if (parserErrorDetails) {
            this._endPostBack(this._createPageRequestManagerParserError(String.format(Sys.WebForms.Res.PRM_ParserErrorDetails, parserErrorDetails)), sender);
            return;
        }
        var updatePanelNodes = [];
        var hiddenFieldNodes = [];
        var arrayDeclarationNodes = [];
        var scriptBlockNodes = [];
        var scriptStartupNodes = [];
        var expandoNodes = [];
        var onSubmitNodes = [];
        var dataItemNodes = [];
        var dataItemJsonNodes = [];
        var scriptDisposeNodes = [];
        var asyncPostBackControlIDsNode, postBackControlIDsNode,
            updatePanelIDsNode, asyncPostBackTimeoutNode,
            childUpdatePanelIDsNode, panelsToRefreshNode, formActionNode;
        for (var i = 0; i < delta.length; i++) {
            var deltaNode = delta[i];
            switch (deltaNode.type) {
                case "updatePanel":
                    Array.add(updatePanelNodes, deltaNode);
                    break;
                case "hiddenField":
                    Array.add(hiddenFieldNodes, deltaNode);
                    break;
                case "arrayDeclaration":
                    Array.add(arrayDeclarationNodes, deltaNode);
                    break;
                case "scriptBlock":
                    Array.add(scriptBlockNodes, deltaNode);
                    break;
                case "scriptStartupBlock":
                    Array.add(scriptStartupNodes, deltaNode);
                    break;
                case "expando":
                    Array.add(expandoNodes, deltaNode);
                    break;
                case "onSubmit":
                    Array.add(onSubmitNodes, deltaNode);
                    break;
                case "asyncPostBackControlIDs":
                    asyncPostBackControlIDsNode = deltaNode;
                    break;
                case "postBackControlIDs":
                    postBackControlIDsNode = deltaNode;
                    break;
                case "updatePanelIDs":
                    updatePanelIDsNode = deltaNode;
                    break;
                case "asyncPostBackTimeout":
                    asyncPostBackTimeoutNode = deltaNode;
                    break;
                case "childUpdatePanelIDs":
                    childUpdatePanelIDsNode = deltaNode;
                    break;
                case "panelsToRefreshIDs":
                    panelsToRefreshNode = deltaNode;
                    break;
                case "formAction":
                    formActionNode = deltaNode;
                    break;
                case "dataItem":
                    Array.add(dataItemNodes, deltaNode);
                    break;
                case "dataItemJson":
                    Array.add(dataItemJsonNodes, deltaNode);
                    break;
                case "scriptDispose":
                    Array.add(scriptDisposeNodes, deltaNode);
                    break;
                case "pageRedirect":
                    if (Sys.Browser.agent === Sys.Browser.InternetExplorer) {
                        var anchor = document.createElement("a");
                        anchor.style.display = 'none';
                        anchor.attachEvent("onclick", cancelBubble);
                        anchor.href = deltaNode.content;
                        document.body.appendChild(anchor);
                        anchor.click();
                        anchor.detachEvent("onclick", cancelBubble);
                        document.body.removeChild(anchor);

                        function cancelBubble(e) {
                            e.cancelBubble = true;
                        }
                    }
                    else {
                        window.location.href = deltaNode.content;
                    }
                    return;
                case "error":
                    this._endPostBack(this._createPageRequestManagerServerError(Number.parseInvariant(deltaNode.id), deltaNode.content), sender);
                    return;
                case "pageTitle":
                    document.title = deltaNode.content;
                    break;
                case "focus":
                    this._controlIDToFocus = deltaNode.content;
                    break;
                default:
                    this._endPostBack(this._createPageRequestManagerParserError(String.format(Sys.WebForms.Res.PRM_UnknownToken, deltaNode.type)), sender);
                    return;
            }
        }
        var i;
        if (asyncPostBackControlIDsNode && postBackControlIDsNode &&
            updatePanelIDsNode && panelsToRefreshNode &&
            asyncPostBackTimeoutNode && childUpdatePanelIDsNode) {
            this._oldUpdatePanelIDs = this._updatePanelIDs;
            var childUpdatePanelIDsString = childUpdatePanelIDsNode.content;
            this._childUpdatePanelIDs = childUpdatePanelIDsString.length ? childUpdatePanelIDsString.split(',') : [];
            var asyncPostBackControlIDsArray = this._splitNodeIntoArray(asyncPostBackControlIDsNode);
            var postBackControlIDsArray = this._splitNodeIntoArray(postBackControlIDsNode);
            var updatePanelIDsArray = this._splitNodeIntoArray(updatePanelIDsNode);
            this._panelsToRefreshIDs = this._splitNodeIntoArray(panelsToRefreshNode);
            for (i = 0; i < this._panelsToRefreshIDs.length; i++) {
                var panelClientID = this._uniqueIDToClientID(this._panelsToRefreshIDs[i]);
                if (!document.getElementById(panelClientID)) {
                    this._endPostBack(Error.invalidOperation(String.format(Sys.WebForms.Res.PRM_MissingPanel, panelClientID)), sender);
                    return;
                }
            }
            var asyncPostBackTimeout = asyncPostBackTimeoutNode.content;
            this._updateControls(updatePanelIDsArray, asyncPostBackControlIDsArray, postBackControlIDsArray, asyncPostBackTimeout);
        }
        this._dataItems = {};
        for (i = 0; i < dataItemNodes.length; i++) {
            var dataItemNode = dataItemNodes[i];
            this._dataItems[dataItemNode.id] = dataItemNode.content;
        }
        for (i = 0; i < dataItemJsonNodes.length; i++) {
            var dataItemJsonNode = dataItemJsonNodes[i];
            this._dataItems[dataItemJsonNode.id] = Sys.Serialization.JavaScriptSerializer.deserialize(dataItemJsonNode.content);
        }
        var handler = this._get_eventHandlerList().getHandler("pageLoading");
        if (handler) {
            handler(this, this._getPageLoadingEventArgs());
        }
        if (formActionNode) {
            this._form.action = formActionNode.content;
        }

        Sys._ScriptLoader.readLoadedScripts();
        Sys.Application.beginCreateComponents();
        var scriptLoader = Sys._ScriptLoader.getInstance();
        this._queueScripts(scriptLoader, scriptBlockNodes, true, false);

        this._updateContext = {
            response: sender,
            updatePanelNodes: updatePanelNodes,
            scriptBlockNodes: scriptBlockNodes,
            scriptDisposeNodes: scriptDisposeNodes,
            hiddenFieldNodes: hiddenFieldNodes,
            arrayDeclarationNodes: arrayDeclarationNodes,
            expandoNodes: expandoNodes,
            scriptStartupNodes: scriptStartupNodes,
            onSubmitNodes: onSubmitNodes
        };
        scriptLoader.loadScripts(0,
            Function.createDelegate(this, this._scriptIncludesLoadComplete),
            Function.createDelegate(this, this._scriptIncludesLoadFailed),
            null);
}

 

4.结语

 

使用UpdatePanel是给已经存在的ASP.NET应用程序添加AJAX体验的最快捷方式,对于应用程序的架构也不会有影响,我们可以使用它来逐步的提高应用程序的用户体验。但是其性能与纯粹的AJAX方式相比较,还是比较差的。

时间: 2024-10-21 20:42:44

ASP.NET AJAX UpdatePanel 控件实现剖析的相关文章

ASP.NET AJAX UpdatePanel控件简介

先决条件 若要在您自己的开发环境中实现这些过程,您需要: Microsoft Visual Studio 2005 或 Microsoft Visual Web Developer 速成 版. 一个支持 AJAX 的 ASP.NET 网站. 使用 UpdatePanel 控件 创建新页并切换到"设计"视图. 在工具箱的"AJAX Extensions"选项卡中,双击 ScriptManager 控件以将 其添加到页面中. 双击 UpdatePanel 控件以将其添加

ASP.NET AJAX UpdateProgress控件介绍

先决条件 若要在您自己的开发环境中实现这些过程,您需要: Microsoft Visual Studio 2005 或 Visual Web Developer 速成版. 一个支持 AJAX 的 ASP.NET 网站. 使用单个 UpdateProgress 控件 首先使用单个 UpdateProgress 控件显示页面上的所有部分页更新的进度. 对整个页使用单个 UpdateProgress 控件 创建新页并切换到"设计"视图. 在工具箱的"AJAX Extensions&

ASP.NET AJAX UpdateProgress控件概述

方案 当网页包含一个或多个用于部分页呈现的 UpdatePanel 控件时,UpdateProgress 控件可帮助您设计更为直观的 UI.如果部分页更新速度较慢,则可以使用 UpdateProgress 控件来提供有关更新状态的可视反馈.可以在页上放置多个 UpdateProgress 控件,其中每个控件都与不同的 UpdatePanel 控件相关联.也可以使用一个 UpdateProgress 控件,并将其与页上的所有 UpdatePanel 控件关联. 背景 UpdateProgress

ASP.NET AJAX Timer控件概述

Timer 控件方案 当您要执行以下操作时,请使用 Timer 控件: 定期更新一个或多个 UpdatePanel 控件的内容,而无需刷新整个网页. 每当 Timer 控件导致回发时运行服务器上的代码. 按定义的时间间隔将整个网页同步发布到 Web 服务器上. 背景 Timer 控件是一个服务器控件,它会将一个 JavaScript 组件嵌入到网页中 .当经过 Interval 属性中定义的时间间隔时,该 JavaScript 组件将从浏览器 启动回发.您可以在运行于服务器上的代码中设置 Tim

自定义ASP.NET Ajax Extender控件

问题描述 前言:熟悉ASP.NETAjax的人都对AjaxControlToolkit里的Extender控件留有非常深的印象.有了Extender控件就可以在原有页面上指定某个服务器控件实施Ajax行为(Behavior).如:常用的CalendarExtender控件.下面内容介绍怎样自定义一个AjaxExtender控件实现Panel服务器控件的高亮边框效果.如图所示.正文:创建一个Extender控件需要完成两个部分的工作.第一,创建客户端的行为(Behavior)控件:第二,创建服务器

一起谈.NET技术,asp.net Ajax ---AutoComplete控件使用

 简介:     AutoComplete控件就是在用户在文本框输入前几个字母或是汉字的时候,该控件就能从存放数据的文或是数据库里将所有以这些字母开头的数据提示给用户,供用户选择,提供方便.  重要属性:     1.TargetControlID:指定要实现提示功能的控件.     2.ServicePath:WebService的路径,提取数据的方法是写在一个WebService中的.     3.ServeiceMethod:写在WebService中的用于提取数据的方法的名字.    

asp.net Ajax ---AutoComplete控件使用

简介:    AutoComplete控件就是在用户在文本框输入前几个字母或是汉字的时候,该控件就能从存放数据的文或是数据库里将所有以这些字母开头的数据提示给用户,供用户选择,提供方便. 重要属性:    1.TargetControlID:指定要实现提示功能的控件.    2.ServicePath:WebService的路径,提取数据的方法是写在一个WebService中的.    3.ServeiceMethod:写在WebService中的用于提取数据的方法的名字.    4.Minim

asp.net ajax学习系列功能强大的UpdatePanel控件

ajax|asp.net|控件 先给一个简单的例子,后面给一个比较复杂的例子. 改进后的UpdatePanel使页面部分更新(Partial-Page Updates)实现起来非常容易.要想在已有web页面或新建页面中加入部分更新内容,都十分容易,下面几个步骤:<1>在页面中加入ScriptManager控件.并保证ScriptManager控件的EnablePartialRendering属性值为true.若EnablePartialRendering=false,那么下面所做的对页面部分更

ASP.NET AJAX入门系列(5):使用UpdatePanel控件(二)

UpdatePanel可以用来创建丰富的局部更新Web应用程序,它是ASP.NET 2.0 AJAX Extensions中很重要的一个控件,其强大之处在于不用编写任何客户端脚本,只要在一个页面上添加几个UpdatePanel控件和一个ScriptManager控件就可以自动实现局部更新.通过本文来学习一下UpdatePanel其他的一些使用方法(第二篇). 一.用编程的方法控制UpdatePanel的更新 对于UpdatePanel,我们也可以使用编程的方法来控制它的更新,可以通过Script