_properties This property references a JavaScript object literal that contains one name/value pair for each property of the type or its base type that the TypeDescriptor object describ
Trang 1The ASP.NET Framework provides you with two ways to inspect the metadata associated with a given type: reflection and TypeDescriptor Metadata inspection plays a central role in the ASP.NET Framework For example, metadata inspection is an integral part of the ASP.NET server controls such as GridView , where data records come from many different data sources It is a well-known fact that different types of data stores expose different types of data records For example, data records stored into or retrieved from a relational database via the ADO.NET layer normally are of type DataRow or DataRowView Data records stored into or retrieved from an XML document via the NET XML layer are of type XmlNode
If an ASP.NET server control such as GridView were to know about the actual type of the data records being retrieved or stored, it would be tied to a particular type of data record, and, consequently, a particular type of data store For example, if an ASP.NET server control were to directly interact with the DataRow or DataRowView objects returned from the ADO.NET layer, it would not be able to interact with XmlNode objects returned from the NET XML layer In other words, the server control would only be able to retrieve data from and store data into a relational database via the ADO.NET layer and would not be able to retrieve data from and store data into
an XML document via the NET XML layer
The metadata inspection capabilities of the NET Framework allows a server control such as
GridView to interact with the data records in generic fashion without knowing their actual types
This allows the same server control to retrieve and store any type of data records
The ASP.NET AJAX client-side framework introduces two metadata inspection facilities that emulate their NET counterparts, reflection and TypeDescriptor Previous chapters covered the reflection capabilities of the ASP.NET AJAX client-side framework This chapter discusses the ASP.NET AJAX type description capabilities, which emulate the NET type description capabilities
As you’ll see later, the ASP.NET AJAX type descriptions provide the client controls with the same capabilities as their server counterparts These capabilities enable the client controls to deal with data records in a generic fashion without having to know their actual types
The ASP.NET AJAX type description infrastructure consists of the following main components:
❑ TypeDescriptor
❑ ICustomTypeDescriptor
Trang 2TypeDescriptor
The ASP.NET AJAX client-side framework includes a client class named TypeDescriptor that
emulates the ASP.NET server-side TypeDescriptor class The following sections discuss the members
of this client class
Constructor
Listing 10-1 presents the implementation of the TypeDescriptor client class’s constructor As the name
suggests, a TypeDescriptor object describes a type A type exposes up to three different kinds of
members: properties, methods, and events Every type can also be annotated with zero or more metadata
attributes that provide more information about the type
A type also inherits the properties, methods, events, and attributes of its base types Therefore, a
com-plete description of a type must include the type’s and its ancestor type’s properties, methods, events,
and attributes That is why the TypeDescriptor client class in Listing 10-1 exposes four properties
named properties , methods , events , and attributes
As the listing shows, the TypeDescriptor class also exposes four getter methods named _get
_ properties , _get_methods , _get_events , and _get_attributes that provide access to these four
properties The following sections discuss these properties
Listing 10-1: The Constructor of the TypeDescriptor Client Class
Sys.Preview.TypeDescriptor = function Sys$Preview$TypeDescriptor()
Trang 3_properties
This property references a JavaScript object literal that contains one name/value pair for each property
of the type (or its base type) that the TypeDescriptor object describes The name part of each name/
value contains the name of the property associated with the pair The value part of each name/value pair
is a JavaScript object literal that describes the property associated with the pair This JavaScript object literal contains up to five name/value pairs, where each pair provides a piece of metadata information about the property that the object literal describes, as follows:
❑ The first name/value pair specifies the name of the property The name part of this name/value pair is name , and the value part is a string that contains the name of the property
❑ The second name/value pair describes the type of the property The name part of this name/
value pair is type , and the value part references the constructor of the property type
❑ The third name/value pair specifies whether the property is read-only The name part of this name/value pair is readOnly , and the value part is a Boolean value
❑ The fourth name/value pair describes the metadata attributes that annotate the type of the property The name part of this name/value pair is attributes , and the value part is an object that contains the attributes
❑ The fifth name/value pair specifies whether the property references a DOM element The name part of this name/value pair is isDomElement , and the value part is a Boolean value
For example, the Component base class exposes the properties shown in the following table
Based on this table, the _properties property of the TypeDescriptor object that describes the
Component base class references the JavaScript object literal shown in Listing 10-2
Listing 10-2: The JavaScript Object Literal Referenced by the _properties of the TypeDescriptor Object that Describes the Component Base Class
‘dataContext’: {name: ‘dataContext’, type: Object, readOnly: false}, ‘id’: {name: ‘id’, type: String, readOnly: false},
‘isInitialized’: {name: ‘isInitialized’, type: Boolean, readOnly: true}, ‘isUpdating’: {name: ‘isUpdating’, type: Boolean, readOnly: true}
Trang 4property of the Component base class:
❑ {name: ‘dataContext’, type: Object, readOnly: false}
❑ {name: ‘id’, type: String, readOnly: false}
❑ {name: ‘isInitialized’, type: Boolean, readOnly: true}
❑ {name: ‘isUpdating’, type: Boolean, readOnly: true}
Now, let’s take a look at the content of the TypeDescriptor object’s _ properties property Because
the Control class derives from the Component base class, it inherits all the properties of its base class
The following table presents all the properties of the Control base class, including those that it inherits
from its base class
visibilityMode Sys.UI.VisibilityMode false
Based on this table, the _ properties property of the TypeDescriptor object that describes the
Control base class references the JavaScript object literal shown in Listing 10-3
Listing 10-3: The JavaScript Object Literal Referenced by the _properties of the
TypeDescriptor Object that Describes the Control Base Class
‘dataContext’: {name: ‘dataContext’, type: Object, readOnly: false},
‘id’: {name: ‘id’, type: String, readOnly: false},
‘isInitialized’: {name: ‘isInitialized’, type: Boolean, readOnly: true},
‘isUpdating’: {name: ‘isUpdating’, type: Boolean, readOnly: true},
‘element’: {name: ‘element’, type: Object, readOnly: true},
‘role’: {name: ‘role’, type: String, readOnly: true},
‘parent’: {name: ‘parent’, type: Object, readOnly: false},
‘visible’: {name: ‘visible’, type: Boolean},
‘visibilityMode’: {name:’visibilityMode’, type: Sys.UI.VisibilityMode,
readOnly:false}
}
Listing 10-4 shows a page that enables you to display the _properties property of the TypeDescriptor
object associated with any ASP.NET AJAX client class, including your own custom classes
Trang 5background-color: LightGoldenrodYellow;
color: black;
border-collapse: collapse;
} properties td, properties th {
border: 1px solid Tan;
padding: 5px;
} header { background-color: Tan; } odd { background-color: PaleGoldenrod; } </style>
<script type=”text/javascript” language=”javascript”>
function displayProperties(instance) {
var td = Sys.Preview.TypeDescriptor.getTypeDescriptor(instance);
var properties = td._getProperties();
var columns = [“Property Name”, “Property Type”, “ReadOnly”, “Property Attributes (Name/Value)”];
var table = document.createElement(“table”);
Sys.UI.DomElement.addCssClass(table, “properties”);
var headerRow = table.insertRow(0);
Sys.UI.DomElement.addCssClass(headerRow, “header”);
var headerCell = null;
for (var i=0, length = columns.length; i<length; i++) {
headerCell = document.createElement(“th”);
headerCell.appendChild(document.createTextNode(columns[i]));
headerRow.appendChild(headerCell);
} for (var property in properties) {
insertRow(table, properties[property]);
}
(continued)
Trang 6var rowIndex = table.rows.length;
var row = table.insertRow(rowIndex);
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1”>
Trang 7function pageLoad(){
var instance = new Sys.UI.Control($get(“forControl”));
displayProperties(instance);
}
The displayProperties method has no knowledge of what the real type of the instance is All it knows
is that the parameter passed into it is an instance of an ASP.NET AJAX client class
The displayProperties function displays information about the properties of the instance passed into
it as its arguments Thanks to the TypeDescriptor class, the logic that the displayProperties uses to
Trang 8inspect the properties of an ASP.NET AJAX client class instance is type-agnostic, meaning it can be used
to inspect the properties of any ASP.NET AJAX client type This logic is highlighted in the following code:
var rowIndex = table.rows.length;
var row = table.insertRow(rowIndex);
Trang 9TypeDescriptor object that describes the type:
var td = Sys.Preview.TypeDescriptor.getTypeDescriptor(instance);
The displayProperties function then calls the _getProperties method on the TypeDescriptor object to return a reference to its _properties property As discussed previously, this property references a single object literal that contains one name/value pair for each property of the type that the
TypeDescriptor object describes:
var properties = td._getProperties();
The displayProperties function then iterates through the name/value pairs of this object literal:
for (var property in properties)
The function invokes the following method for each enumerated name/value pair:
of the property, which is then passed into the insertCell method to display it within the opening and closing tags of a <td> HTML element:
insertCell(row, property[“name”]);
The second name/value pair of this object literal specifies the type of the property that the object literal describes As discussed earlier, the name part of this name/value pair contains type , and the value part references the constructor of the type of the property The insertRow method uses type as an index into this object literal to access the reference to this constructor, which is then passed into the insertCell method to display it within the opening and closing tags of a <td> HTML element:
insertCell(row, property[“type”]);
The property[“type“] returns a reference to the actual constructor of the property type, which means that you can directly call the new operator on this reference to create a new instance of the property type
Trang 10As discussed earlier, the third name/value pair of this object literal specifies whether the property that
the object literal describes is read-only The name part of this name/value pair is a string that contains
readOnly , and the value part is a Boolean value The insertRow method uses the string readOnly as
an index into the object literal to access this Boolean value, which is then passed into the insertCell
method to display it within the opening and closing tags of a td HTML element:
insertCell(row, property[“readOnly”]);
The fourth name/value pair of this object literal describes the attributes that annotate the type of the
property that the object literal describes As discussed previously, the name part of this name/value pair
contains attributes , and the value part references an object that contains the attributes The
insertRow uses attributes as an index into the object literal to access this value, iterates through
the attributes that this value contains, and displays the value of each attribute within the opening and
closing tags of a <td> HTML element:
var attributesText = ”No attributes are defined!”;
This property references an object literal that contains one name/value pair for each method of the type
(or base type) that the TypeDescriptor object describes The name part of each name/value is a string
that contains the name of the associated method The value part of each name/value pair is an object
literal that describes the associated method This object literal contains two name/value pairs, and each
pair provides a piece of metadata information about the method that the object literal describes, as
follows:
❑ The first name/value pair specifies the name of the method The name part of this name/value
pair is name , and the value part is a string that contains the name of the method
❑ The second name/value pair describes the parameters of the method The name part of this
name/value pair is parameters , and the value part is an array of object literals Each object
literal in the array describes a parameter of the method and contains two name/value pairs, as
follows:
❑ The first name/value pair specifies the name of the parameter The name part of this
name/value pair is name , and the value part is a string that contains the name of the parameter
❑ The second name/value pair describes the type of the parameter The name part of this
name/value pair is type , and the value part references the constructor of the type of the property
Trang 11For example, the Control base class exposes the methods shown in the following table
Method Name Parameter Name Parameter Type
removeCssClass className StringtoggleCssClass className String
Based on this table, the _methods property of the TypeDescriptor object that describes the Control base class references the object literal shown in Listing 10-5
Listing 10-5: The Object Literal Referenced by the _methods Property of the TypeDescriptor Object that Describes Control Base Class References
‘addCssClass’: {name: ‘addCssClass’, parameters: [{name: ‘className’, type: String]}, ‘removeCssClass’: {name: ‘removeCssClass’,
parameters: [{name: ‘className’, type: String]}, ‘toggleCssClass’: {name: ‘toggleCssClass’,
parameters: [{name: ‘className’, type: String]}
}
This object literal contains three name/value pairs The name part of each name/value pair contains the name of a method: ‘ addCssClass’ , ‘removeCssClass’ , and ‘toggleCssClass’ The value part of each name/value pair contains the object literal that describes the corresponding method of the Control base class, as follows:
❑ {name: ‘addCssClass’, parameters: [{name: ‘className’, type: String]}
❑ {name: ‘removeCssClass’, parameters: [{name: ‘className’, type: String]}
❑ {name: ‘toggleCssClass’, parameters: [{name: ‘className’, type: String]}
Listing 10-6 shows a page that enables you to display _methods property of the TypeDescriptor object associated with any ASP.NET AJAX client class, including your own custom classes Figure 10-2 shows what you’ll see in your browser when you access this page
Listing 10-6: A Page that Displays the _methods Property of the TypeDescriptor Object Associated with an ASP NET AJAX Client Class
Trang 12header { background-color: Tan; }
odd { background-color: PaleGoldenrod; }
var methods = td._getMethods();
var columns = [“Method Name”, “Parameter (Name/Type)”];
var table = document.createElement(“table”);
Sys.UI.DomElement.addCssClass(table, “properties”);
var headerRow = table.insertRow(0);
Sys.UI.DomElement.addCssClass(headerRow, “header”);
var headerCell = null;
for (var i=0, length = columns.length; i<length; i++)
Trang 13Listing 10-6 (continued)
function insertRow(table, method) {
var rowIndex = table.rows.length;
var row = table.insertRow(rowIndex);
if (rowIndex % 2 == 1) Sys.UI.DomElement.addCssClass(row, “odd”);
insertCell(row, method[“name”]);
var parametersText = ”No parameters are defined!”;
if (method[“parameters”]) {
var parameters = method[“parameters”];
var paramBuffer = [];
for(var parameter in parameters) {
paramBuffer.push(String.format(“({0} / {1})”, parameters[parameter].name, parameters[parameter].type.getName()));
} parametersText = paramBuffer.join();
} insertCell(row, parametersText);
} function insertCell(row, value) {
var cell = row.insertCell(row.cells.length);
cell.appendChild(document.createTextNode(value));
} function pageLoad() {
var instance = new Sys.UI.Control($get(“forControl”));
displayMethods(instance);
} </script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1”>
Trang 14As you can see in Listing 10-6 , the pageLoad method instantiates an instance of the Control base class
and invokes the displayMethods JavaScript function, passing in the instance as its argument:
The displayMethods function has no knowledge of what the real type of the instance is All it knows is
that the parameter passed into it is an instance of an ASP.NET AJAX client class
The displayMethods function displays information about the methods of the instance passed into it as
its arguments Thanks to the TypeDescriptor class, the logic that displayMethods uses to inspect the
methods of an ASP.NET AJAX client class instance is type-agnostic, which means it can be used to
inspect the methods of any ASP.NET AJAX client type This logic is highlighted in the following code:
Trang 15(continued)
function insertRow(table, method) {
var rowIndex = table.rows.length;
var row = table.insertRow(rowIndex);
if (rowIndex % 2 == 1) Sys.UI.DomElement.addCssClass(row, “odd”);
insertCell(row, method[“name”]);
var parametersText = ”No parameters are defined!”;
if (method[“parameters”]) {
var parameters = method[“parameters”];
var paramBuffer = [];
for(var parameter in parameters) {
paramBuffer.push(String.format(“({0} / {1})”, parameters[parameter].name, parameters[parameter].type.getName()));
} parametersText = paramBuffer.join();
} insertCell(row, parametersText);
var methods = td._ getMethods();
The displayMethods function then iterates through the name/value pairs of this object literal:
for (var m in methods)
As discussed earlier, the name part of the enumerated name/value pair is a string that contains the name
of the associated method The displayMethods function uses this string as an index into the _methods property of the TypeDescriptor object to access the value part of the enumerated name/value pair The value part references the object literal that describes the method associated with the name/value pair
The displayMethods then passes this object literal into the insertRow method:
insertRow(table, methods[m]);
Trang 16This object literal contains two name/value pairs The first name/value pair specifies the name of the
method that the object literal describes The name part of this name/value pair contains name , and
the value part is a string that contains the name of the method insertRow uses name as an index into
this object literal to access the name of the method, which is then passed into the insertCell method to
display it within the opening and closing tags of a <td> HTML element:
insertCell(row, method[“name”]);
The second name/value pair of this object literal describes the parameters of the method that the object
literal describes The name part of this name/value pair contains parameters , and the value part
references an array that contains one object for each parameter of the associated method Each of these
objects in turn contains two name/value pairs that describe the name and type of the parameter
insertRow iterates through these objects and displays the name and type of each parameter:
for(var parameter in parameters)
This property references an object literal that contains one name/value pair for each event of the type
(or base type) that the TypeDescriptor object describes The name part of each name/value is a string
that contains the name of the event associated with the pair The value part of each name/value pair is
an object literal that describes the event associated with the pair This object literal contains a single
name/value pair that specifies the name of the event The name part of this name/value pair is name ,
and the value part is a string that contains the name of the event
For example, the Control base class exposes a single event named propertyChanged Therefore, the
_events property of the TypeDescriptor object that describes the Control base class references
the object literal shown in Listing 10-7
Listing 10-7: The Object Literal Referenced by the _events Property of the
TypeDescriptor Object that Describes Control Base Class References
‘propertyChanged’: {name: ‘propertyChanged’}
}
This object literal contains a single name/value pair The name part of this name/value pair is a string
that contains the name of the event: ‘propertyChanged’ The value part of this name/value pair
con-tains the object literal that describes the event of the Control base class: {name: ‘propertyChanged’}
Now, let’s take a look at the _events property of the TypeDescriptor object that describes the Button
client control Because the Button control derives from the Control class, it inherits the propertyChanged
event from its base class The Button control also exposes an event of its own named click Listing 10-8
shows the object literal that the _events property of the TypeDescriptor object references
Trang 17Listing 10-9: A Page that Displays the _events Property of the Type D escriptor Object Associated with an ASP NET AJAX Client Class
background-color: LightGoldenrodYellow;
color: black;
border-collapse: collapse;
} properties td, properties th {
border: 1px solid Tan;
padding: 5px;
} header { background-color: Tan; } odd { background-color: PaleGoldenrod; } </style>
<script type=”text/javascript” language=”javascript”>
function displayEvents(instance) {
var td = Sys.Preview.TypeDescriptor.getTypeDescriptor(instance);
var events = td._getEvents();
var columns = [“Event Name”];
var table = document.createElement(“table”);
Sys.UI.DomElement.addCssClass(table, “properties”);
var headerRow = table.insertRow(0);
Sys.UI.DomElement.addCssClass(headerRow, “header”);
(continued)
Trang 18Listing 10-9 (continued)
var headerCell = null;
for (var i=0, length = columns.length; i<length; i++)
var rowIndex = table.rows.length;
var row = table.insertRow(rowIndex);
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1”>
Trang 19function pageLoad(){
var instance = new Sys.UI.Control($get(“forControl”));
Trang 20var rowIndex = table.rows.length;
var row = table.insertRow(rowIndex);
if (rowIndex % 2 == 1)
Sys.UI.DomElement.addCssClass(row, “odd”);
insertCell(row, event[“name”]);
}
Here’s how this logic works The displayEvents function first calls the getTypeDescriptor static
method on the TypeDescriptor class, passing in the instance to access the TypeDescriptor object that
describes the type of the instance in a generic fashion:
var td = Sys.Preview.TypeDescriptor.getTypeDescriptor(instance);
The displayEvents function then calls the _getEvents method on the TypeDescriptor object to
return a reference to its _events property This property references a single object literal that contains
one name/value pair for each event of the type that the TypeDescriptor object describes:
var properties = td._getEvents();
The displayEvents function then iterates through the name/value pairs of this object literal:
for (var e in events)
The name part of the enumerated name/value pair is a string that contains the name of the associated
event The displayEvents function uses this string as an index into the _events property of the
TypeDescriptor object to access the value part of the enumerated name/value pair The value part
references the object literal that describes the event associated with the name/value pair The
displayEvents function then passes this object literal into the insertRow method:
insertRow(table, events[e]);
Trang 21This object literal contains a single name/value pair The name part of the pair is name , and the value part is a string that contains the name of the event The insertRow function invokes the insertCell method to display this value:
insertCell(row, event[“name”]);
get TypeDescriptor
The previous sections provided several examples of how you can use the getTypeDescriptor method
in your own applications This section walks you through the internal implementation of this method to introduce its extensibility points, setting the stage for later discussions of why, when, and how to extend the getTypeDescriptor method
The getTypeDescriptor method takes an instance of a type as its argument and returns a
TypeDescriptor object that describes the type, as shown in Listing 10-10 Listing 10-10 : The get T ype D escriptor Static Method of the Type D escriptor Class
Sys.Preview.TypeDescriptor.getTypeDescriptor =function Sys$Preview$TypeDescriptor$getTypeDescriptor(instance){
var type = Object.getType(instance);
var td = type._descriptor;
if (!td && !type._descriptorChecked) {
}
The getTypeDescriptor method first calls the getType static method on the Object class, passing in the type instance to return a reference to the type itself:
var type = Object.getType(instance);
Next, it checks whether the type instance implements an interface named ITypeDescriptorProvider
If so, it delegates the responsibility of creating and initializing the TypeDescriptor object that sents the type to the getDescriptor method of the type itself If not, it calls the generateDescriptor static method on the TypeDescriptor class to create and initialize a TypeDescriptor object that describes the type In either case, the TypeDescriptor object is cached in an internal field named
repre-_descriptor for future access Subsequent calls to the getTypeDescriptor method will be serviced from the cache to improve performance
You can extend the functionality of the TypeDescriptor class by having your type implement the
ITypeDescriptorProvider interface This interface exposes a single method named getDescriptor
Trang 22Your type’s implementation of this method must use whatever logic is necessary to create and initialize
an ICustomTypeDescriptor object and return the object to its caller
generate Descriptor
This section presents the internal implementation of the TypeDescriptor class’s generateDescriptor
static method to help you understand the significant role that the descriptor property of a type plays
in enabling others to inspect its members, and why it is important to implement the descriptor
property of your ASP.NET AJAX client classes
Listing 10-11 contains the internal code for the generateDescriptor static method of the
TypeDescriptor class This method takes a reference to a type and returns a TypeDescriptor object
that describes the type
Listing 10-11 : The generate Descriptor Static Method of the TypeDescriptor Class
Then, starting with the type itself, the generateDescriptor method marches upward through the
ancestor types, calling the append static method on the TypeDescriptor class to append each type’s
descriptor property to the newly instantiated TypeDescriptor object
The default implementation of the getTypeDescriptor method (the method that calls the
generateDescriptor method) assumes that your ASP.NET AJAX type and its ancestor ASP.NET AJAX
types expose metadata information about their properties, methods, events, and attributes through a
static property named descriptor If your ASP.NET AJAX client type does not implement the
descriptor property, the clients of your type will not be able to use the TypeDescriptor class to
inspect its members If one of the ancestor ASP.NET AJAX client types of your ASP.NET AJAX client type
does not implement the descriptor property, the clients of your type will not be able to inspect the
members that your type inherits from that ancestor type
Trang 23❑ How to implement the descriptor property of your own ASP.NET AJAX client types
❑ The four different kinds of metadata information that the descriptor static property of your ASP.NET AJAX class can expose to its clients
A descriptor static property of a type references an object literal that contains up to five name/value pairs, as follows:
❑ The first name/value pair describes the properties of the type The name part of this name/
value pair contains properties , and the value part references an array of object literals Each object literal in this array describes a property of the type and contains up to four name/value pairs, as follows:
❑ The first name/value pair specifies the name of the property The name part of this name/value pair is name , and the value part is a string that contains the name of the property
❑ The second name/value pair specifies the type of the property The name part of this name/value pair is type , and the value part references the actual type of the property
❑ The third name/value pair specifies whether the property is read-only The name part of this name/value pair is the ‘readOnly’ string, and value part is a Boolean
❑ The fourth name/value pair specifies the attributes of the property
❑ The fifth name/value pair specifies whether the property references a DOM element The name part of this name/value pair is isDomElement , and the value part is a Boolean value
❑ The second name/value pair describes the methods of the type The name part of the pair is
methods , and the value part is an array of object literals Each object literal in this array describes a method of the type and contains two name/value pairs, as follows:
❑ The first name/value pair specifies the name of the method The name part of the pair is
name , and the value part is a string that contains the name of the method
❑ The second name/value pair describes the parameters of the method The name part of the pair is parameters , and the value part is an array of object literals Each object literal in this array describes a parameter of the method and contains two name/value pairs The first name/value pair specifies the name of the parameter where the name part of the pair
is name , and the value part is a string that contains the name of the parameter The second name/value pair specifies the type of the parameter where the name part of the pair is
type , and the value part references the actual type of the parameter
❑ The third name/value pair describes the events of the type The name part of the pair is events , and the value part is an array of object literals Each object literal in this array describes an event
Trang 24of the type and contains a single name/value pair where the name part of the pair is name , and
the value part is a string that contains the name of the event
❑ The fourth name/value pair describes the attributes of the type The name part of the pair is
attributes , and the value part is an array of object literals Each object literal in this array
describes an attribute and contains two name/value pairs, as follows:
❑ The first name/value pair specifies the name of the attribute The name part of the pair is
name , and the value part is a string that contains the name of the attribute
❑ The second name/value pair specifies the value of the attribute The name part of the pair
is value , and the value part is the actual value of the attribute
Listing 10-12 shows the append static method of the TypeDescriptor class
Listing 10-12: The append Static Method of the TypeDescriptor Class
var length = descriptor.properties.length;
for (var i = 0; i < length; i++)
{
var property = descriptor.properties[i];
var propertyName = property.name;
var associatedAttributes = property.attributes;
var readOnly = property.readOnly? property.readOnly : false;
var isDomElement = !!(property.isDomElement);
var isInteger = !!(property.isInteger);
Trang 25Listing 10-12 (continued)
if (descriptor.events) {
var length = descriptor.events.length;
for (var i = 0; i < length; i++) {
var eventName = descriptor.events[i].name
if (! td._getEvents()[eventName]) td.addEvent(eventName);
} }
if (descriptor.methods) {
var length = descriptor.methods.length;
for (var i = 0; i < length; i++) {
var methodName = descriptor.methods[i].name;
if (! td._getMethods()[methodName]) {
var params = descriptor.methods[i].params;
if(!params) params = descriptor.methods[i].parameters;
if (params) td.addMethod(methodName, params);
else td.addMethod(methodName);
} } }
if (descriptor.attributes) {
var length = descriptor.attributes.length;
for (var i = 0; i < length; i++) {
var attributeName = descriptor.attributes[i].name
if (! td._getAttributes()[attributeName]) td.addAttribute(attributeName, descriptor.attributes[i].value);
} }}
The append static method takes two arguments The first argument references the TypeDescriptor object that describes a type, and the second argument references the descriptor property of another type The main goal of the append method is to copy the contents of the descriptor property of the latter type into the TypeDescriptor object that describes the former type
The descriptor property contains up to four name/value pairs The name parts of these four pairs are
properties , events , methods , and attributes The value parts of these four pairs are arrays of object literals, where each object literal describes a property, event, method, or attribute of the type That is why the append static method consists of four major sections — each section copies the contents of the associated array into the specified TypeDescriptor object
Trang 26After the completion of the call into the append method, the TypeDescriptor object contains the
contents of the descriptor property of the other type in addition to its original content As previously
discussed, the generateDescriptor method calls the append method to append the contents of the
descriptor properties of all the ancestor types of a given type into the TypeDescriptor object that
repre-sents the type Therefore, the _getProperties , _getMethods , _getEvents , and _getAttributes
methods of the TypeDescriptor object return all the properties, methods, events, and attributes of the
ancestor types of the type, in addition to the properties, methods, events, and attributes of the type itself
get Property
The TypeDescriptor class includes a static method named getProperty that takes up to three
parameters The first parameter references an instance of a type whose property value is being queried
The second parameter is a string that contains the name of a property whose value is being queried The
last parameter is optional The main responsibility of the getProperty method is to return the value of
the specified property of the specified instance of the type
This section walks you through the internal implementation of the getProperty method to help you
understand the following:
❑ The extensibility points of this method, which sets the stage for later discussions of why, when,
and how to extend the getProperty method of the TypeDescriptor class
❑ The role that the last argument of the getProperty method plays, and why and when you
should specify this argument
Listing 10-13 shows the getProperty static method
Listing 10-13: The get Property Static Method of the TypeDescriptor Class
var propertyInfo = td._getProperties()[propertyName];
var getter = instance[‘get_’ + propertyInfo.name];
var object = getter.call(instance);
The getProperty method first checks whether the instance implements the ICustomTypeDescriptor
interface If so, the method delegates the responsibility of retrieving the property value to the
getProperty method of the instance itself Note that the method simply returns the value returned
from the getProperty method of the instance:
Trang 27if (Sys.Preview.ICustomTypeDescriptor.isImplementedBy(instance)) return instance.getProperty(propertyName, key);
Therefore, you can customize the functionality of the getProperty method by having your type implement the ICustomTypeDescriptor interface As you’ll see later, your type’s implementation of the getProperty method of this interface must use whatever logic necessary to retrieve the value of the property with the specified name
If the instance does not implement the ICustomTypeDescriptor interface, the getProperty method takes the following actions:
❑ It calls the getTypeDescriptor static method on the TypeDescriptor class, passing in the instance
to return a reference to the TypeDescriptor object that describes the type of the instance in a generic fashion:
var td = Sys.Preview.TypeDescriptor.getTypeDescriptor(instance);
This is the first step you must take every time you need to use the ASP.NET AJAX type inspection capabilities (This was also the first step taken in Listings 10-4 , 10-6 , and 10-9 )
❑ It calls the _getProperties method on the TypeDescriptor object to return a reference to the
_properties array property of the object It uses the property name as an index into this array
to return the object literal that describes the property with the specified name:
var propertyInfo = td._getProperties()[propertyName];
As discussed earlier, this object literal contains up to four name/value pairs where each pair provides a piece of metadata information about the property The first name/value pair specifies the name of the property where the name part of the pair is name , and the value part is a string that contains the name of the property The getProperty method uses name to access the value part (the string containing the name of the property) and appends this string to the string
“get_” to arrive at the name of the getter method that gets the value of the property
❑ It uses the name of the getter method as an index into the instance to return a reference to the getter method itself:
var getter = instance[‘get_’ + propertyInfo.name];
❑ It invokes the call method on the getter method, passing in the instance to return the value of the property:
var object = getter.call(instance);
The third optional parameter of the getProperty method is named key The meaning of key depends
on the implementation of the getProperty method The default implementation (which is what’s shown in Listing 10-13 ) interprets the key as the name of a descendant subproperty of the specified property The descendant subproperties of a property include the subproperties of the property, the subproperties of the subproperties of the property, the subproperties of the subproperties of the subproperties of the property, and so on Therefore, if you call the getProperty method with the third argument, the method assumes that you’re asking for the value of a descendant subproperty whose name is given by the key parameter
Trang 28If the key does not contain the character dot ( ), the getProperty method assumes that you’re asking
for the value of the immediate subproperty, as shown in boldfaced portion of the following code
fragment:
object = key.indexOf(‘.’) === -1 ? (object[key]) :
(Sys.Preview.TypeDescriptor._evaluatePath(object, key));
Otherwise, the method calls the _evaluatePath method, where it iterates through the descendant
subproperties of the specified property to find the subproperty whose name is given by the key , as
var parts = path.split(‘.’);
var current = instance;
for(var i = 0; i < parts.length; i++)
The great thing about the getProperty method is that it enables you to access the value of the property
of an ASP.NET AJAX type instance without having to know the real type of the instance This enables
you to access the values of the properties of ASP.NET AJAX objects in type-agnostic fashion, which
means that you can write one set of JavaScript code to query the property values of any ASP.NET AJAX
object of any type
get Attribute
The TypeDescriptor class features a static method named getAttribute that takes two parameters The
first parameter references an instance of an ASP.NET AJAX type The second parameter is a string that
con-tains the name of an attribute of the type The main goal of the getAttribute method is to retrieve and
return the value of the specified attribute of a specified instance Listing 10-15 shows this method
Listing 10-15: The get Attribute Static Method of the TypeDescriptor Class
Trang 29The getAttribute method first invokes the getTypeDescriptor static method on the TypeDescriptor class, passing in the instance to return a reference to the TypeDescriptor object that describes the type of the instance in a generic fashion:
var td = Sys.Preview.TypeDescriptor.getTypeDescriptor(instance);
Next, it invokes the _getAttributes method on the TypeDescriptor object to return a reference to the object’s _attributes collection, and uses the attribute name as an index into this collection to return the attribute’s value:
return td._getAttributes()[attributeName];
set Proper ty
The setProperty method of the TypeDescriptor class takes up to four parameters The first parameter references an instance of an ASP.NET AJAX type The second parameter is a string that contains the name of a property of the type The third parameter contains the value of the property The fourth parameter is optional The main goal of the setProperty method is to set the specified property
of the specified instance to a specified value
This section walks you through the internal implementation of the setProperty method to help you understand the following:
❑ The extensibility points of this method
❑ The role the last argument of the setProperty method plays, and why and when you should specify this argument
Listing 10-16 shows this method
Listing 10-16: The set Property Static Method of the TypeDescriptor Class
Sys.Preview.TypeDescriptor.setProperty =function Sys$Preview$TypeDescriptor$setProperty(instance, propertyName, value, key){
if (Sys.Preview.ICustomTypeDescriptor.isImplementedBy(instance)) {
instance.setProperty(propertyName, value, key);
return;
} var td = Sys.Preview.TypeDescriptor.getTypeDescriptor(instance);
var propertyInfo = td._getProperties()[propertyName]; if (key) {
var getter = instance[‘get_’ + propertyInfo.name];
var object = getter.call(instance);
if(key.indexOf(‘.’) === -1) object[key] = value;
(continued)