1. Trang chủ
  2. » Công Nghệ Thông Tin

ASP.NET AJAX Programmer’s Reference - Chapter 23 pot

74 149 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Asynchronous Partial Page Rendering: Server Side Processing
Trường học Unknown University
Chuyên ngành ASP.NET Web Development
Thể loại Outline
Năm xuất bản 2007
Thành phố Unknown City
Định dạng
Số trang 74
Dung lượng 411,51 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

if this._changedPostDataConsumers == null this._changedPostDataConsumers = new ArrayList; Next, it iterates through the name/value pairs in the NameValueCollection passed into it as it

Trang 1

Asynchronous Par tial Page Rendering: Ser ver Side

Processing

The previous chapter followed the current client-side PageRequestManager instance as it made

an asynchronous page postback or partial-page-rendering request to the server This chapter will move on to the server side to follow the asynchronous page postback request from the time it arrives in ASP.NET to the time the final response text is sent back to the server

Chapter 21 followed the Page object as it went through its life cycle phases to process the first request made to a Web page enabled for partial page rendering Since the first request wasn’t a postback, the Page object skipped the postback-related life cycle phases when it was processing the first request This chapter, on the other hand, follows the current Page object as it goes through its life cycle phases to process an asynchronous page postback request to the same page that the first request downloaded Since an asynchronous page postback is a postback request, the current

Page will go through both postback and non-postback life cycle phases, shown in Listing 21-1 and Figure 21-2

Since the non-postback life cycle phases were discussed thoroughly in Chapter 21 , I’ll discuss only the postback-related life cycle phases in this chapter

RetrievePostData

This is the life cycle phase in which the Page object populates an internal collection of type

NameValueCollection named _requestValueCollection with the posted data, as shown in Listing 23-1 As such, this phase makes sense for postback requests — whether synchronous or asynchronous

Trang 2

Listing 23-1: The RetrievePostData Method of the Page Object

private void RetrievePostData()

Depending on the HTTP verb used to make a request, the clients of a page will use one of the following

two approaches to submit data to the server

If the HTTP POST verb is used to make a request, the clients of the page include the data in the body of

the request The data consists of a list of data items separated by the & character, each of which consists

of two parts separated by the equals sign ( = ) The first part of each data item helps the server determine

what type of information the item contains The second part of each data item contains the actual data or

information being submitted As an example, consider the ASP.NET page shown in Listing 23-2 As you

can see, this page contains a TextBox and a DropDownList server control

Every ASP.NET server control inherits a property named UniqueID from the Control base class The

value of this property is automatically set by ASP.NET when the page containing the server control is

accessed This value is a string that contains one or more substrings separated by the dollar sign, of

which the first substring contains the value of the ID property of the control and the subsequent

sub-strings contain the values of the UniqueID properties of those parent controls of the control that

imple-ment the INamingContainer interface In the case of Listing 23-2 , none of the parent controls of the

TextBox and DropDownList server controls (other than the Page itself whose UniqueID property

returns an empty string) implements this interface, which means that ASP.NET sets the values of the

UniqueID properties of these two server controls to the respective values of their ID properties

You may be wondering what the significance of the UniqueID property of a server control is As the

name suggests, this property uniquely identifies the server control among other server controls on the

<form id=”form1” runat=”server”>

<asp:TextBox runat=”server” ID=”TextBox1” />

<asp:DropDownList runat=”server” ID=”DropDownList1”>

<asp:ListItem Text=”Text1” Value=”Value1” />

<asp:ListItem Text=”Text2” Value=”Value2” />

<asp:ListItem Text=”Text3” Value=”Value3” />

</asp:DropDownList>

Trang 3

<asp:Button runat=”server” Text=”Submit” />

<input name=”TextBox1” type=”text” id=”TextBox1” />

<select name=”DropDownList1” id=”DropDownList1”>

<option value=”Value1”>Text1</option>

<option value=”Value2”>Text2</option>

<option value=”Value3”>Text3</option>

</select>

Listing 23-3: The HTML Markup Text Sent to the Client

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”

<input name=”TextBox1” type=”text” id=”TextBox1” />

<select name=”DropDownList1” id=”DropDownList1”>

❑ The value of the name HTML attribute of the select HTML element — that is, the string

“DropDownList1” Recall that this value is that of the UniqueID property of the DropDownList server control

Trang 4

❑ The string that the end user has entered into the text field — that is, the string MyText

❑ The value of the value HTML attribute of the selected option of the select HTML element —

that is, the string “Value2”

Therefore, the data that the browser needs to send to the server consists of two data items The first

con-tains the string that the end user has entered into the text field, and the second concon-tains the value of the

value HTML attribute of the selected option of the select HTML element:

TextBox1=MyText&DropDownList1=Value2

As you can see, each data item consists of two parts The first part is the value of the UniqueID property

of the server control and the second part is the value associated with the server control The UniqueID

property values allow ASP.NET to determine which data item is associated with which server control

So far, I’ve covered the case in which the client uses the HTTP POST verb to make its request to the

server The second scenario is when the client uses the HTTP GET verb This scenario often involves

e-commerce Web applications For example, consider a page that displays the list of product names to

the end users When a user selects a product to see more details about it, the primary key values of the

product and its distributor are passed as a query string to the server:

http://www.mysite.com/Product.aspx?ProductID=2&DistributorID=3

As you can see, the query string consists of a list of data items separated by the ampersand character,

each of which consists of two parts separated by the equals sign

ASP.NET represents each request with an instance of a class named HttpReques t, which exposes two

collection properties of type NameValueCollection named Form and QueryString ASP.NET

auto-matically populates the Form collection with the posted data if the request was made using the HTTP

POST verb; otherwise it populates the QueryString collection with the posted data

Keep in mind that we’re following the current Page through its life cycle phases to process the

asynchro-nous page postback method that the current client-side PageRequestManager instance has made to the

server As we just discussed, the clients of a page have two options when it comes to submitting data to

the server Which option did the current client-side PageRequestManager instance use to submit its

data to the server? The answer lies in Listing 22-22 Recall that this code listing presents the internal

implementation of the _onFormSubmit method of the current client-side PageRequestManager

instance As we discussed in the previous chapter, this method is automatically invoked when a page

postback occurs, allowing the current client-side PageRequestManager instance to intercept

the page postback before the page is actually posted back to the server The current client-side

PageRequestManager instance then determines whether the page must be posted back asynchronously

If so, it takes over the form submission, bypassing the browser’s default synchronous form submission

As Listing 22-22 shows, the current client-side PageRequestManager instance iterates through all the

input form elements on the current page, generates for each input form element one string that consists

of two substrings separated by the equals sign (the first substring containing the value of the name

HTML attribute of the form element and the second containing the value of the form element), and

finally packs all these strings into a single string using the & character as the separator Recall from

Listing 22-22 that the current client-side PageRequestManager instance adds this string to the body of

Trang 5

the request being made to the server As you can see, the current client-side PageRequestManager instance submits its data to the server via the body of an HTTP POST request

Now back to the implementation of the RetrievePostData method of the Page object, as shown in Listing 23-1 Recall that the Page object calls this method when it enters the Retrieve Post Data life cycle phase As this code listing shows, this method simply stores the content of the Form or QueryString collection of the HttpRequest object that represents the current request in an internal collection named

_requestValueCollection This collection contains one name/value pair for each posted data item Recall that each posted data item consists of two parts separated by the equals sign The name part of each name/value pair in this collection contains the first part of the data item and the value part contains the second part of the data item This means that the first part of the data item can be used as an index into the collection to access the second part of the data item For example, in the case of the examples dis-cussed earlier, the following items are true:

❑ The UniqueID property value of the TextBox server control can be used as an index into the

_requestValueCollection to access the text that the end user has entered into the text field:

string text1 = this._requestValueCollection[“TextBox1”];

❑ The UniqueID property value of the DropDownList server control can be used as an index into the _requestValueCollection to access the value of the value HTML attribute of the selected option of the select HTML element associated with the server control:

string text1 = this._requestValueCollection[“DropDownList1”];

❑ The string “ProductID” can be used as an index into the _requestValueCollection to access the primary key value of the product:

string text1 = this._requestValueCollection[“ProductID”];

