CS2中的Ajax原理二

添加人:iyond五级(2333分)   添加时间:2007-05-22    阅读次数:1542  收藏此教程

上一节用了一个示例说明了 Ajax CS2 中的一点简单的应用,这一节里着重探讨一下 CS2 中的 Ajax 的高级应用和实现原理,在了解 Ajax 的实现原理前我觉得有必要先了解一下 aspx 页面从请求到返回 HTML 都做了些什么,我想在了解了页面处理机制再来认识 Ajax 处理原理应该应该会很有帮助的,见下表:

序号

阶段

页面事件

可覆盖的方法

1

页面初始化

Init

 

2

加载视图状态

 

LoadViewState

3

处理回发数据

 

任意实现 IPostBackDataHandler 接口的控件中的 LoadPostData 方法

4

加载页面

Load

 

5

回发更改通知

 

任意实现 IPostBackDataHandler 接口的控件中的 RaisePostDataChangedEvent 方法

6

处理回发事件

由控件定义的任意回发事件

任意实现 IPostBackDataHandler 接口的控件中的 RaisePostBackEvent 方法

7

页面显示前阶段

PreRender

 

8

保存视图状态

 

SaveViewState

9

显示页面

 

Render

10

卸载页面

Unload

 

此表从上到下为处理 ASPX 页面所经历的过程,现简单解说一下各过程处理的工作:

1.              在页面实例化所有控件之后触发,页面的控件也仅仅是实例化好了,各静态变量也有了初始值,在这个事件里,一般我们可以设置一些事件处理程序等等。

2.              如果是回发那么加载视图状态。

3.              处理回发数据,为控件赋值,这里通过读取回发过来的数据初始化各控件的属性。这样,第一步生成的控件就会有值了。

4.              激发Load事件

5.              处理比如OnChange这样的事件,也就是控件属性和ViewState中的值不同的话,将会激发事件处理程序。

6.              处理页面里的按钮等回发事件。

7.              页面即将展现给用户,也就是即将生成HTML代码前激发的事件,注意这个地方是我们的Ajax关键事件,在这里页面前的初始化和相关事件都处理完了,服务器控件已经具有该有的值了,而Ajax应用只会返回给客户段一小部分HTML或字符串,作为客户端调用AjaxPostBack的返回值,显然此处不下手还等待何时呢,在这里,筛选出我们想要返回给客户端的对象并且只呈现这些对象即可,而执行完这些对象的Render方法之后即可停止Response数据了。

8.              保存视图状态,在aspx中视图状态保存在客户端HTML的隐藏字段__VIEWSTATE中。

9.              依次执行各个控件的Render方法,输出需要呈现的HTML标签,Render方法是asp.net中很关键的方法,每个服务器控件都有相应的Render方法,它的目的就是把自己生成HTML格式的字符串,此字符串最终返回给客户端的浏览器。

10.           最后一步触发卸载页面事件,我们可以在此处理资源回收等操作。

了解了此原理后,我想要理解 Ajax 的运行原理应该不是大问题了,让我们再了解一下 Ajax 都有哪些操作,我想不外乎两种方式,一种是不返回值或返回简单字符串形式,客户端通过这点标识操作一些功能,另外一种方式是返回 HTML 格式内容,比如在网页里的某个区块的动态加载内容,这两种方式大同小意,我们的 Ajax 在后台都是相似的处理,我想要说明处理过程还是结合示例来说比较通俗,现在就拿在 CS2 中经常用到的 AjaxPager 这个控件来说,这个控件在 CS2 中算是比较高级一点的应用了,与我们的第三方 Ajax 控件比较类似,使用方法既是在 AjaxPager 控件里嵌入相应的服务器控件 ( 比如 Repeater 控件 ) 以后就可以使里面的内容无刷新分页显示数据了,表面上看来是很高深很酷的功能,其实处理过程并不是想象中的那般复杂。

< CS:AjaxPager runat="Server" id="PostsPager" ShowFirstLastLinks="false">
    <asp:Repeater id="Posts" runat="Server" >
           <ItemTemplate>
                  <div class="CommonSidebarContentItem">
                         <asp:HyperLink Runat="server" id="TitleLink" />
                  </div>
           </ItemTemplate>
    </asp:Repeater>
</CS:AjaxPager>

大致分析一下此控件的工作流程:此控件为分页控件,在页面加载的时候赋上分页属性,计算页数,并生成相应的Ajax分页连接,当用户点击分页连接的时候,页面在后台进行ajax提交,通过服务器处理后返回HTML形式的提交结果,并通过js脚本呈现到浏览器上。

