Community Server系列之五:CS2中的Ajax原理

上一节用了一个示例说明了AjaxCS2中的一点简单的应用,这一节里着重探讨一下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脚本呈现到浏览器上。

AjaxPager
    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(
05, 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来向浏览器呈现指定区域的数据。

你可能感兴趣的:(Community Server系列之五:CS2中的Ajax原理)