❑ The string “DistributorID” can be used as an index into the _requestValueCollection to access the primary key value of the distributor:

string text1 = this._requestValueCollection[“DistributorID”];

Since the current Page is processing the asynchronous request shown in Listing 22-22 , the

_requestValueCollection of the current Page contains all the name/value pairs that Listing 22-22 stuffed into the body of the request

LoadScrollPosition

This is the life cycle phase in which the Page object retrieves the scroll x and y positions from the

_requestValueCollection and assigns them to the _scrollPositionX and _scrollPositionY fields, respectively, as shown in Listing 23-4 As you’ll see later, the Page object uses these two fields to set the scroll position in the response text before submitting the response back to the client This life cycle phase takes effect only if the MaintainScrollPositionOnPostBack property of the Page object has been set to true As the name suggests, this property instructs the Page to maintain the scroll position

on page postbacks — be they synchronous or asynchronous

Trang 6

Listing 23-4: The LoadScrollPosition Method of the Page Object

Private void LoadScrollPosition()

{

if (this._requestValueCollection != null)

{

string text1 = this._requestValueCollection[“ SCROLLPOSITIONX”];

if ((text1 != null) && !int.TryParse(text1, out this._scrollPositionX))

this._scrollPositionX = 0;

string text2 = this._requestValueCollection[“ SCROLLPOSITIONY”];

if ((text2 != null) && !int.TryParse(text2, out this._scrollPositionY))

PageRequestManager instance, and since the _requestValueCollection contains only the name/

value pairs that the current client-side PageRequestManager instance stuffed into the body of the

request, you may be wondering where the scroll x and y values come from The answer lies in

Listing 22-22 itself, which is partially repeated in the following code listing

Recall that this code listing contains the code for the _onFormSubmit method of the current client-side

PageRequestManager instance As discussed earlier, this method is automatically invoked when a

post-back occurs As the highlighted portions of the following code listing show, the current client-side

PageRequestManager instance iterates through all the input form elements on the page, including the

hidden fields named SCROLLPOSITIONX and SCROLLPOSITIONY , and forms for each input form

element one string that consists of two substrings, the first containing the value of the name HTML

attribute of the input form element (in this case, these values are SCROLLPOSITIONX and

SCROLLPOSITIONY ) and the second containing the value of the value HTML attribute of the input

form element (in this case, these values are the scroll x and y positions)

As the boldface portion of the following code listing shows, the current client-side PageRequestManager

instance stores the current scroll x and y positions in an internal field named _scrollPostion before it

submits the request to the server As you’ll see later, when the server response arrives, the current

client-side PageRequestManager instance retrieves the new scroll x and y positions from the response

and compares them with the old values stored in the _scrollPosition field to determine whether the

scroll x and y positions have indeed changed

Trang 7

formBody.append(encodeURIComponent(element.value));

formBody.append(‘&’);

} } } this._scrollPosition = this._getScrollPosition();

}

InitRecursive

I covered the non-postback-related parts of the InitRecursive life cycle phase of the Page object

in Chapter 21 ; therefore I’ll just cover the postback-related parts of this phase in this section Recall from Chapter 21 that the OnInit method of the current ScriptManager instance is automatically invoked when the current Page enters its Init phase Listing 23-5 presents the ScriptManager class’s internal implementation of the OnInit method, which it inherits from the Control base class I discussed all the parts of Listing 23-6 in Chapter 21 except for the highlighted portion, which is applicable only to post-back requests As you can see, this portion calls the IsAsyncPostBackRequest static method on the server-side PageRequestManager , passing in the NameValueCollection that contains the names and values of the request headers The main responsibility of this method is to determine whether the current request is an asynchronous page postback

Listing 23-5: The OnInit Method of the ScriptManager Class