public class AjaxPager : Label, IPagedControl
{
    //Member variables and constructor
    #region Member variables and constructor
    protected CSContext csContext = CSContext.Current;
    HyperLink previousLink;
    HyperLink nextLink;
    HyperLink firstLink;
    HyperLink lastLink;
    HyperLink[] pagingHyperLinks;

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);

        // Add Page buttons
        //
        AddPageLinks();

        // Add Previous Next child controls
        //
        AddPreviousNextLinks();

        // Add First Last child controls
        //
        AddFirstLastLinks();

    }
    #endregion

    //Render functions
    #region Render functions
    protected override void Render(HtmlTextWriter writer)
    {
        Render(writer, true);
    }

    protected virtual void Render(HtmlTextWriter writer, bool renderContainer)
    {
        if (renderContainer)
            writer.Write("<div id=\"" + this.ClientID + "_Content\">");

        this.RenderChildren(writer);

        if (Visible)
        {
            int totalPages = CalculateTotalPages();

            // Do we have data?
            //
            if (totalPages <= 1)
                return;

            AddAttributesToRender(writer);


            // Render the paging buttons
            //
            writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass, false);
            //writer.RenderBeginTag(HtmlTextWriterTag.Span);

            // Render the first button
            //
            if (this.ShowFirstLastLinks)
                RenderFirst(writer);

            // Render the previous button
            //
            RenderPrevious(writer);

            // Render the page button(s)
            //
            RenderPagingButtons(writer);

            // Render the next button
            //
            RenderNext(writer);

            // Render the last button
            //
            if (this.ShowFirstLastLinks)
                RenderLast(writer);

            //writer.RenderEndTag();
        }

        if (renderContainer)
            writer.Write("</div>");
    }

    void RenderFirst(HtmlTextWriter writer)
    {

        int totalPages = CalculateTotalPages();

        if ((PageIndex >= 3) && (totalPages > 5))
        {
            firstLink.RenderControl(writer);

            LiteralControl l = new LiteralControl("&nbsp;&nbsp;");
            l.RenderControl(writer);
        }
    }

    void RenderLast(HtmlTextWriter writer)
    {
        int totalPages = CalculateTotalPages();

        if (((PageIndex + 3) < totalPages) && (totalPages > 5))
        {
            LiteralControl l = new LiteralControl("&nbsp;&nbsp;");
            l.RenderControl(writer);

            lastLink.RenderControl(writer);
        }

    }

    void RenderPrevious(HtmlTextWriter writer)
    {
        Literal l;

        if (HasPrevious)
        {
            previousLink.RenderControl(writer);

            l = new Literal();
            l.Text = "&nbsp;";
            l.RenderControl(writer);
        }

    }

    void RenderNext(HtmlTextWriter writer)
    {
        Literal l;

        if (HasNext)
        {

            l = new Literal();
            l.Text = "&nbsp;";
            l.RenderControl(writer);

            nextLink.RenderControl(writer);
        }

    }

    void RenderButtonRange(int start, int end, HtmlTextWriter writer)
    {

        for (int i = start; i < end; i++)
        {

            // Are we working with the selected index?
            //
            if (PageIndex == i)
            {

                // Render different output
                //
                Literal l = new Literal();
                //l.Text = "<span class=\"currentPage\">[" + (i + 1).ToString() + "]</span>";
                l.Text = (i + 1).ToString();

                l.RenderControl(writer);
            }
            else
            {
                pagingHyperLinks[i].RenderControl(writer);
            }
            if (i < (end - 1))
                writer.Write("");

        }

    }

    void RenderPagingButtons(HtmlTextWriter writer)
    {
        int totalPages;

        // Get the total pages available
        //
        totalPages = CalculateTotalPages();

        // If we have <= 5 pages display all the pages and exit
        //
        if (totalPages <= 5)
        {
            RenderButtonRange(0, totalPages, writer);
        }
        else
        {

            int lowerBound = (PageIndex - 2);
            int upperBound = (PageIndex + 3);

            if (lowerBound <= 0)
                lowerBound = 0;

            if (PageIndex == 0)
                RenderButtonRange(0, 5, writer);

            else if (PageIndex == 1)
                RenderButtonRange(0, (PageIndex + 4), writer);

            else if (PageIndex < (totalPages - 2))
                RenderButtonRange(lowerBound, (PageIndex + 3), writer);

            else if (PageIndex == (totalPages - 2))
                RenderButtonRange((totalPages - 5), (PageIndex + 2), writer);

            else if (PageIndex == (totalPages - 1))
                RenderButtonRange((totalPages - 5), (PageIndex + 1), writer);
        }
    }
    #endregion

    //ControlTree functions
    #region ControlTree functions
    void AddPageLinks()
    {
        // First add the lower page buttons
        //
        pagingHyperLinks = new HyperLink[CalculateTotalPages()];

        // Create the buttons and add them to
        // the Controls collection
        //
        for (int i = 0; i < pagingHyperLinks.Length; i++)
        {
            pagingHyperLinks[i] = new HyperLink();
            pagingHyperLinks[i].EnableViewState = false;
            pagingHyperLinks[i].Text = (i + 1).ToString();
            pagingHyperLinks[i].ID = (i + 1).ToString();
            pagingHyperLinks[i].NavigateUrl = "#";
            pagingHyperLinks[i].Attributes.Add("onclick", CreatePagerJavaScript((i + 1).ToString()));
        }
    }

    void AddFirstLastLinks()
    {
        int start = 1;
        firstLink = new HyperLink();
        firstLink.ID = "First";
        firstLink.Text = ResourceManager.GetString("Utility_Pager_firstButton");
        firstLink.NavigateUrl = "#";
        firstLink.Attributes.Add("onclick", CreatePagerJavaScript(start.ToString()));

        int last = CalculateTotalPages();
        lastLink = new HyperLink();
        lastLink.ID = "Last";
        lastLink.Text = ResourceManager.GetString("Utility_Pager_lastButton");
        lastLink.NavigateUrl = "#";
        lastLink.Attributes.Add("onclick", CreatePagerJavaScript(last.ToString()));
    }

    void AddPreviousNextLinks()
    {
        int previous;

        if (this.PageIndex < 2)
            previous = 1;
        else
            previous = this.PageIndex;

        previousLink = new HyperLink();
        previousLink.ID = "Prev";
        previousLink.Text = ResourceManager.GetString("Utility_Pager_previousButton");
        previousLink.NavigateUrl = "#";
        previousLink.Attributes.Add("onclick", CreatePagerJavaScript(previous.ToString()));

        int next = this.PageIndex + 2;
        nextLink = new HyperLink();
        nextLink.ID = "Next";
        nextLink.Text = ResourceManager.GetString("Utility_Pager_nextButton");
        nextLink.NavigateUrl = "#";
        nextLink.Attributes.Add("onclick", CreatePagerJavaScript(next.ToString()));
    }
    #endregion

    //Private Properties
    #region Private Properties
    private bool HasPrevious
    {
        get
        {
            if (PageIndex > 0)
                return true;

            return false;
        }
    }

    private bool HasNext
    {
        get
        {
            if (PageIndex + 1 < CalculateTotalPages())
                return true;

            return false;
        }
    }
    #endregion

    //Helper methods and Public Properties
    #region Helper methods and Public Properties

    string url = "AjaxPager.GetPage('{0}', {1}, new Function('result', 'if (result.error) {{ alert(result.error); }} else {{ document.getElementById(\\'{0}_Content\\').innerHTML = result.value; }}')); return false;";
    protected virtual string CreatePagerJavaScript(string pageIndex)
    {
        return string.Format(url, this.ClientID, pageIndex);
    }

    // *********************************************************************
    // CalculateTotalPages
    //
    /**/
    /// <summary>
    /// Static that caculates the total pages available.
    /// </summary>
    ///
    // ********************************************************************/
    public virtual int CalculateTotalPages()
    {
        int totalPagesAvailable;

        if (TotalRecords == 0)
            return 0;

        // First calculate the division
        //
        totalPagesAvailable = TotalRecords / PageSize;

        // Now do a mod for any remainder
        //
        if ((TotalRecords % PageSize) > 0)
            totalPagesAvailable++;

        return totalPagesAvailable;
    }

    int _pageIndex = 0;
    public virtual int PageIndex
    {
        get
        {
            // Give first try to the ViewState if it was a postback
            if (Page.IsPostBack && ViewState["PageIndex"] != null)
            {
                _pageIndex = (int)ViewState["PageIndex"];
            }
            else
            {
                if (csContext.QueryString["pageindex"] != null)
                    _pageIndex = int.Parse(csContext.QueryString["pageindex"]) - 1;
            }

            if (_pageIndex < 0)
                return 0;
            else
                return _pageIndex;
        }
        set
        {
            ViewState["PageIndex"] = value;
            _pageIndex = value;
        }
    }

    public virtual int PageSize
    {
        get
        {
            int pageSize = Convert.ToInt32(ViewState["PageSize"]);

            if (pageSize == 0)
                return 10;

            return pageSize;
        }
        set
        {
            ViewState["PageSize"] = value;
        }

    }

    public virtual bool ShowFirstLastLinks
    {
        get
        {
            object state = ViewState["ShowFirstLastLinks"];
            if (state == null)
                return true;
            else
                return (bool)state;
        }
        set
        {
            ViewState["ShowFirstLastLinks"] = value;
        }
    }

    private bool _causeValidation = true;
    public bool CausesValidation
    {
        get { return _causeValidation; }
        set { _causeValidation = value; }
    }

    public int TotalRecords
    {
        get
        {
            return Convert.ToInt32(ViewState["TotalRecords"]);
        }
        set
        {
            ViewState["TotalRecords"] = value;
        }
    }
    /**/
    /// <summary>
    /// Making sure to strip the existing pager value from the querystring before appending a new one
    /// </summary>
    /// <param name="RawURL">Usually CSContext.Current.RawUrl</param>
    /// <param name="QueryName">Usually "pageindex"</param>
    /// <returns></returns>
    public string UrlCleaner(string RawURL, string QueryName)
    {
        // Configure the Url
        if (RawURL.IndexOf("?") != -1)
        {
            bool hasPart = false;
            string queryString = RawURL.Substring(RawURL.IndexOf("?") + 1);
            string[] parts = queryString.Split('&');
            for (int i = 0; i < parts.Length; i++)
            {
                if (parts[i].StartsWith(QueryName))
                {
                    parts[i] = QueryName + "={0}";
                    hasPart = true;
                }
            }
            if (hasPart == true)
                RawURL = RawURL.Replace(queryString, string.Join("&", parts));
            else
                RawURL = RawURL.Replace(queryString, string.Join("&", parts) + "&" + QueryName + "={0}");
        }
        else
            RawURL = RawURL + "?" + QueryName + "={0}";

        return RawURL;

    }

    #endregion

    public override bool Visible
    {
        get
        {
            object state = this.ViewState["Visible"];
            if (state == null)
                return true;
            else
                return (bool)state;
        }
        set
        {
            this.ViewState["Visible"] = value;
        }
    }

    //AJAX
    #region AJAX

    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);
        AjaxManager.Register(this, "AjaxPager");
    }

    [AjaxMethod(IncludeControlValuesWithCallBack = true)]
    public virtual string GetPage(int page)
    {
        StringWriter stringWriter = new StringWriter();
        HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);

        AddPageLinks();
        AddPreviousNextLinks();

        if (this.ShowFirstLastLinks)
            AddFirstLastLinks();

        this.Render(htmlWriter, false);

        return stringWriter.ToString();
    }

    #endregion
}

