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

ASP.NET AJAX Programmer’s Reference with ASP.NET 2.0 or ASP.NET 3.5 phần 10 pdf

158 277 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 đề ASP.NET AJAX Programmer’s Reference with ASP.NET 2.0 or ASP.NET 3.5
Trường học University of Example
Chuyên ngành Web Development
Thể loại Reference book
Năm xuất bản 2007
Thành phố Unknown
Định dạng
Số trang 158
Dung lượng 2,7 MB

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

Nội dung

If so, it invokes the add method on the data collection to add an empty record to the collection: if Sys.Preview.Data.IData.isImplementedBythis._data this._data.add{}; If the bound dat

Trang 1

Appendix D: Data Control

1367

Next, it checks whether the bound data collection implements the IData interface If so, it invokes the

add method on the data collection to add an empty record to the collection:

if (Sys.Preview.Data.IData.isImplementedBy(this._data)) this._data.add({});

If the bound data collection is a JavaScript array, and if it exposes a method named add , the addItem method simply calls this method to add an empty record to the data collection:

else if (this._data instanceof Array) {

if(typeof(this._data.add) === “function”) this._data.add({});

If the bound data collection is a JavaScript array but it does not expose the add method, the addItem method simply calls the add static method on the Array class to add an empty record to the data collection:

else if (this._data instanceof Array) {

if(typeof(this._data.add) === “function”) this._data.add({});

else Array.add(this._data, {});

this.triggerChangeEvents(oldState);

Listing D-14: The addItem Method

function Sys$Preview$UI$Data$DataControl$addItem(){

if (this._data) {

var oldState = this.prepareChange();

if (Sys.Preview.Data.IData.isImplementedBy(this._data)) this._data.add({});

(continued)

Trang 2

Appendix D: Data Control

As the name suggests, the deleteCurrentItem method deletes the current data record from the bound

data collection — if the data control is indeed bound to a data collection As Listing D-15 shows, this

method begins by invoking the prepareChange method to return the JavaScript object that contains the

current values of the dataIndex , canMoveNext , and canMovePrevious properties:

var oldState = this.prepareChange();

Next, it sets an internal flag to true to signal that all change notifications must be suspended because

we’re about to introduce new changes:

this._suspendChangeNotifications = true;

Then it calls the get_dataItem getter to return a reference to the current data record:

var item = this.get_dataItem();

Next, it resets the current data index if the current data record is the last data record in the data

collection:

if (this.get_dataIndex() === this.get_length() - 1)

this.set_dataIndex(Math.max(0, this.get_length() - 2));

Then it checks whether the bound data collection implements the IData interface If so, it invokes the

remove method on the bound data collection to remove the current data record:

if (Sys.Preview.Data.IData.isImplementedBy(this._data))

this._data.remove(item);

Next, the deleteCurrentItem method checks whether the bound data collection is a JavaScript array

and whether it supports the remove method If so, it invokes the remove method on the data collection

to remove the current data record:

Trang 3

Appendix D: Data Control

1369

else if (this._data instanceof Array) {

if(typeof(this._data.remove) === “function”) this._data.remove(item);

If the bound data collection is a JavaScript array but does not support the remove method, it calls the

remove static method on the Array class to remove the current data record for the data collection:

else if (this._data instanceof Array) {

if(typeof(this._data.remove) === “function”) this._data.remove(item);

else Array.remove(this._data, item);

this.triggerChangeEvents(oldState);

Listing D-15: The deleteCurrentItem Method

function Sys$Preview$UI$Data$DataControl$deleteCurrentItem(){

if (this._data) {

var oldState = this.prepareChange();

this._suspendChangeNotifications = true;

var item = this.get_dataItem();

if (this.get_dataIndex() === this.get_length() - 1) this.set_dataIndex(Math.max(0, this.get_length() - 2));

if (Sys.Preview.Data.IData.isImplementedBy(this._data)) this._data.remove(item);

else if (this._data instanceof Array) {

if(typeof(this._data.remove) === “function”) this._data.remove(item);

else Array.remove(this._data, item);

} this._suspendChangeNotifications = false;

this.triggerChangeEvents(oldState);

}}

Trang 4

Appendix D: Data Control

1370

getItem

The getItem method of the DataControl base class enables you to return a reference to the data record

with the specified data index As you can see from Listing D-16 , this method first checks whether the

data control is indeed bound to a data collection If not, it returns null If so, it checks whether the

bound data collection implements the IData interface If so, it simply calls the getItem method on the

data collection to return a reference to the data record with the specified index:

if (Sys.Preview.Data.IData.isImplementedBy(this._data))

return this._data.getItem(index);

If not, it checks whether the bound data collection is a JavaScript array If so, it uses the specified data

index as an index into the data collection to return a reference to the data record with the specified index:

if (this._data instanceof Array)

The moveNext method of the DataControl base class enables you to move to the next data record in the

bound data collection As Listing D-17 shows, if the data control is not bound to any data collection, the

moveNext method does not do anything This method begins by invoking the prepareChange method,

as usual:

var oldState = this.prepareChange();

Next, it calls the get_dataIndex getter to return the current data index, and increments this value by

one to arrive at the new value for the current data index:

var newIndex = this.get_dataIndex() + 1;

Trang 5

Appendix D: Data Control

1371

If the new value is not greater than or equal to the total number of data records in the bound collection, it calls the set_dataIndex setter to set the current data index to the new value:

if (newIndex < this.get_length()) this.set_dataIndex(newIndex);

Finally, it invokes the triggerChangeEvents method as usual to trigger the necessary events:

this.triggerChangeEvents(oldState);

Listing D-17: The moveNext Method

function Sys$Preview$UI$Data$DataControl$moveNext(){

if (this._data) {

var oldState = this.prepareChange();

var newIndex = this.get_dataIndex() + 1;

if (newIndex < this.get_length()) this.set_dataIndex(newIndex);

this.triggerChangeEvents(oldState);

}}

movePrevious

As the name suggests, the movePrevious method of the DataControl base class enables you to move

to the previous data record of the bound data collection As Listing D-18 shows, this method begins by calling the prepareChange method as usual:

var oldState = this.prepareChange();

Next, it calls the get_dataIndex getter to return the current data index and decrements this value by one to arrive at the new value:

var newIndex = this.get_dataIndex() - 1;

If the new value is a positive number, it invokes the set_dataIndex setter to set the current data index

to the new value:

if (newIndex >=0) this.set_dataIndex(newIndex);

Finally, it invokes the triggerChangeEvents method as usual:

this.triggerChangeEvents(oldState);

Trang 6

Appendix D: Data Control

var oldState = this.prepareChange();

var newIndex = this.get_dataIndex() - 1;

The DataControl base class overrides the onBubbleEvent method that it inherits from the Control

base class, as shown in Listing D-19 Recall that the onBubbleEvent method is where a client control

captures the command events raised by its child controls The DataControl base class’ implementation

of this method only handles the select event; that is why the method begins by calling the

get_commandName method on its second parameter to determine whether the current event is a

select event If so, it takes these steps to handle the event First, it calls the get_argument method

on its second parameter to return the index of the selected data record:

var arg = args.get_argument();

If no data index has been specified, the onBubbleEvent takes these steps to access the current data

index, and uses this index as the selected index First, it invokes the get_dataContext to return a

reference to the current data record:

var dataContext = source.get_dataContext();

Next, it invokes the get_index method on the current data record to return its index, and uses this

index as the selected index:

arg = dataContext.get_index();

Next, it calls the set_dataIndex method to specify the selected index as the current data index:

this.set_dataIndex(arg);

Trang 7

Appendix D: Data Control

1373

Listing D-19: The onBubbleEvent Method

function Sys$Preview$UI$Data$DataControl$onBubbleEvent(source, args){

if (args.get_commandName() === “select”) {

var arg = args.get_argument();

if (!arg && arg !== 0) {

var dataContext = source.get_dataContext();

if (dataContext) arg = dataContext.get_index();

}

if (arg && String.isInstanceOfType(arg)) arg = Number.parseInvariant(arg);

if (arg || arg === 0) {

this.set_dataIndex(arg);

return true;

} } return false;

}

descriptor

The DataControl base class, like any other ASP.NET AJAX client class, exposes a static property named descriptor that describes its methods and properties to enable its clients to use the ASP.NET AJAX client-side type inspection facilities to inspect its methods and properties generically, without knowing the actual type of the class, as shown in Listing D-20

Listing D-20: The descriptor Property

Sys.Preview.UI.Data.DataControl.descriptor = {

properties: [ { name: ‘canMoveNext’, type: Boolean, readOnly: true }, { name: ‘canMovePrevious’, type: Boolean, readOnly: true }, { name: ‘data’, type: Sys.Preview.Data.DataTable },

{ name: ‘dataIndex’, type: Number }, { name: ‘dataItem’, type: Object, readOnly: true }, { name: ‘length’, type: Number, readOnly: true } ], methods: [ { name: ‘addItem’ },

{ name: ‘deleteCurrentItem’ }, { name: ‘moveNext’ },

{ name: ‘movePrevious’ } ]}

Trang 8

Appendix D: Data Control

1374

Developing a Custom Data Control

Listing D-21 presents the content of a JavaScript file named CustomTable.js that contains the

implementation of a new version of the CustomTable control that derives from the DataControl base

class As you can see, the render method is where all the action is This is the method that renders the

user interface of the CustomTable custom data control As you can see, this method begins by invoking

the get_data method to return a reference to the data collection bound to the CustomTable data

control This control, like any other data control, inherits the get_data method from the DataControl

base class:

var dataSource = this.get_data();

Next, the render method raises an exception if the data collection bound to the data control is neither a

JavaScript array nor an IData object:

if (Sys.Preview.Data.IData.isImplementedBy(dataSource))

isArray = false;

else if (!Array.isInstanceOfType(dataSource))

throw Error.createError(‘Unknown data source type!’);

Next, the render method simply iterates through the data records in the data collection bound to the

data control to render each record in a <tr> DOM element

Listing D-21: The Content of the CustomTable.js JavaScript File that Contains the

Implementation of the CustomTable Custom Data Control

var isArray = true;

var dataSource = this.get_data();

Trang 9

Appendix D: Data Control

var length = isArray ? dataSource.length : dataSource.get_length();

for (var i=0; i<length; i++) {

var dataItem = isArray? dataSource[i] : dataSource.getItem(i);

if (i == 0) {

sb.append(‘<tr style=”background-color:Tan; font-weight:bold”>’);

for (var c in this._dataFieldNames) {

sb.append(‘<td>’);

sb.append(this._dataFieldNames[c]);

sb.append(‘</td>’);

} sb.append(‘</tr>’);

}

if (i % 2 == 1) sb.append(‘<tr style=”background-color:PaleGoldenrod”>’);

else sb.append(‘<tr>’);

for (var j in this._dataFieldNames) {

var dataFieldName = this._dataFieldNames[j];

var dataFieldValue = Sys.Preview.TypeDescriptor.getProperty(dataItem, dataFieldName, null); var typeName = Object.getTypeName(dataFieldValue);

if (typeName !== ‘String’ && typeName !== ‘Number’ && typeName !== ‘Boolean’) {

var convertToStringMethodName = Sys.Preview.TypeDescriptor.getAttribute(dataFieldValue, “convertToStringMethodName”);

if (convertToStringMethodName) dataFieldValue = Sys.Preview.TypeDescriptor.invokeMethod(dataFieldValue, convertToStringMethodName); }

sb.append(‘<td>’) sb.append(dataFieldValue);

sb.append(‘</td>’);

}

(continued)

Trang 10

Appendix D: Data Control

Listing D-22 shows a page that uses the CustomTable data control If you run this page, you’ll get the

result shown in Figure D-1

Listing D-22: A Page that Uses the CustomTable Data Control

<script type=”text/javascript” language=”javascript”>

function onSuccess(result, userContext, methodName)

Trang 11

Appendix D: Data Control

var properties = [];

properties[“dataFieldNames”] = [‘Title’, ‘AuthorName’, ‘Publisher’];

var customTable = $create(CustomComponents.CustomTable, properties, null, null, $get(“mydiv”));

MyWebService.GetBooks(onSuccess, onFailure, customTable);

} </script>

</head>

<body>

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

<asp:ScriptManager runat=”server” ID=”ScriptManager1”>

Trang 12

Appendix D: Data Control

1378

Listing D-22 retrieves the data from the Web service shown in Listing D-23 This code listing presents the

content of the WebService.asmx file that contains the implementation of our Web service As you can

see, this Web service exposes a method named GetBooks that retrieves the data from the underlying

database and populates an array of Book objects with them

Note that the underlying database is a database named BooksDB that contains two tables named Books

and Authors The following table describes the Books database table:

Figure D-1

Trang 13

Appendix D: Data Control

1379

The following table describes the Authors database table:

Column Name Data Type

BookID int

Title nvarchar(50)

Publisher nvarchar(50)

Price decimal(18, 0)

AuthorID int

Column Name Data Type

AuthorID int

<add connectionString=”Data Source=ServerName;Initial Catalog=BooksDB;

Integrated Security=SSPI” name=”MyConnectionString” />

private string title;

public string Title {

get { return this.title; } set { this.title = value; } }

(continued)

Trang 14

Appendix D: Data Control

1380

Listing D-23 (continued)

private string authorName;

public string AuthorName

{

get { return this.authorName; }

set { this.authorName = value; }

}

private string publisher;

public string Publisher

{

get { return this.publisher; }

set { this.publisher = value; }

}

private decimal price;

public decimal Price

{

get { return this.price; }

set { this.price = value; }

string connectionString = settings.ConnectionString;

string commandText = “Select Title, AuthorName, Publisher, Price “ +

“From Books Inner Join Authors “ +

“On Books.AuthorID = Authors.AuthorID “;

DataTable dt = new DataTable();

SqlDataAdapter ad = new SqlDataAdapter(commandText, connectionString);

ad.Fill(dt);

Book[] books = new Book[dt.Rows.Count];

for (int i=0; i<dt.Rows.Count; i++)

Trang 15

Templated Controls

Appendix D implemented a client control named CustomTable that uses predetermined HTML content to render its user interface to display the specified data records This client control is an example of one that hard-codes its HTML content A templated client control is a control that enables page developers to customize the HTML content that makes up its user interface In other words, a templated client control does not hard-code its HTML Every ASP.NET AJAX templated client control exposes a property of type ITemplate As Listing E-1 shows, the ITemplate inter-face exposes the methods discussed in the following table:

Method Description

createInstance Every subclass of the ITemplate interface must implement

this method The subclass must contain the appropriate logic to create the DOM subtree that the template represents and to attach this subtree to the document object

initialize Every subclass of the ITemplate interface must implement

this method to initialize itself

disposeInstance A static method that must be used as is This method simply

disposes the current MarkupContext Recall that the current

MarkupContext maintains two important pieces of tion: the DOM subtree that the template represents and its associated ASP.NET AJAX components

informa-(continued)

Listing E-1: The ITemplate Interface

Sys.Preview.UI.ITemplate = function Sys$Preview$UI$ITemplate(){

throw Error.notImplemented();

}function Sys$Preview$UI$ITemplate$createInstance(){

throw Error.notImplemented();

}

Trang 16

Appendix E: Templated Controls

instanceElement References the root DOM element of the subtree of DOM elements

represented by the template and its associated MarkupContext

callbackResult Normally references a DOM element with a specified id HTML

attribute value

A subclass of the ITemplate interface normally instantiates and initializes an instance of the

TemplateInstance class inside the createInstance method, and returns this instance as

the return value of the createInstance method

Listing E-2: The TemplateInstance Type

Sys.Preview.UI.TemplateInstance = function Sys$Preview$UI$TemplateInstance()

{

this.instanceElement = null;

this.callbackResult = null;

}

Trang 17

Appendix E: Templated Controls

1383

Template

The ASP.NET AJAX client-side framework comes with an implementation of the ITemplate interface named Template , as shown in Listing E-3 , which is used in ASP.NET AJAX templated controls such as

ListView I’ll discuss the members of this class in the following sections

Listing E-3: The Template Type

Sys.Preview.UI.Template = function Sys$Preview$UI$Template(layoutElement, scriptNode, parentMarkupContext){

createInstance: Sys$Preview$UI$Template$createInstance, dispose: Sys$Preview$UI$Template$dispose,

initialize: Sys$Preview$UI$Template$initialize}

Sys.Preview.UI.Template.registerClass(‘Sys.Preview.UI.Template’, null, Sys.Preview.UI.ITemplate, Sys.IDisposable);

Constructor

The constructor of the Template class takes three parameters, as shown in the following table:

Parameter Description

layoutElement References the DOM element, such as a <div> HTML element,

that represents the template on the current page Every ASP.NET AJAX template must be associated with an HTML element This HTML element is known as a layout element

scriptNode References the xml-script <template> element that represents

the template in the xml-script

MarkupContext is normally the global MarkupContext Recall that the global MarkupContext represents the current

document object

Trang 18

Appendix E: Templated Controls

1384

parseFromMarkup

Every ASP.NET AJAX component either exposes a parseFromMarkup static method or inherits this static

method from its parent component through the process discussed in Appendix A When the xml-script

parser is parsing an xml-script node that represents an ASP.NET AJAX client class of type Component , it

first accesses a reference to the type and then invokes the parseFromMarkup method of the type,

pass-ing in three parameters to have the type parse the child xml-script nodes of the xml-script node that

represents the type The following table presents these three parameters:

Parameter Description

node being parsed, which is the <template> xml-script node in this case

markupContext References the current MarkupContext (Recall that the current

MarkupContext maintains two important pieces of information:

a DOM subtree and its associated ASP.NET AJAX components.)

As you can see from Listing E-4 , the parseFromMarkup static method of the Template class first

calls the getNamedItem method on the attributes collection property of the node that references the

<template> xml-script element to return a reference to the attribute node named layoutElement :

var layoutElementAttribute = node.attributes.getNamedItem(‘layoutElement’);

Next, it calls the nodeValue property on the attribute node to return the value of the attribute node This

value is a string that contains the value of the id HTML attribute of the HTML element, such as a <div>

element, that represents the template:

var layoutElementID = layoutElementAttribute.nodeValue;

Next, it calls the findElement instance method on the current MarkupContext to return a reference to

the HTML DOM element in the subtree of nodes represented by the current MarkupContext Keep in

mind that this subtree of nodes is not part of the document object As a result, you cannot call the

getElementById method on the document object to return a reference to this DOM element The

document object is part of the global MarkupContext , not the local MarkupContext , which is local to

the current template:

var layoutElement = markupContext.findElement(layoutElementID);

Finally, it instantiates a Template object, passing in three parameters The first parameter references the

HTML DOM element returned by the call into the findElement method This DOM element is the root

node of the subtree that the current MarkupContext represents The second parameter references the

xml-script <template> node that represents the template in the xml-script XML document The third

parameter references the current MarkupContext

Trang 19

Appendix E: Templated Controls

1385

Listing E-4: The parseFromMarkup Static Method of the Template Class

Sys.Preview.UI.Template.parseFromMarkup = function Sys$Preview$UI$Template$parseFromMarkup(type, node, markupContext){

var layoutElementAttribute = node.attributes.getNamedItem(‘layoutElement’);

var layoutElementID = layoutElementAttribute.nodeValue;

var layoutElement = markupContext.findElement(layoutElementID);

return new Sys.Preview.UI.Template(layoutElement, node, markupContext);

containerElement References the DOM element on the current page

that will contain the subtree of DOM elements erated by the call into the createInstance method The createInstance method basically parses the specified <template> node in xml-script to extract the information that it needs to generate this subtree of DOM nodes

dataContext References the current data context, which is

nor-mally the current data record in the underlying data record collection

instanceElementCreatedCallback References a JavaScript function or delegate that

will be invoked right after the parsing of the child nodes of the <template> node that represents the template in the xml-script document

callbackContext References contextual information that will be

passed into the JavaScript function or delegate

as is

As Listing E-5 shows, the Template class’s implementation of the createInstance method performs the following tasks First, it instantiates an instance of the TemplateInstance class:

var result = new Sys.Preview.UI.TemplateInstance();

Next, it invokes the cloneNode method on the DOM element that represents the template on the current page Note that the createInstance method passes true to the cloneNode method to instruct it to clone the descendants of this DOM element as well In other words, the return value of the cloneNode is

Trang 20

Appendix E: Templated Controls

1386

a subtree of DOM nodes, in which the root is the clone of the DOM element that represents the template

on the current page The createInstance method then stores this subtree in the instanceElement

property of the newly instantiated TemplateInstance object:

result.instanceElement = this._layoutElement.cloneNode(true);

Then, it invokes the createDocumentFragment method on the document object to create a new

docu-ment fragdocu-ment:

var documentFragment = document.createDocumentFragment();

Next, it appends the cloned sub tree of DOM nodes to this document fragment:

documentFragment.appendChild(result.instanceElement);

Then the createInstance method invokes the createLocalContext static method on the

MarkupContext class to create a new local MarkupContext to represent the preceding document

frag-ment Note that the createInstance method passes three parameters into the createLocalContext

method The first parameter references the new document fragment, which contains the preceding

cloned subtree The second parameter references the current MarkupContext , which is normally the

global MarkupContext While the global MarkupContext represents the document object, the local

markup context represents this document fragment (Keep in mind that this document fragment, which

contains the cloned subtree, is not part of the document object In other words, you cannot invoke the

getElementById method on the document object to access the DOM elements in this closed subtree.)

The third parameter is the reference to the current data context The data context is normally the current

data record in the underlying data record collection:

var markupContext =

Sys.Preview.MarkupContext.createLocalContext(documentFragment,

this._parentMarkupContext, dataContext);

Next, the createInstance method invokes the open method on the newly created MarkupContext

object Recall that the open method simply instantiates the _pendingReferences collection of the

MarkupContext object

markupContext.open();

Then it invokes the parseNodes static method on the MarkupParser class to parse the child nodes of

the <template> node that represents the template in xml-script Note that the createInstance method

passes two parameters into the parseNodes method The first is an array that contains the references to

all the child nodes of the <template> node that represents the template in xml-script (These child

nodes are normally the ASP.NET AJAX components that the page developer declares between the

opening and closing of the template element.) The second parameter references the newly instantiated

local MarkupContext This is the MarkupContext that represents a cloned subtree of nodes:

Sys.Preview.MarkupParser.parseNodes(this._scriptNode.childNodes, markupContext);

The caller of the createInstance method can pass a reference to a JavaScript function or delegate that

represents a JavaScript function, and use this as the third parameter of the createInstance method

The createInstance method invokes this JavaScript function or delegate at this point and passes three

Trang 21

Appendix E: Templated Controls

1387

parameters into it The first parameter references the cloned subtree of nodes The second parameter references the newly created MarkupContext The third parameter references the JavaScript object that the caller of the createInstance method has passed into the method as its last parameter (if any) As you can see, the createInstance method doesn’t do anything with its last parameter It simply passes

it back into its caller through the JavaScript function, or the delegate that the caller passed into the

createInstance method as its third argument It is the responsibility of this JavaScript function or delegate to use the parameters passed into it to run the necessary custom code and return the result to the createInstance method The createInstance method simply stores the returned value of this JavaScript function or delegate in the callbackResult property of the TemplateInstance object The caller of the createInstance method can then access this return value via the callbackResult prop-erty of this object

if (instanceElementCreatedCallback) result.callbackResult = instanceElementCreatedCallback(result.instanceElement, markupContext, callbackContext);

Next, the createInstance method stores the newly created markupContext in the markupContext property of the instanceElement property of the TemplateInstance object

markupContext.close();

Finally, the createInstance method returns the TemplateInstance object to its caller:

return result;

Listing E-5: The createInstance Method

function Sys$Preview$UI$Template$createInstance(containerElement, dataContext, instanceElementCreatedCallback, callbackContext)

{ var result = new Sys.Preview.UI.TemplateInstance();

result.instanceElement = this._layoutElement.cloneNode(true);

var documentFragment = document.createDocumentFragment();

documentFragment.appendChild(result.instanceElement);

var markupContext = Sys.Preview.MarkupContext.createLocalContext(documentFragment, this._parentMarkupContext, dataContext); markupContext.open();

Sys.Preview.MarkupParser.parseNodes(this._scriptNode.childNodes, markupContext);

(continued)

Trang 22

Appendix E: Templated Controls

Developing a Custom Template

Listing E-6 presents the content of a JavaScript file named TemplateField.js that contains the

imple-mentation of a custom template named TemplateField (You’ll see an application of this custom

template later in this appendix.) As you can see, the TemplateField inherits from the Template class

and extends its functionality to add support for a new property named headerText :

CustomComponents.TemplateField.registerClass(“CustomComponents.TemplateField”,

Sys.Preview.UI.Template);

I’ll walk you through the implementation of the members of this template in the following sections

Listing E-6: The Content of the TemplateField.js JavaScript File that Contains the

Implementation of the TemplateField Custom Template

Trang 23

Appendix E: Templated Controls

1389

var layoutElementID = layoutElementAttribute.nodeValue;

var layoutElement = markupContext.findElement(layoutElementID);

Sys.Debug.assert(!!layoutElement, String.format(‘Could not find the HTML element with ID “{0}”

associated with the template’, layoutElementID));

var headerTextAttribute = node.attributes.getNamedItem(‘headerText’);

var headerText = headerTextAttribute.nodeValue;

return new CustomComponents.TemplateField(layoutElement, node, markupContext, headerText);

}if(typeof(Sys)!==’undefined’) Sys.Application.notifyScriptLoaded();

Constructor

As Listing E-7 shows, the constructor of the TemplateField custom template takes a fourth parameter,

in addition to the parameters that the Template constructor takes This fourth parameter is used to set the headerText property of this custom template:

this._headerText = headerText;

Note that the constructor of the TemplateField custom template passes its first three parameters to the constructor of the Template class:

CustomComponents.TemplateField.initializeBase(this, [layoutElement, scriptNode, parentMarkupContext]);

Listing E-7: The Constructor of the TemplateField Custom Template

CustomComponents.TemplateField = function CustomComponents$TemplateField(layoutElement, scriptNode,parentMarkupContext, headerText)

{ CustomComponents.TemplateField.initializeBase(this, [layoutElement, scriptNode, parentMarkupContext]); this._headerText = headerText;

}

headerText

The TemplateField custom template extends the functionality of the Template base class to add support for a read-only string property named headerText You can set this property only through the constructor of the TemplateField custom template Therefore, this custom template does not expose a setter method for setting this property Listing E-8 presents the implementation of the getter method for this property

Trang 24

Appendix E: Templated Controls

Every time you implement a custom template that derives from the Template class, you must also

implement a method for your custom template that meets the following criteria:

❑ It must be named parseFromMarkup

❑ It must be static — that is, it must be defined on your custom template, not its prototype

property

❑ This method must take the following three parameters:

❑ type : This parameter references the Type object that describes the ASP.NET AJAX type

that represents the DOM node referenced by the second parameter

❑ node : This parameter references the DOM node that represents your custom template in

xml-script

❑ markupContext : This parameter references the current MarkupContext , which is

normally the global MarkupContext

❑ This method must instantiate an instance of your custom template and return the instance to

its caller

Listing E-9 presents the TemplateField custom template’s implementation of the parseFromMarkup

method This method begins by invoking the getNamedItem method on the attributes collection

property of the DOM node that represents your custom template in xml-script, in order to return a

refer-ence to the DOM node that represents the layoutElement attribute on your custom template:

var layoutElementAttribute = node.attributes.getNamedItem(‘layoutElement’);

Next, it invokes the nodeValue property on the DOM node that represents the layoutElement

attribute, to access the value of this attribute:

var layoutElementID = layoutElementAttribute.nodeValue;

Then it invokes the findElement method on the current MarkupContext to return a reference to the

associated DOM element of the TemplateField custom template:

var layoutElement = markupContext.findElement(layoutElementID);

Note that parseFromMarkup raises an exception if the current page does not contain the specified

DOM element

Trang 25

Appendix E: Templated Controls

1391

Next, the parseFromMarkup method invokes the getNamedItem method on the attributes collection property of the DOM node that represents the TemplateField in xml-script, in order to return a refer-ence to the DOM node that represents the headerText attribute:

var headerTextAttribute = node.attributes.getNamedItem(‘headerText’);

Then it invokes the nodeValue property on the attribute node to access the value of this attribute:

var headerText = headerTextAttribute.nodeValue;

Next, it instantiates and returns an instance of the TemplateField , passing in the reference to the associated DOM element of the template, the reference to the DOM element that represents the template

in xml-script, the reference to the current MarkupContext , and the header text

return new CustomComponents.TemplateField(layoutElement, node, markupContext, headerText);

Listing E-9: The parseFromMarkup Method

CustomComponents.TemplateField.parseFromMarkup = function Sys$Preview$UI$Template$parseFromMarkup(type, node, markupContext){

var layoutElementAttribute = node.attributes.getNamedItem(‘layoutElement’);

Sys.Debug.assert(!!(layoutElementAttribute &&

layoutElementAttribute.nodeValue.length), ‘Missing layoutElement attribute on template definition’);

var layoutElementID = layoutElementAttribute.nodeValue;

var layoutElement = markupContext.findElement(layoutElementID);

Sys.Debug.assert(!!layoutElement, String.format(‘Could not find the HTML element with ID “{0}”

associated with the template’, layoutElementID));

var headerTextAttribute = node.attributes.getNamedItem(‘headerText’);

var headerText = headerTextAttribute.nodeValue;

return new CustomComponents.TemplateField(layoutElement, node, markupContext, headerText);

}

Developing a Custom Templated Data Control

Recall that we developed a custom data control named CustomTable in Appendix D The main problem with this data control is that its user interface is not customizable The great thing about templates is that they enable page developers to customize the HTML content of their associated client controls In this section, I’ll present and discuss the implementation of a new version of the CustomTable data control that enables page developers to declare instances of the TemplateField custom template in xml-script

to customize the HTML content of the CustomTable client control and the appearance of the control

Listing E-10 presents the content of a JavaScript file named CustomTable.js that contains the

Trang 26

Appendix E: Templated Controls

1392

implementation of the CustomTable templated data control This is a data control because it derives

from the DataControl base class:

CustomComponents.CustomTable.registerClass(“CustomComponents.CustomTable”,

Sys.Preview.UI.Data.DataControl);

I’ll discuss the members of this custom templated data control in the following sections

Listing E-10: The Content of the CustomTable.js JavaScript File that Contains the

Implementation of the CustomTable Templated Data Control

Trang 27

Appendix E: Templated Controls

1393

function CustomComponents$CustomTable$get_alternatingItemCssClass(){

return this._alternatingItemCssClass;

}function CustomComponents$CustomTable$set_alternatingItemCssClass(value){

this._alternatingItemCssClass = value;

}function CustomComponents$CustomTable$render(){

var isArray = true;

var dataSource = this.get_data();

if (Sys.Preview.Data.IData.isImplementedBy(dataSource)) isArray = false;

else if (!Array.isInstanceOfType(dataSource)) throw Error.createError(‘Unknown data source type!’);

var table = document.createElement(“table”);

if (this._cssClass) table.className = this._cssClass;

var length = isArray ? dataSource.length : dataSource.get_length();

headerRow = table.insertRow(index);

if (this._headerCssClass) headerRow.className = this._headerCssClass;

this._toggleCssClassHandler = Function.createDelegate(this, this._toggleCssClass);

for (var i=0; i<length; i++) {

dataItem = isArray? dataSource[i] : dataSource.getItem(i);

(continued)

Trang 28

Appendix E: Templated Controls

1394

Listing E-10 (continued)

if (this._fields)

{

dataRow = table.insertRow(index + i);

$addHandler(dataRow, “mouseover”, this._toggleCssClassHandler);

$addHandler(dataRow, “mouseout”, this._toggleCssClassHandler);

if ((i % 2 === 1) && (this._alternatingItemCssClass))

Trang 29

Appendix E: Templated Controls

1395

CustomComponents.CustomTable.descriptor ={

properties: [{name: “fields”, type: Array, readOnly: true}, {name: ‘cssClass’, type: String },

{name: ‘hoverCssClass’, type: String }, {name: ‘headerCssClass’, type: String }, {name: ‘itemCssClass’, type: String }, {name: ‘alternatingItemCssClass’, type: String } ]

}if(typeof(Sys)!==’undefined’) Sys.Application.notifyScriptLoaded();

fields

The CustomTable template data control exposes an array property named fields , as shown in Listing E-11 As you’ll see later, page developers declaratively add instances of the TemplateField template to this array in xml-script They do this in order to specify which data fields of each data record

of the data collection bound to the CustomTable must be displayed, and what header text must be used for each data column

Listing E-11: The Getter Method for the Fields Property

function CustomComponents$CustomTable$get_fields(){

cssClass Specifies the name of the CSS class for the containing

<table> element of the CustomTable control

headerCssClass Specifies the name of the CSS class for the header row

hoverCssClass Specifies the name of the CSS class for the data row

when the mouse hovers over the data row

itemCssClass Specifies the name of the CSS class for the

numbered data rows

alternatingItemCssClass Specifies the name of the CSS class for the

numbered data rows

render

Every data control that inherits from the DataControl base class must implement a method named

render Listing E-12 presents the CustomTable class’s implementation of this method As you can see,

Trang 30

Appendix E: Templated Controls

1396

it begins by calling the get_data method to return a reference to the data collection bound to the

CustomTable control The CustomTable control, like any other data control, inherits the get_data

method from the DataControl base class:

var dataSource = this.get_data();

Next, it raises an exception if the bound data collection is not a JavaScript array and does not implement

the IData interface:

if (Sys.Preview.Data.IData.isImplementedBy(dataSource))

isArray = false;

else if (!Array.isInstanceOfType(dataSource))

throw Error.createError(‘Unknown data source type!’);

Then it creates a table DOM element:

var table = document.createElement(“table”);

Next, it assigns the value of the cssClass style property to the className property of the table DOM

element:

if (this._cssClass)

table.className = this._cssClass;

Next, the CustomTable control determines the total number of data records in the data collection bound

to the CustomTable data control:

var length = isArray ? dataSource.length : dataSource.get_length();

Then it inserts a new row into the table DOM element and sets its className property to the value of

the headerCssClass property:

headerRow = table.insertRow(index);

if (this._headerCssClass)

headerRow.className = this._headerCssClass;

index++;

Next, it iterates through the template objects in the fields collection, inserts a cell into the newly added row,

and sets the inner text of this cell to the value of the headerText property of the enumerated template object:

for (var c in this._fields)

Trang 31

Appendix E: Templated Controls

1397

Next, the CustomTable control iterates through the data records in the data collection bound to the

CustomTable control and performs the following tasks for each enumerated data record

dataItem = isArray? dataSource[i] : dataSource.getItem(i);

First, it inserts a new row into the table DOM element and registers the _toggleCssClassHandler delegate as the event handler for the mouseover and mouseout events of the newly added row

dataRow = table.insertRow(index + i);

$addHandler(dataRow, “mouseover”, this._toggleCssClassHandler);

$addHandler(dataRow, “mouseout”, this._toggleCssClassHandler);

If the row is an even-numbered row, the CustomTable control assigns the value of the

itemCssClass property to the className property of the row Otherwise, it assigns the value of the alternatingItemCssClass property to the className property:

if ((i % 2 === 1) && (this._alternatingItemCssClass)) dataRow.className = this._alternatingItemCssClass;

else if (this._itemCssClass) dataRow.className = this._itemCssClass;

Next, it iterates through the template objects in the fields collection, inserts a new cell for each plate object, and calls the createInstance method of the template object to render the HTML enclosed within the template object into the newly added cell to display the current data record:

for (var c in this._fields) {

var isArray = true;

var dataSource = this.get_data();

if (Sys.Preview.Data.IData.isImplementedBy(dataSource)) isArray = false;

else if (!Array.isInstanceOfType(dataSource)) throw Error.createError(‘Unknown data source type!’);

(continued)

Trang 32

Appendix E: Templated Controls

1398

Listing E-12 (continued)

var table = document.createElement(“table”);

dataRow = table.insertRow(index + i);

$addHandler(dataRow, “mouseover”, this._toggleCssClassHandler);

$addHandler(dataRow, “mouseout”, this._toggleCssClassHandler);

if ((i % 2 === 1) && (this._alternatingItemCssClass))

Trang 33

Appendix E: Templated Controls

1399

_toggleCssClass

Recall from Listing E-12 that the render method registers the delegate that represents the

_toggleCssClass method as an event handler for the mouseout and mouseover events of the CustomTable control As Listing E-13 shows, the _toggleCssClass method invokes the

toggleCssClass static method on the DomElement to toggle the CSS class name

Listing E-13: The _toggleCssClass Method

function CustomComponents$CustomTable$_toggleCssClass(evt){

Listing E-14: The Descriptor Static Property

CustomComponents.CustomTable.descriptor ={

properties: [{name: “fields”, type: Array, readOnly: true}, {name: ‘cssClass’, type: String },

{name: ‘hoverCssClass’, type: String }, {name: ‘headerCssClass’, type: String }, {name: ‘itemCssClass’, type: String }, {name: ‘alternatingItemCssClass’, type: String } ]

}

Using the TemplateField and CustomTable Templated

Data Controls

Follow these steps to use the TemplateField and CustomTable templated data controls:

1 Create a new Ajax-enabled Web site in Visual Studio 2005

2 Add a new JavaScript file named TemplateField.js to the root directory of this application and add Listing E-6 to this file

3 Add a new JavaScript file named CustomTable.js to the root directory of this application and add Listing E-10 to this file

Trang 34

Appendix E: Templated Controls

1400

4 Add a new Web page named CustomTable.aspx to the root directory of this application and

add Listing E-15 to this file

5 Add a new Web service named WebService.asmx to the root directory of this application

and add Listing E-16 to this file

Keep in mind that this example uses the same BooksDB database discussed in Appendix D

Listing E-15: A Page that Uses the TemplateField and CustomTable Templated

<script type=”text/javascript” language=”javascript”>

function onSuccess(result, userContext, methodName)

{

userContext.set_data(result);

}

Trang 35

Appendix E: Templated Controls

var customTable = Sys.Application.findComponent(“customTable”);

MyWebService.GetBooks(onSuccess, onFailure, customTable);

} </script>

</head>

<body>

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

<asp:ScriptManager runat=”server” ID=”ScriptManager1”>

Trang 36

Appendix E: Templated Controls

Trang 37

Appendix E: Templated Controls

1403

Listing E-16: The MyWebService Web Service

<%@ WebService Language=”C#” Class=”MyWebService” %>

private string title;

public string Title {

get { return this.title; } set { this.title = value; } }

private string authorName;

public string AuthorName {

get { return this.authorName; } set { this.authorName = value; } }

private string publisher;

public string Publisher {

get { return this.publisher; } set { this.publisher = value; } }

private decimal price;

public decimal Price {

get { return this.price; } set { this.price = value; } }

}[WebService(Namespace = “http://tempuri.org/”)]

string connectionString = settings.ConnectionString;

string commandText = “Select Title, AuthorName, Publisher, Price “ + “From Books Inner Join Authors “ +

“On Books.AuthorID = Authors.AuthorID “;

(continued)

Trang 38

Appendix E: Templated Controls

1404

Listing E-16 (continued)

DataTable dt = new DataTable();

SqlDataAdapter ad = new SqlDataAdapter(commandText, connectionString);

ad.Fill(dt);

Book[] books = new Book[dt.Rows.Count];

for (int i=0; i<dt.Rows.Count; i++)

Using a custom ASP.NET AJAX class in xml-script requires you to define on the <page> element an

XML namespace prefix that maps to the ASP.NET AJAX namespace containing the custom ASP.NET

AJAX class In this case, Listing E-15 defines an XML namespace prefix named custom that maps to the

ASP.NET AJAX CustomComponents namespace, because this is the namespace that contains the

You must qualify with this XML namespace prefix the names of the XML elements that represent

instances of your custom client class in xml-script In this case, Listing E-15 qualifies the names of the

TemplateField and CustomTable XML elements with the prefix custom , as shown in the highlighted

portions of the following excerpt from Listing E-15 :

Trang 39

Appendix E: Templated Controls

Trang 40

Appendix E: Templated Controls

1406

Also note that each TemplateField contains a <label> element that represents a <span> element,

which is the subelement of the associated HTML element of the TemplateField For example, in the

preceding two excerpts, the <label> subelement of the <custom:TemplateField> represents the

<span> subelement of the <div> element associated with this TemplateField

Note that each <label> element contains a <bindings> subelement, which in turn contains a

<binding> subelement This <binding> subelement binds the text property of its associated <label>

element to the specified data field of the current data record of the data collection bound to the

CustomTable data control For example, in the case of the following excerpt from Listing E-15 , the

<binding> subelement binds the text property of the <label> element that has the id attribute value

of title to the Title data field of the current data record:

<custom:TemplateField layoutElement=“field1” headerText=“Title“>

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

TỪ KHÓA LIÊN QUAN