protected override void OnInit(EventArgs e){

base.OnInit(e);

if (ScriptManager.GetCurrent(this.Page) != null) throw new InvalidOperationException(“OnlyOneScriptManager”);

this.IPage.Items[typeof(ScriptManager)] = this;

this.IPage.PreRenderComplete += new EventHandler(this.OnPagePreRenderComplete);

if (this.IPage.IsPostBack) this._isInAsyncPostBack = PageRequestManager.IsAsyncPostBackRequest(this.IPage.Request.Headers); this.PageRequestManager.OnInit();

string[] textArray1 = headers.GetValues(“X-MicrosoftAjax”);

Trang 8

Then it iterates through these values searching for a value that contains the string “Delta=true” If it

finds a value that contains this string, it returns true to signal its caller that the current request is an

asynchronous page postback

Listing 22-22 contains the client-side code that made the current asynchronous page postback The

fol-lowing code listing repeats a portion of Listing 22-22 As the highlighted portion of the folfol-lowing code

shows, the current client-side PageRequestManager instance added the “Delta=true” header value to

a custom request header named “X-MicrosoftAjax” to signal the server-side PageRequestManager

instance that the current request is an asynchronous page postback

string[] textArray2 = textArray1[num1].Split(new char[] { ‘,’ });

for (int num2 = 0; num2 < textArray2.Length; num2++)

The OnInit Method of PageRequestManager

Recall from Chapter 21 that the OnInit method of the current server-side PageRequestManager

instance is automatically invoked when the Page object enters its Init life cycle phase Listing 23-7

pres-ents the internal implementation of the OnInit method of the server-side PageRequestManager I

cov-ered all the parts of this method in Chapter 21 , except for the highlighted portion, because this portion is

run only when the current request is an asynchronous page postback As you can see, this portion simply

registers the OnPageError method of the current server-side PageRequestManager instance as an event

handler for the Error event of the current Page object

Trang 9

Listing 23-7: The OnInit Method of the PageRequestManager Class

internal void OnInit(){

if (this._owner.IsInAsyncPostBack) this._owner.IPage.Error += new EventHandler(this.OnPageError);

}

Load Post Data

Currently we’re at the Load Post Data life cycle phase, in which the ProcessRequest method (see Listing 21-1 ) invokes the ProcessPostData method of the current Page , passing in two parameters The first parameter is the _requestValueCollection field of the current Page Recall that this field is a col-lection of type NameValueCollection that contains one name/value pair for each posted data item, the name part containing the UniqueID property value of a server control and the value part containing the value associated with that server control For example, the name part of the name/value pair associ-ated with a TextBox server control contains the value of the UniqueID property of the TextBox control, and the value part contains the text that the end user has entered into the text field

As Listing 21-1 shows, the ProcessRequest method of the current Page passes true as the second ment of the ProcessPostData method to instruct this method that the Page is currently in a pre-Load life cycle phase As you’ll see later, the same ProcessPostData method will also be called after the Load life cycle phase The second Boolean argument allows this method to distinguish between these two calls

Listing 23-8 presents the internal implementation of the ProcessPostData method of the Page

Listing 23-8: The ProcessPostData Method of the Page Object

private void ProcessPostData(NameValueCollection postData, bool fBeforeLoad){

if (this._changedPostDataConsumers == null) this._changedPostDataConsumers = new ArrayList();

foreach (string text1 in postData) {

if (!Page.IsSystemPostField(text1)) {

Control control1 = this.FindControl(text1);

if (control1 == null) {

if (fBeforeLoad) {

if (this._leftoverPostData == null) this._leftoverPostData = new NameValueCollection();

this._leftoverPostData.Add(text1, null);

} } else { IPostBackDataHandler handler1 = control1 as IPostBackDataHandler;

Trang 10

This method first instantiates an ArrayList field named _changePostDataConsumers , if it hasn’t

already been instantiated You’ll see the significance of this field later

if (this._changedPostDataConsumers == null)

this._changedPostDataConsumers = new ArrayList();

Next, it iterates through the name/value pairs in the NameValueCollection passed into it as its first

argument, and takes the following actions for each enumerated name/value pair if the name part of the

pair does not contain the name attribute value of one of the standard hidden fields such as VIEWSTATE

(the name/value pairs associated with standard hidden fields will be processed later):

❑ The ProcessPostData method calls the FindControl method on the current Page object to

return a reference to the server control whose UniqueID property value is given by the name

part of the enumerated name/value pair:

Trang 11

Control control1 = this.FindControl(text1);

❑ If the current Page does not contain a server control with this UniqueID property value, this indicates that the server control has been dynamically added to the Page during the Page ’s Load life cycle phase For example, the page developers could add server controls to the current

Page within the Page_Load method, which is invoked when the Page enters its Load life cycle phase Since we’re currently at the Load Post Data life cycle phase, which occurs before the Load life cycle phase (see Figure 21-2 ), the ProcessPostData method does not process the enumer-ated name/value pair and instead stores the value of the name part of the pair in a collection named _leftOverPostData so that this name/value pair can be processed after the Load life cycle phase — that is, after the associated server control is added to the current Page :

if (this._leftoverPostData == null) this._leftoverPostData = new NameValueCollection();

this._leftoverPostData.Add(text1, null);

❑ If the current Page contains a server control whose UniqueID property value is given by the name part of the enumerated name/value pair, the ProcessPostData method takes the follow-ing steps to process the enumerated name/value pair First, it checks whether the server control implements the IPostBackDataEventHandler interface:

❑ If not, it checks whether the server control implements the IPostBackEventHandler interface If so, it calls the RegisterRequiresRaiseEvent method of the current Page , passing in the server control This method stores this server control in an internal field for future reference As you’ll see later, when the current Page enters its RaisePostBackEvent life cycle phase, it will automatically call the RaisePostBackEvent method of this server control In general, controls that implement the IPostBackEventHandler interface raise postback events

IPostBackDataHandler handler1 = control1 as IPostBackDataHandler;

if (handler1 == null) {

if (control1 as IPostBackEventHandler != null) this.RegisterRequiresRaiseEvent((IPostBackEventHandler)control1);

}

❑ If so, it calls the LoadPostData method on the associated server control, passing in two parameters The first parameter is the name part of the enumerated name/value pair, and the second parameter is the _requestValueCollection field Recall that this field con-tains all the name/value pairs (or data items) that the client has posted back to the server

❑ It is the responsibility of the LoadPostData method of the server control to use its first parameter as an index into the second parameter to retrieve the value of the value part of the associated name/value pair (or data item) For example, the LoadPostData method

of an ASP.NET TextBox server control uses its UniqueID property value as an index into the _requestValueCollection to retrieve the text that the end user has entered into the text field This method then compares this text with the value of the Text property

of the TextBox control If these two values are different, the end user has changed the original value of the text field As a result, the LoadPostData method returns true to signal the ProcessPostData method that its value has changed and that therefore its

RaisePostDataChangedEvent method must be invoked when the current Page enters its

RaisePostDataChangedEvent life cycle phase (see Figure 21-2 )

Trang 12

❑ Note that if the LoadPostData method returns true , the ProcessPostData method adds

the server control to an internal collection named _changedPostDataConsumers As you’ll see later, when the current Page object enters its RaisePostDataChangedEvent life cycle phase, it will iterate through the server controls in the _changedPostDataConsumers collection and invoke their RaisePostDataChangedEvent methods

if (handler1.LoadPostData(text1, this._requestValueCollection))

this._changedPostDataConsumers.Add(control1);

❑ Finally, the ProcessPostData method removes the current UniqueID property value

from the _controlsRequiringPostBack collection This collection maintains the

UniqueID property values of those server controls whose RaisePostBackEvent methods must be called when the current Page enters the RaisePostBackEvent life cycle phase

this._controlsRequiringPostBack.Remove(text1);

Next, the ProcessPostData method iterates through the items in the _controlsRequiringPostBack

collection Recall that this collection contains the UniqueID property values of those server

controls whose RaisePostBackEvent method must be invoked when the current Page enters the

RaisePostBackEvent life cycle phase The ProcessPostData method takes the following actions for

each enumerated UniqueID property value in this collection:

❑ First, it calls the FindControl method on the current Page to return a reference to the server

control with the enumerated UniqueID property value:

Control control2 = this.FindControl(text2);

❑ If the current Page object contains a server control with the enumerated UniqueID property value,

the ProcessPostData method calls the LoadPostData method on the server control, and if this

method returns true , it adds the server control to the _changedPostDataConsumers collection:

❑ If the current page does not contain a server control with the enumerated UniqueID property

value, the ProcessPostData method adds the server control to a local array, which is finally

added to the _controlsRequiringPostBack collection:

Trang 13

UpdatePanel

As these discussions show, the Load Post Data life cycle phase of the current Page object is applicable only to those server controls that implement the IPostBackDataHandler interface Since the current implementation of the UpdatePanel server control does not implement this interface, none of the

UpdatePanel server controls on the current Page will participate in the current Page ’s Load Post Data life cycle phase However, you can write a custom UpdatePanel server control that inherits the

UpdatePanel server control and implements the IPostBackDataHandler interface If you do so, the current Page object will automatically call the LoadPostData method of your custom UpdatePanel server control, and if your implementation of this method returns true , the current Page object will also automatically call the RaisePostDataChangedEvent method of your custom control

ScriptManager

As the boldface portion of Listing 23-9 shows, the ScriptManager implements the

IPostBackDataHandler interface As I already mentioned, this interface exposes two methods named LoadPostData and RaisePostDataChangedEvent

Listing 23-9: The Declaration of the ScriptManager Class

[ParseChildren(true), DefaultProperty(“Scripts”), NonVisualControl,PersistChildren(false)]

public class ScriptManager : Control, IPostBackDataHandler, IControl, IClientUrlResolver, IScriptManagerInternal{

protected virtual bool LoadPostData(string postDataKey,

of the current ScriptManager instance passes the same two parameters that were passed into it into the

LoadPostData method of the current server-side PageRequestManager instance

Trang 14

The LoadPostData Method of PageRequestManager

Listing 23-10 presents the internal implementation of the LoadPostData method of the server-side

PageRequestManager Recall that when the LoadPostData method of the current ScriptManager

server control calls the LoadPostData method of the current server-side PageRequestManager

instance, it passes two parameters into it The first parameter is a string that contains the value of the

UniqueID property of the current ScriptManager server control The second parameter is the

NameValueCollection that contains all the name/value pairs (each pair represents a posted data item)

that the current client-side PageRequestManager instance has posted back to the server

Recall that Listing 22-22 presents the internal implementation of the _onFormSubmit method of the

current client-side PageRequestManager instance This method is where the current client-side

PageRequestManager instance posts all its name/value pairs back to the server asynchronously The

following code listing repeats a portion of Listing 22-22 :

As the highlighted portion of this code listing shows, the current client-side PageRequestManager

instance posts a name/value pair whose name part contains the UniqueID property value of the current

ScriptManager server control, and whose value part contains the value of the panelID property of the

postback settings JavaScript object

Recall that Listing 22-22 shows where the value of the panelID property of the postback settings JavaScript

object is set This code listing contains the implementation of the _getPostBackSettings method of the

current client-side PageRequestManager instance As discussed in Chapter 22 , this method uses the

fol-lowing logic to set the value of the panelID property of the postback settings JavaScript object:

❑ If the server control that caused the current page postback resides in an UpdatePanel server

control whose ChildrenAsTriggers property is set to true , the _getPostBackSettings

method sets the value of the panelID property to a string that contains two substrings

sepa-rated by the pipe character ( | ), of which the first substring contains the UniqueID property

value of the UpdatePanel server control, and the second the UniqueID property value of the

server control that caused the current page postback As you’ll see shortly, the presence of the

UniqueID property value of this UpdatePanel server control signals the current server-side

PageRequestManager instance that this UpdatePanel server control must be updated

When a page postback is caused by a server control that resides in an UpdatePanel server control

whose ChildrenAsTriggers property is set to true , it automatically triggers the update of its

par-ent UpdatePanel server control

❑ If the _asyncPostBackControlIDs collection of the current client-side PageRequestManager

instance contains the UniqueID property value of the server control that caused the current

Trang 15

page postback, or one of its ancestor server controls, but the server control itself does not reside

in an UpdatePanel server control whose ChildrenAsTrigger property is set to true ,

_getPostBackSettings sets the value of the panelID property to a string that contains two substrings separated by the | character: the first substring contains the UniqueID property value of the ScriptManager server control, and the second substring contains the UniqueID property value of the server control that caused the current page postback

Keep this logic in mind as we’re walking through the implementation of the LoadPostData method of the server-side PageReqeustManager As you can see from Listing 23-10 , this method first uses its first parameter, which is nothing but the UniqueID property value of the current ScriptManager server control, as an index into the NameValueCollection to return the associated posted string value data

string text1 = postCollection[postDataKey];

This string value consists of up to two substrings separated by the | character As we discussed earlier, the second substring is the UniqueID property value of the server control that caused the current page postback The LoadPostData method assigns this substring to the _asyncPostBackSourceElementID field for future reference

As we also discussed earlier, the first substring contains the value of the UniqueID property of either the

ScriptManager server control or the UpdatePanel control whose content must be updated If the string does not contain the value of the UniqueID property of the ScriptManager server control — that

sub-is, if it contains the value of the UniqueID property of a specific UpdatePanel server control — the

LoadPostData method assigns this substring to the _updatePanelRequestUpdate field:

while (enumerator1.MoveNext()) {

enumerator1.Current.Initialize();

} }

Finally, the LoadPostData method sets an internal flag named _panelsInitialized to signal that all

UpdatePanel controls on the page have been initialized:

this._panelsInitialized = true;

Trang 16

Listing 23-10: The LoadPostData Method of the PageRequestManager

internal void LoadPostData(string postDataKey, NameValueCollection postCollection)

Figure 23-1 contains all the method calls that occur when the current Page object enters its Load Post

Data life cycle phase

LoadPostData

Page ScriptManager PageRequestManage UpdatePanel

LoadPostData (postdataKey, postCollection)

Initialize ( ) LoadPostData (postdataKey, postCollection)

Figure 23-1

The Raise Post Data Changed Event

When the Page object enters its Raise Post Data Changed Event phase (see Figure 21-2 ), it calls the

RaisePostDataChangedEvent methods of those server controls that meet the following two requirements:

❑ They implement the IPostBackDataHandler interface

❑ Their implementation of the LoadPostData method returns true

Trang 17

The ScriptManager server control meets the first requirement However, it does not meet the second requirement because its LoadPostData method returns false, as shown in Listing 23-9 Therefore, the

RaisePostDataChangedEvent method of the ScriptManager server control is never invoked As the following code listing shows, this method does not do anything anyway:

protected virtual void RaisePostDataChangedEvent(){

}

That said, you can implement a custom ScriptManager that extends the functionality of the

ScriptManager server control, where its implementation of the LoadPostData method returns true,

to have the Page object invoke its RaisePostDataChangedEvent Your custom ScriptManager control’s implementation of this method can then take the appropriate actions in response

Listing 23-11: The OnPreRender Method of the ScriptManager

protected override void OnPreRender(EventArgs e){

base.OnPreRender(e);

if (this.IsInAsyncPostBack) this.PageRequestManager.OnPreRender();

}

The OnPreRender Method of PageRequestManager

Listing 23-12 presents the implementation of the OnPreRender method of the server-side

PageRequestManager The Page object, like any other server control, inherits a method named

SetRenderMethodDelegate from the Control base class This method registers a delegate of type RenderMethod that represents another method In the case of Listing 23-12 , the

SetRenderMethodDelegate method registers a delegate that represents the RenderPageCallback method of the server-side PageRequestManager

As you’ll see later in this chapter, when the server control on which the SetRenderMethodDelegate method was invoked enters its rendering phase, in which its RenderChildren method is invoked, the

RenderChildren method invokes the RenderMethod delegate, and consequently the method that the delegate represents, bypassing the normal rendering logic that renders the child controls of the server control

Trang 18

In the case of Listing 23-12 , when the Page server control enters its rendering phase in which its

RenderChildren method is called, the RenderChildren method will invoke the delegate that

repre-sents the RenderPageCallback method of the PageRequestManager , bypassing the normal rendering

logic that renders the server controls in the Controls collection of the Page This allows the server-side

PageRequestManager to take complete control of the rendering of the server controls in the Controls

collection of the Page object when the current request is an asynchronous page postback

Listing 23-12: The OnPreRender Method of the PageRequestManager

internal void OnPreRender()

In this section, we’ll see what happens when the Page enters its rendering life cycle phase, in which the

Render method of the Page is invoked The Page inherits the Render method from the Control base

class Listing 23-13 presents the implementation of the Render method of the Control base class

Listing 23-13: The Render Method of the Control Base Class

protected internal override void Render(HtmlTextWriter writer)

{

this.RenderChildren(writer);

}

As you can see, the Render method of the Control base class simply calls the RenderChildren method

shown in Listing 23-14 Recall from Listing 23-12 that the OnPreRender method of the server-side

PageRequestManager created a RenderMethod delegate that represents the RenderPageCallback

method of the server-side PageRequestManager and invoked the SetRenderMethodDelegate method

on the Page object , passing in this RenderMethod delegate

Every server control, including the Page object, inherits the RenderChildren method shown in

Listing 23-14 from the Control base class As you can see, the RenderChildren method of a server

control first calls the GetRenderMethod to return a reference to the RenderMethod delegate registered

with the server control, if any In the case of the Page server control, since the current request is an

asynchronous page postback, the GetRenderMethod returns a reference to the RenderMethod delegate

that represents the PageRenderCallback method of the PageRequestManager

As Listing 23-14 shows, the RenderChildren method of a server control bypasses the normal rendering

logic of the server control’s child controls if a RenderMethod delegate has been registered with the

server control As the boldface portion of Listing 23-14 shows, the normal rendering logic of the server

control’s child controls simply iterates through the child controls in the server control’s Controls

collection and invokes the RenderControl method on each enumerated child control

In the case of the Page server control, if the current request is an asynchronous page postback, the

RenderChildren method invokes the registered RenderMethod delegate, which in turn invokes the

Trang 19

PageRenderCallback method of the server-side PageRequestManager The end result of all this is that the server-side PageRequestManager takes complete control over what gets rendered when the current request is an asynchronous page postback

Listing 23-14: The RenderChildren Method of the Control Base Class

protected internal virtual void RenderChildren(HtmlTextWriter writer){

RenderMethod renderMethod = this.GetRenderMethod();

if (renderMethod != null) {

writer.BeginRender();

renderMethod(writer, this);

writer.EndRender();

} else if (this.Controls != null) {

foreach (Control control in this.Controls)

{

control.RenderControl(writer);

}

}}

The Encode Method of PageRequestManager

Since the implementation of the RenderPageCallback method makes use of another method of

PageRequestManager named EncodeString , I’ll discuss the implementation of this method first

Listing 23-15 presents the internal implementation of the EncodeString method of the server-side

PageRequestManager This method takes four parameters as follows, encodes the values of its second through fourth parameters into a string, and writes this encoded string into the TextWriter object refer-enced by its first parameter

❑ writer : This parameter references the TextWriter instance in which the encoded string is stored This is normally an HtmlTextWriter instance that wraps the response output stream

❑ type : This parameter is a string that specifies the type of information that the encoded string contains As you’ll see later, the current client-side PageRequestManager instance will use this string to determine what type of information it is dealing with For example, if the encoded string contains the value of a hidden field, the server-side PageRequestManager instance uses the string “hiddenField” as the type to tell the current client-side PageRequestManager in-stance that the encoded string contains the name and value of a hidden field

❑ id : As you’ll see later, if the encoded string contains the value associated with a server control, this optional parameter contains the value of the ClientID property of the server control

❑ content : This parameter contains the actual value being encoded

As Listing 23-15 shows, the EncodeString method generates a string that contains four substrings rated by the | character, where the second, third, and fourth substrings contain the second and third parameters, and the encoded form of the fourth parameter, of the EncodeString method

Trang 20

Listing 23-15: The EncodeString Method of PageRequestManager

internal static void EncodeString(TextWriter writer, string type, string id,

char[] chArray1 = content.ToCharArray();

for (int num4 = 0; num4 < chArray1.Length; num4++)

The RenderPageCallback Method of PageRequestManager

Now back to the implementation of the RenderPageCallback method of the server-side

PageRequestManager , as shown in Listing 23-16 As you can see, this method takes two arguments

The first references the HtmlTextWriter instance that wraps the response output stream, which means

that anything that this method writes into this HtmlTextWriter instance will be automatically written

into the response output stream The second argument references the Page object The main

responsibility of this method is to render the Page object and its contents to the specified

HtmlTextWriter instance and consequently to the response output stream

As you can see, the RenderPageCallback method first calls the ProcessUpdatePanels method on

the current server-side PageRequestManager instance As you’ll see later in this chapter, the main

Trang 21

responsibility of this method is to determine which UpdatePanel server controls on the current page must be updated

this.ProcessUpdatePanels();

The RenderPageCallback method then sets the ContentType property of the ASP.NET Response object to text/plain to inform the current client-side PageRequestManager instance that the body of the response contains plain text:

IHttpResponse response1 = this._owner.IPage.Response;

response1.ContentType = “text/plain”;

Next, it calls the SetNoServerCaching on the ASP.NET Cache object to turn off server-side output caching for the current response, because the current request is an asynchronous page postback, which only updates specific portions of the page — that is, the portions encapsulated in the UpdatePanel server controls If the output caching were allowed, the next synchronous request for the page would be served from the cache and consequently the client would get HTML that contains only portions of the original page:

response1.Cache.SetNoServerCaching();

Next, the RenderPageCallback method creates a delegate of type RenderMethod that represents the

RenderFormCallback method of the server-side PageRequestManager :

RenderMethod renderMethod = new RenderMethod(this.RenderFormCallback);

Then it calls the SetRenderMethodDelegate method on the HtmlForm server control that represents the <form runat=”server”> HTML element of the current page to register the RenderMethod dele-gate with the HtmlForm server control As we discussed earlier, the HtmlForm server control, like any other, inherits the SetRenderMethodDelegate method from the Control base class When the

HtmlForm server control enters its rendering phase, in which its RenderChildren method is invoked, the RenderChildren method will call the delegate and consequently the RenderFormCallback method that the delegate represents, bypassing the normal rendering logic of the HtmlForm server control’s child controls Recall that this normal logic simply iterates through all the visible child controls of the

UpdatePanel server controls must be rendered; the rest of the page must be left alone

IHtmlForm form1 = this._owner.IPage.Form;

this._updatePanelWriter = writer;

Trang 22

Then the method instantiates an instance of an internal StringWriter subclass named

ParserStringWriter A string writer is a stream that contains an internal string in which it

accumulates the information written into the stream

PageRequestManager.ParserStringWriter writer1 =

new PageRequestManager.ParserStringWriter();

Next, the RenderPageCallback method instantiates an instance of an internal HtmlTextWriter

sub-class named ParserHtmlTextWriter that wraps the ParserStringWriter instance Note the

differ-ence between the ParserHtmlTextWriter instance and the HtmlTextWriter instance stored in the

_updatePanelWriter field While the former wraps the ParserStringWriter , the latter wraps the

response output stream This means that anything written into the former will be automatically written

into the ParserStringWriter , which is nothing but a string writer, and anything written into the latter

will be automatically written into the response output stream, which is sent back to the client In other

words, what’s written into the ParserHtmlTextWriter instance remains in the server memory for the

duration of the current request, while what’s written into the HtmlTextWriter instance stored in the

_updatePanelWriter field is sent to the client

PageRequestManager.ParserHtmlTextWriter writer2 =

new PageRequestManager.ParserHtmlTextWriter(writer1);

writer1.ParseWrites = true;

Then the RenderPageCallback method calls the RenderControl method on the HtmlForm server

con-trol to render the concon-trol and its child concon-trols into the ParserHtmlTextWriter instance Since the

RenderControl method renders the HtmlForm server control and its child controls into an in-memory

stream, as opposed to the response output stream, what gets rendered remains in memory, allowing the

RenderPageCallback method to decide which part of this rendered HTML markup must be sent to

the client

form1.RenderControl(writer2);

writer1.ParseWrites = false;

The HtmlForm server control may contain hidden fields When the RenderControl method of the

HtmlForm server control renders these hidden fields into the ParserHtmlTextWriter instance, this

instance stores these hidden fields in a collection named HiddenFields This collection contains one

KeyValuePair object for each hidden field: the name part of the pair contains the name of the hidden

field and the value part contains the value

The RenderPageCallback method iterates through the KeyValuePair objects in this collection and

takes the following steps for each enumerated pair First, it checks whether the enumerated pair

represents a standard hidden field If so, it calls the EncodeString static method on the

PageRequestManager class

As we discussed earlier, the EncodeString method first creates an encoded string that consists of three

main substrings separated by the | character — “hiddentField”|pair1.Key.ToString()|pair1

.Value.ToString() — in which the Key and Value properties of the KeyValuePair object contain the

name and value of the hidden field, respectively Next, the EncodeString method writes this encoded

string into the HtmlTextWriter object that wraps the response output stream

Trang 23

foreach (KeyValuePair<string, string> pair1 in writer1.HiddenFields) {

if (PageRequestManager.IsBuiltInHiddenField(pair1.Key)) PageRequestManager.EncodeString(writer, “hiddenField”, pair1.Key, pair1.Value);

PageRequestManager.EncodeString(writer, “asyncPostBackControlIDs”, string.Empty, this.GetAsyncPostBackControlIDs(false));

❑ “postBackControlIDs”|””|this.GetPostBackControlIDs(false) : The first part of this encoded string tells the current client-side PageRequestMananger instance that this string con-tains the list of the UniqueID property values of all server controls on the current page that cause synchronous page postbacks:

PageRequestManager.EncodeString(writer, “postBackControlIDs”, string.Empty, this.GetPostBackControlIDs(false));

❑ “updatePanelIDs”|””|this.GetAllUpdatePanelIDs() : The first part of this encoded string tells the current client-side PageRequestMananger instance that this string contains the list of the UniqueID property values of all UpdatePanel server controls on the current page:

PageRequestManager.EncodeString(writer, “updatePanelIDs”, string.Empty, this.GetAllUpdatePanelIDs());

❑ “childUpdatePanelIDs”|””|this.GetChildUpdatePanelIDs() : The first part of this encoded string tells the current client-side PageRequestMananger instance that this string contains the list of the UniqueID property values of all UpdatePanel server controls on the current page that reside inside another UpdatePanel server control:

PageRequestManager.EncodeString(writer, “childUpdatePanelIDs”, string.Empty, this.GetChildUpdatePanelIDs());

❑ “panelsToRefreshIDs”|””|this.GetRefreshingUpdatePanelIDs() : The first part of this encoded string tells the current client-side PageRequestMananger instance that this string con-tains the list of the UniqueID property values of all UpdatePanel server controls on the current page that need refreshing:

PageRequestManager.EncodeString(writer, “panelsToRefreshIDs”, string.Empty, this.GetRefreshingUpdatePanelIDs());

Trang 24

❑ “asyncPostBackTimeout”|””|this._owner.AsyncPostBackTimeout.ToString() : The

first part of this encoded string tells the current client-side PageRequestMananger instance that

this string contains the value of the asynchronous page postback timeout:

PageRequestManager.EncodeString(writer, “asyncPostBackTimeout”, string.Empty,

this._owner.AsyncPostBackTimeout.ToString());

❑ “formAction”|””|writer2.FormAction : The first part of this encoded string tells the

current client-side PageRequestMananger instance that this string contains the value of the

form action:

if (writer2.FormAction != null)

PageRequestManager.EncodeString(writer, “formAction”, string.Empty,

writer2.FormAction);

❑ “pageTitle”|””|this._owner.IPage.Title : The first part of this encoded string tells the

current client-side PageRequestMananger instance that this string contains the value of

the page title:

Next, the RenderPageCallback method calls the RenderDataItems method on the current server-side

PageRequestManager instance to render the data items into the server output stream:

Finally, the RenderPageCallback method calls the ProcessFocus method on the current server-side

PageRequestManager instance to give the mouse focus to the appropriate server control

this.ProcessFocus(writer);

Listing 23-16: The RenderPageCallback Method of the PageRequestManager

private void RenderPageCallback(HtmlTextWriter writer, Control pageControl)

Trang 25

this._updatePanelWriter = writer;

PageRequestManager.ParserStringWriter writer1 = new PageRequestManager.ParserStringWriter(); PageRequestManager.ParserHtmlTextWriter writer2 =

new PageRequestManager.ParserHtmlTextWriter(writer1); writer1.ParseWrites = true;

} PageRequestManager.EncodeString(writer, “asyncPostBackControlIDs”, string.Empty, this.GetAsyncPostBackControlIDs(false));

PageRequestManager.EncodeString(writer, “postBackControlIDs”, string.Empty, this.GetPostBackControlIDs(false));

PageRequestManager.EncodeString(writer, “updatePanelIDs”, string.Empty, this.GetAllUpdatePanelIDs());

PageRequestManager.EncodeString(writer, “childUpdatePanelIDs”, string.Empty, this.GetChildUpdatePanelIDs());

PageRequestManager.EncodeString(writer, “panelsToRefreshIDs”, string.Empty, this.GetRefreshingUpdatePanelIDs());

PageRequestManager.EncodeString(writer, “asyncPostBackTimeout”, string.Empty, this._owner.AsyncPostBackTimeout.ToString());

if (writer2.FormAction != null) PageRequestManager.EncodeString(writer, “formAction”, string.Empty, writer2.FormAction);

if (this._owner.IPage.Header != null) {

string text1 = this._owner.IPage.Title;

if (!string.IsNullOrEmpty(text1)) PageRequestManager.EncodeString(writer, “pageTitle”, string.Empty, text1);

} this.RenderDataItems(writer);

this.ProcessScriptRegistration(writer);

this.ProcessFocus(writer);

}

The ProcessUpdatePanels Method of PageRequestManager

As we discussed, the current page consists of a bunch of UpdatePanel server controls, each of which designates a particular region of the page as a partially updatable region When an asynchronous page postback request arrives — that is, when a request for a partial page rendering arrives, how does the page know which UpdatePanel server controls need updating or refreshing? An UpdatePanel server control needs updating if it meets one or more of the following requirements:

❑ The first substring of the string that the LoadPostData method retrieves from the post collection contains the value of the UniqueID property of the UpdatePanel server control

(Recall from Listing 23-10 that the LoadPostData method assigns this substring to the

_updatePanelRequestUpdate field.) As we discussed earlier in this chapter, this scenario

Trang 26

occurs when the server control that caused the current asynchronous page postback resides

inside an UpdatePanel server control whose ChildrenAsTriggers property is set to true

❑ The UpdateMode property of the UpdatePanel server control is set to Always

❑ The Update method of the UpdataPanel server control is explicitly invoked As you can see

from Listing 23-17 , this method simply sets the internal Boolean _explicitUpdate field of

the UpdatePanel server control on which the method is invoked to true , to specify that this

UpdatePanel server control must be updated

Use the Update method to imperatively force an UpdatePanel server control to refresh regardless of

what triggered the current asynchronous page postback

If you call the Update method on an UpdatePanel server control whose UpdateMode property is set

to Always , the method will raise an invalid operation exception

❑ The second substring of the string that the LoadPostData method retrieves from the post

collection contains the value of the UniqueID property of the associated server control of

one of the triggers in the Triggers collection of the UpdatePanel server control

Recall from Listing 23-10 that the LoadPostData method assigns this substring to the

_asyncPostBackSourceElementID field

❑ The UpdatePanel server control is a child of an UpdatePanel server control that needs updating

As you can see, the update of an UpdatePanel server control automatically triggers the updates of all its

descendant UpdatePanel server controls However, the update of an UpdatePanel server control does

not automatically trigger the updates of its ancestor UpdatePanel server controls If you want to force

the ancestor UpdatePanel server controls to update as well, you must take one of these extra steps

❑ Set the UpdateMode properties of the ancestor UpdatePanel server controls to Always

❑ Add the triggers that trigger the update of a child UpdatePanel server control to the Triggers

collections of the ancestor UpdatePanel server controls

❑ Imperatively call the Update methods of the ancestor UpdatePanel server controls

Listing 23-17: The Update Method of the UpdatePanel Server Control

public void Update()

PageRequestManager instance calls the ProcessUpdatePanels method on the current

server-side PageRequestManager instance to determine which UpdatePanel server controls on the current

page must be updated Recall that the current server-side PageRequestManager instance maintains

the list of all UpdatePanel server controls on the current page in an internal collection named

_allUpdatePanels

Trang 27

As you can see from Listing 23-18 , the ProcessUpdatePanels method iterates through the UpdatePanel server controls in this collection and takes the following steps for each enumerated UpdatePanel server control to determine whether the control needs refreshing (Note that this method sets a local Boolean variable named updatePanelNeedsToUpdate to specify whether the enumerated UpdatePanel server control needs updating.)

Listing 23-18: The ProcessUpdatePanels Method of the PageRequestManager

private void ProcessUpdatePanels(){

if (this._allUpdatePanels != null) {

this._updatePanelsToRefresh = new List<UpdatePanel>(this._allUpdatePanels.Count); this._childUpdatePanelsToRefresh =

new List<UpdatePanel>(this._allUpdatePanels.Count); HtmlForm form1 = this._owner.Page.Form;

for (int num1 = 0; num1 < this._allUpdatePanels.Count; num1++) {

UpdatePanel panel1 = this._allUpdatePanels[num1];

bool updatePanelNeedsToUpdate = false;

if ((this._updatePanelRequiresUpdate != null) &&

string.Equals(panel1.UniqueID, this._updatePanelRequiresUpdate)) updatePanelNeedsToUpdate = true;

else updatePanelNeedsToUpdate = panel1.RequiresUpdate;

Control control1 = panel1.Parent;

while (control1 != form1) {

UpdatePanel panel2 = control1 as UpdatePanel;

if ((panel2 != null) &&

(this._updatePanelsToRefresh.Contains(panel2) ||

this._childUpdatePanelsToRefresh.Contains(panel2))) {

updatePanelNeedsToUpdate = false;

this._childUpdatePanelsToRefresh.Add(panel1);

break;

} control1 = control1.Parent;

if (control1 == null) {

updatePanelNeedsToUpdate = false;

break;

} }

if (updatePanelNeedsToUpdate) {

panel1.SetAsyncPostBackMode(true);

this._updatePanelsToRefresh.Add(panel1);

} else panel1.SetAsyncPostBackMode(false);

} }}

Trang 28

The ProcessUpdatePanels method first checks whether the _updatePanelRequiresUpdate field of

the current server-side PageRequestManager instance contains the UniqueID property value of the

enumerated UpdatePanel server control Recall from Listing 23-10 that the LoadPostData method of

the current server-side PageRequestManager instance retrieves from the posted data the UniqueID

property value of the UpdatePanel server control that requires refreshing (if any) and stores the value in

the _updatePanelRequestUpdate field of the current server-side PageRequestManager instance

if ((this._updatePanelRequiresUpdate != null) &&

string.Equals(panel1.UniqueID, this._updatePanelRequiresUpdate))

updatePanelNeedsToUpdate = true;

If this check fails — that is, if the _updatePanelRequiresUpdate field does not contain the UniqueID

property value of the enumerated UpdatePanel server control — the ProcessUpdatePanels method

sets the value of the updatePanelNeedsToUpdate Boolean variable to the value of the RequiresUpdate

property of the enumerated UpdatePanel server control

else

updatePanelNeedsToUpdate = panel1.RequiresUpdate;

Next, I’ll digress from our discussion of the implementation of the ProcessUpdatePanels method to

discuss the implementation of the RequiresUpdate property of the UpdatePanel server control

As Listing 23-19 shows, the RequiresUpdate property returns true if:

❑ The _explicitUpdate field of the UpdatePanel server control is set to true Recall from

List-ing 23-17 that the value of this field is set to true only when you explicitly invoke the Update

method of the UpdatePanel server control from within your code

❑ The UpdateMode property of the UpdatePanel server control is set to Always

❑ The HasTriggered method of the Triggers collection of the UpdatePanel server control returns

true As Listing 23-20 shows, the HasTriggered method of this collection returns true if the

HasTriggered method of at least one of its UpdatePanelTrigger objects returns true (I

dis-cussed the HasTriggered method of the AsyncPostBackTrigger class in Chapter 21 )

Listing 23-19: The RequiresUpdate Method of the UpdatePanel Server Control

protected internal virtual bool RequiresUpdate

Trang 29

Listing 23-20: The HasTriggered Method of the UpdatePanelTriggerCollection Class

internal bool HasTriggered(){

using (IEnumerator<UpdatePanelTrigger> enumerator1 = base.GetEnumerator()) {

while (enumerator1.MoveNext()) {

if (enumerator1.Current.HasTriggered()) return true;

} } return false;

UpdatePanel server control to determine whether it resides inside another UpdatePanel server control

If so, it checks whether the _updatePanelsToRefresh or _childUpdatePanelsToRefresh collection

of the current server-side PageRequestManager instance already contains the container UpdatePanel server control If so, this indicates that the container UpdatePanel server control of the enumerated

UpdatePanel server control needs to update Since updating the container UpdatePanel server control automatically updates all its content, including the enumerated UpdatePanel server control, the

ProcessUpdatePanels method first sets the updatePanelNeedsToUpdate local Boolean variable

to false to signal that the enumerated UpdatePanel server control mustn’t be added to the

_updatePanelsToRefresh collection This avoids duplicate updates The ProcessUpdatePanels method then adds the enumerated UpdatePanel server control to the _childUpdatePanelsToRefresh collection

while (control1 != form1) {

UpdatePanel panel2 = control1 as UpdatePanel;

if ((panel2 != null) &&

(this._updatePanelsToRefresh.Contains(panel2) ||

this._childUpdatePanelsToRefresh.Contains(panel2))) {

updatePanelNeedsToUpdate = false;

this._childUpdatePanelsToRefresh.Add(panel1);

break;

} control1 = control1.Parent;

if (control1 == null) {

updatePanelNeedsToUpdate = false;

break;

} }

Trang 30

Finally, the ProcessUpdatePanels method checks whether the updatePanelNeedsToUpdate variable

is set to true If so, it first calls the SetAsyncPostBackMode method on the enumerated UpdatePanel

server control and then adds the control to the _updatePanelsToRefresh collection of the current

server-side PageRequestManager instance As Listing 23-21 shows, the SetAsyncPostBackMode

method sets an internal flag of the specified UpdatePanel server control, named _asyncPostBackMode ,

Recall from Listing 21-31 that the markup text that makes up an UpdatePanel server

control consists of two main parts The first part is a div or span HTML element The

second is the rest of the markup text that makes up the UpdatePanel server control

The first part is known as the containing or outermost HTML element because it contains

or encapsulates the second part The second part is known as content because it is

con-tained or enclosed within the opening and closing tags of the containing or outermost

HTML element

As you’ll see later in this chapter, the current server-side PageRequestManager

instance renders the content of an UpdatePanel server control that needs updating

into a string, which is then sent back to the current client-side PageRequestManager

instance for processing

As you’ll see in the next chapter, the current client-side PageRequestManager instance

simply assigns the string that contains the content of the UpdatePanel server control

to the innerHTML property of the containing or outermost HTML element of the

UpdatePanel server control This means that the original content of the UpdatePanel

server control is completely wiped out and replaced with the new content This has an

important consequence when the UpdatePanel server control contains child

UpdatePanel server controls, because updating the UpdatePanel server control

deletes the current child UpdatePanel server controls and replaces them with

brand-new child UpdatePanel server controls, even though the new child UpdatePanel

server controls have the same UniqueID and ClientID property values as the

deleted ones

Therefore, as far as the current client-side PageRequestManager instance is concerned,

when an UpdatePanel server control updates, its child UpdatePanel server controls

do not Instead they are completely deleted from the current page and replaced with

brand-new child UpdatePanel server controls with the same UniqueID and ClientID

property values as the deleted ones

So far, we’ve assumed that the new content simply deleted the old child UpdatePanel

server controls and replaced them with the brand-new child UpdatePanel server controls

with the same UniqueID and ClientID property values as the deleted ones However, it

is quite possible that the new content may also include new child UpdatePanel server

controls that are not replacing the old ones It is also quite possible that some of the old

child UpdatePanel server controls are indeed deleted from the new content, which

means that the new content may have fewer child server controls than the old

Trang 31

If the updatePanelNeedsToUpdate variable is set to false , the ProcessUpdatePanels method calls the SetAsyncPostBackMode method on the enumerated UpdatePanel server control to set its

_asyncPostBackMode field to false

else panel1.SetAsyncPostBackMode(false);

As you’ll see later, when the UpdatePanel server control enters its rendering phase, it checks the value

of its _asyncPostBackMode field to determine how to render its content

Listing 23-21: The SetAsyncPostBackMode Method of the UpdatePanel

internal void SetAsyncPostBackMode(bool asyncPostBackMode){

if (this._asyncPostBackModeInitialized) throw new InvalidOperationException(“SetPartialRenderingModeCalledOnce”);

this._asyncPostBackMode = asyncPostBackMode;

this._asyncPostBackModeInitialized = true;

}

RenderControl Method of HtmlForm

Recall from Listing 23-16 that the RenderPageCallback method creates a delegate of type

RenderMethod that represents the RenderFormCallback method of the current server-side

PageRequestManager instance and calls the SetRenderMethodDelegate method on the HtmlForm server control that represents the <form runat=”server”> HTML element of the current page to register this RenderMethod delegate with the HtmlForm server control

Also recall from Listing 23-16 that the RenderPageCallback method calls the RenderControl method

on the HtmlForm server control to have this server control render itself to the ParserHtmlTextWriter instance passed into the RenderControl method as its argument Listing 23-22 presents the internal implementation of the RenderControl method of the HtmlForm server control As you can see, this method calls the RenderControl method of the Control base class, which in turn calls the Render method of the Control base class As Listing 23-13 shows, the Render method of the Control base class calls the RenderChildren method

Listing 23-22: The RenderControl Method of the HtmlForm Server Control

public override void RenderControl(HtmlTextWriter writer){

if (base.DesignMode) base.RenderChildren(writer);

else base.RenderControl(writer);

}

Listing 23-23 presents the internal implementation of the RenderChildren method of the HtmlForm server control This method first invokes the OnFormRender method on the Page :

this.Page.OnFormRender();

Trang 32

Next, it calls the BeginFormRender method on the Page :

Listing 23-23: The RenderChildren Method of the HtmlForm Server Control

protected internal override void RenderChildren(HtmlTextWriter writer)

The RenderChildren method of the Control base class (see Listing 23-14 ) calls the GetRenderMethod

to return a reference to the RenderMethod delegate that represents the RenderFormCallback method

and calls this delegate, and consequently the RenderFormCallback method, bypassing the normal

rendering logic of the child controls of the HtmlForm server control Recall that this normal logic simply

iterates through the child controls in the Controls collection of the HtmlForm server control and calls

the RenderControl method on each child control, as shown in the boldface portion of Listing 23-14

The RenderFormCallback Method of PageRequestManager

Listing 23-24 presents the internal implementation of the RenderFormCallback method of the

server-side PageRequestManager Recall that the ProcessUpdatePanels method populates an internal

collection named _updatePanelsToRefresh with all the UpdatePanel server controls that need

refreshing The RenderFormCallback method iterates through the UpdatePanel server controls in this

collection and calls the RenderControl method on each enumerated UpdatePanel server control to have

the control render itself into the HtmlTextWriter object that the _updatePanelWriter field references

Recall from Listing 23-16 that the RenderPageCallback method of the PageRequestManager

stores a reference to the HtmlTextWriter object that wraps the response output stream in the

_updatePanelWriter field Therefore, the RenderControl method of the enumerated UpdatePanel

Trang 33

server control ends up rendering itself into the response output stream The response output stream is the stream that contains the response text that is sent back to the requesting browser

As you can see, the RenderFormCallback method renders the UpdatePanels in the

_updatePanelsToRefresh collection only In other words, none of the other server controls on the current page is rendered when the current request is an asynchronous page postback The other server controls go through all their life cycle phases as usual, except for the rendering phase This phase is what makes an asynchronous page postback request different from a synchronous page postback request

While the HTML markup contained in the response output stream in the case of a synchronous page postback contains HTML markup text from all visible server controls on the current page, the HTML markup text contained in the response output stream in the case of an asynchronous page postback contains HTML markup text only from the visible UpdatePanel server controls in the

_updatePanelsToRefresh collection

Listing 23-24: The RenderFormCallback Method of the PageRequestManager

private void RenderFormCallback(HtmlTextWriter writer, Control containerControl){

PageRequestManager.ParserStringWriter writer1 = writer.InnerWriter as PageRequestManager.ParserStringWriter;

writer1.ParseWrites = false;

if (this._updatePanelsToRefresh != null) {

foreach (UpdatePanel panel1 in this._updatePanelsToRefresh) {

if (panel1.Visible) panel1.RenderControl(this._updatePanelWriter);

} } writer1.ParseWrites = true;

}

The RenderControl Method of the UpdatePanel

The UpdatePanel server control inherits the RenderControl method from the Control base class The base class’s implementation of the RenderControl method calls the Render method shown in Listing 23-25 As you can see, this method calls the VerifyRenderingInServerForm method on the Page object to raise an exception if the UpdatePanel server control is not inside a <form runat=”server”> element:

this.IPage.VerifyRenderingInServerForm(this);

Next, it calls the Render method of its base class, which in turn calls the RenderChildren method

Listing 23-25: The Render Method of the UpdatePanel

protected override void Render(HtmlTextWriter writer){

this.IPage.VerifyRenderingInServerForm(this);

base.Render(writer);

}

Trang 34

Listing 23-26 presents the implementation of the RenderChildren method of the UpatePanel server

control This method checks whether the UpdatePanel control is in asynchronous postback mode

Recall from Listing 23-21 that the ProcessUpdatePanels method calls the SetAsyncPostBackMode

method on each UpdatePanel server control in the _allUpdatePanels collection of the current

server-side PageRequestManager instance to set the value of the _asyncPostBackMode field of the

UpdatePanel server control to specify whether the UpdatePanel server control must be rendered in

asynchronous postback mode

As Listing 23-26 shows, the RenderChildren method of an UpdatePanel server control instantiates an

HtmlTextWriter instance when the control is in asynchronous postback mode :

HtmlTextWriter writer1 = new HtmlTextWriter(new StringWriter());

Next, it calls the RenderChildren method of its base class — that is, the Control base class — passing

in the HtmlTextWriter instance Recall from Listing 23-14 that the RenderChildren method of the

Control base class iterates through the child controls in the Controls collection and invokes the

RenderControl method on each child control to have the child control render itself into the preceding

HtmlTextWriter instance In other words, this HtmlTextWriter instance accumulates the HTML

markup text generated by the server controls and HTML enclosed within the opening and closing tags of

the <ContentTemplate> child element of the <UpdatePanel> tag

base.RenderChildren(writer1);

Finally, the RenderChildren method of the UpdatePanel control calls the EncodeString static

method on the server-side PageRequestManager class, passing in four parameters: the first references

the HtmlTextWriter instance that wraps the response output stream, the second is the string

“updatePanel” , the third contains the value of the ClientID property of the UpdatePanel server

con-trol, and the last is a string that contains all the HTML markup of the child controls of the UpdatePanel

server control The main responsibility of the EncodeString method is to encode the last three

parameters into a string and render the string into the HtmlTextWriter object that the first parameter

references — that is, the HtmlTextWriter instance that wraps the response output stream

Listing 23-26: The RenderChildren Method of the UpdatePanel

protected override void RenderChildren(HtmlTextWriter writer)

Trang 35

if (this.RenderMode == UpdatePanelRenderMode.Block) writer.RenderBeginTag(HtmlTextWriterTag.Div);

else writer.RenderBeginTag(HtmlTextWriterTag.Span);

base.RenderChildren(writer);

writer.RenderEndTag();

} this._rendered = true;

}

The GetAsyncPostBackControlIDs Method of the Server-Side PageRequestManager

Listing 23-27 presents the implementation of the GetAsyncPostBackControlIDs method of the

PageRequestManager As you can see, this method simply delegates to the GetControlIDsFromList method, passing in the _asyncPostBackControls collection Recall that the PageRequestManager server class features a private collection named _asyncPostBackControl that contains all the server controls that cause asynchronous page postbacks

Listing 23-27: The GetAsyncPostBackControlIDs Method of the PageRequestManager

private string GetAsyncPostBackControlIDs(bool includeQuotes){

return PageRequestManager.GetControlIDsFromList(this._asyncPostBackControls, includeQuotes);

}

The GetControlIDsFromList Method of the Server-Side PageRequestManager

Listing 23-28 contains the code for the GetControlIDsFromList method of the server-side

PageRequestManager This method takes a List<Control> collection that contains a list of server controls The main responsibility of this method is to return a comma-separated list of strings, each of which contains the UniqueID property value of a server control in the List<Control> collection Note that this method takes a second Boolean argument that specifies whether these UniqueID property values must be rendered in quotes

Listing 23-28: The GetControlIDsFromList Method of the PageRequestManager

private static string GetControlIDsFromList(List<Control> list, bool includeQuotes){

if ((list == null) || (list.Count <= 0)) return string.Empty;

StringBuilder builder1 = new StringBuilder();

bool flag1 = true;

for (int num1 = 0; num1 < list.Count; num1++) {

if (list[num1].Visible) {

if (!flag1) builder1.Append(“,”);

(continued)

Trang 36

The GetPostBackControlIDs Method of the Server-Side PageRequestManager

Listing 23-29 presents the implementation of the GetPostBackControlIDs method of the server-side

PageRequestManager As you can see, this method simply delegates to the GetControlIDsFromList

method, passing in the _postBackControls collection Recall that the PageRequestManager features a

private collection field named _postBackControls that contains all the server controls on the current

page that cause synchronous page postbacks

Listing 23-29: The GetPostBackControlIDs Method of the PageRequestManager

private string GetPostBackControlIDs(bool includeQuotes)

Recall from Listing 23-16 that the RenderPageCallback method of the server-side PageRequestManager

calls the GetAllUpdatePanelIDs method to return a comma-separated list of strings that contain the

values of the UniqueID properties of all the UpdatePanel server controls on the current page:

PageRequestManager.EncodeString(writer, “updatePanelIDs”, string.Empty,

this.GetAllUpdatePanelIDs());

Listing 23-30 presents the internal implementation of the GetAllUpdatePanelIDs method As you can

see, this method simply delegates to the GetUpdatePanelIDsFromList static method of the server-side

PageRequestManager

Listing 23-30: The GetAllUpdatePanelIDs Method of the PageRequestManager

private string GetAllUpdatePanelIDs()

{

return PageRequestManager.GetUpdatePanelIDsFromList(this._allUpdatePanels, true);

}

Trang 37

GetUpdatePanelIDsFromList method takes a second Boolean argument that specifies whether each substring must contain the first part — that is, the letter f or t

As you can see from Listing 23-31 , the GetUpdatePanelIDsFromList method instantiates a

StringBuilder , iterates through the UpdatePanel server controls in the List<UpdatePanel>

collection, and appends a substring to the StringBuilder for each UpdatePanel server control Note that the content of the first part of this substring depends on the value of the ChildrenAsTriggers property of the associated UpdatePanel server control If this property is set to true , the first part of the substring contains the letter t Otherwise the first part of the substring contains the letter f

Listing 23-31: The GetUpdatePanelIDsFromList Static Method of the PageRequestManager

private static string GetUpdatePanelIDsFromList(List<UpdatePanel> list, bool includeChildrenAsTriggersPrefix){

if ((list == null) || (list.Count <= 0)) return string.Empty;

StringBuilder builder1 = new StringBuilder();

bool flag1 = true;

for (int num1 = 0; num1 < list.Count; num1++) {

if (list[num1].Visible) {

if (!flag1) builder1.Append(‘,’);

flag1 = false;

if (includeChildrenAsTriggersPrefix) builder1.Append(list[num1].ChildrenAsTriggers ? ‘t’ : ‘f’);

builder1.Append(list[num1].UniqueID);

} } return builder1.ToString();

}

GetChildUpdatePanelIDs

Recall from Listing 23-16 that the RenderPageCallback method of the server-side PageRequestManager calls the GetChildUpdatePanelIDs method to return a comma-separated list of strings that contain the values of the UniqueID properties of all the child UpdatePanel server controls on the current page:

PageRequestManager.EncodeString(writer, “childUpdatePanelIDs”, string.Empty, this.GetChildUpdatePanelIDs());

Ngày đăng: 09/08/2014, 06:23