关键一点是让程序注册

PreRender 处理事件,我们知道,在此事件控件还没有生成 HTML 代码,也就是 Response 对象里还没有具体的 HTML 代码,所有的控件还没有执行 Reader 方法,这里我们就可以选择想执行 Reader 方法的控件而忽略其他控件,在此我们只需要执行包含在 AjaxPager 控件内的控件(也就是 AjaxPager 的子控件)的 Reader 方法, 这样就可以保证 Response 给客户端的是纯净的,只在 AjaxPager 控件内部的控件的 HTML 实现。

让我看看此控件重写的 OnInit 方法,此方法里的 AjaxManager.Register(this, "AjaxPager"); 这一句在前面一章已经介绍过,为注册Ajax的回发脚本,当然它还有另外一个作用那就是注册PreRender事件,当触发PreReader事件 到时候,AjaxManager会去找控件里具有AjaxMethod属性的方法来执行,让我们看看在AjaxPager里的AjaxMethod都干了些什么:

[AjaxMethod(IncludeControlValuesWithCallBack = true)]
public virtual string GetPage(int page)
{
    StringWriter stringWriter = new StringWriter();
    HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);

    AddPageLinks();
    AddPreviousNextLinks();

    if (this.ShowFirstLastLinks)
        AddFirstLastLinks();

    this.Render(htmlWriter, false);

    return stringWriter.ToString();
}

可以看出,倒数第二句就是Reader方法,输出的HTML保存到HtmlTextWriter中,最后一句即是把控件的HTML呈现字符串对象返回给调用者(这里指的是AjaxManager),AjaxManager获得值后按照指定的格式Response到客户端,并中止其他的Response,客户端通过调用document.getElementById(‘’).innerHTML = result.value来向浏览器呈现指定区域的数据。

1页 第1上一页1下一页
相关的教程: Felix的Community Server系列
收藏此教程 到论坛求助

当前平均分: 0.0(0 次打分)

012345678910
评论主题
您的大名
您的评论
验证码 点击换一个验证码
知识库搜索: