[转]剖析ASP.NET AJAX的面向对象思想和ASP.NET AJAX的组件开发

剖析ASP.NET AJAX的面向对象思想

作者:Asp.Net 发布时间:20070306 文章来源: http://www.chinaaspx.com/dotnet/aspnet/20070306/2096.html

人们期待已久的ASP.NET AJAX v1.0正式版终于发布了。现在你能用Microsoft ASP.NET AJAX的javascript很容易的写出丰富的、交互式的web应用。尤其值得关注的是Microsoft AJAX Library增加了面向对象的支持,而以前javascript是不支持面向对象开发的。现在icrosoft AJAX Library能很好的支持类、名字空间、继承、接口、枚举、反射等特征。这些新增加的功能类似于.NET Framework,这使得开发ASP.NET AJAX应用变得容易维护,容易扩充。现在我们看看Microsoft AJAX Library是如何支持以上特征的。

1.类、成员和名字空间

在Microsoft AJAX Library中,所有的JavaScript类都继承自object(类似于.NET Framework库,都继承自object),在ASP.NET AJAX应用中你可以运用面向对象的编程模式创建继承自Microsoft AJAX基类的对象和组件,类有四种成员:字段、属性、方法、事件。字段和属性是名/值对,用于描述一个类的一个实例的特性的。字段是由简单类型构成且可直接访问,例如:

myClassInstance.name="Fred"。

属性可以是任何简单类型或引用类型,通过get和set方法访问。在ASP.NET AJAX中,get和set是独立的函数,并规定在函数名中使用前缀"get_" 或 "set_" ,例如要获取或设置cancel属性的值时,你可以调用get_cancel或set_cancel方法。

一个方法是完成一个活动的函数而不是返回一个属性的值。属性和方法在下面的例子里都有示范。

事件指示特指的动作发生。当一个事件发生时,它可以调用一个或多个函数。事件所有者可以完成等待事件发生的任何任务。

名字空间是对关联类的逻辑分组。名字空间使你可以对公共功能进行分组。

为了使ASP.NET Web页面具有ASP.NET AJAX功能,你必须添加<asp:ScriptManager>控件到页面上,当页面启动时,参照ASP.NET AJAX库的脚本自动产生。

下面的例子显示了页面使用了<asp:ScriptManager>控件。

<asp:ScriptManager runat="server" />

下面的例子演示了如何使用Type.registerNamespace和.registerClass方法来把Person类增加到Demo名字空间中、创建类然后注册类。

Type.registerNamespace("Demo");

Demo.Person = function(firstName, lastName, emailAddress) {

this._firstName = firstName;

this._lastName = lastName;

this._emailAddress = emailAddress;

}

Demo.Person.prototype = {

getFirstName: function() {

return this._firstName;

},

getLastName: function() {

return this._lastName;

},

getName: function() {

return this._firstName + ' ' + this._lastName;

},

dispose: function() {

alert('bye ' + this.getName());

}

}

Demo.Person.registerClass('Demo.Person', null, Sys.IDisposable);

在脚本文件Namespace.js中定义了类Person,制定了类的名字空间为"Demo"。运行页面Namespace.aspx,点击按钮将创建一个Demo.Person类的实例。

2.访问修饰

许多面向对象编程语言都有访问修饰的概念。允许你指定类或成员在某种范围内有效。例如可在外部执行的程序、具有相同名字空间的内部类或特指的代码快内的类等。在JavaScript中没有访问修饰,但在ASP.NET AJAX中约定以下划线字符开头"_"的被认为是私有的,类的外部不能访问。

3.继承

继承是一个类派生于另一个类的能力。派生类自动继承基类的所有字段、属性、方法和事件。派生类可以增加新的成员或者重写基类已存在的成员来改变成员的行为。

下面的脚本实例有两个类Person和Employee,Employee从Person继承而来,两个类示范了私有字段的使用,它们都有公共属性、方法。另外Employee类重写了Person类的toString实现,并调用了基类的功能。

Type.registerNamespace("Demo");

Demo.Person = function(firstName, lastName, emailAddress) {

this._firstName = firstName;

this._lastName = lastName;

this._emailAddress = emailAddress;

}

Demo.Person.prototype = {

getFirstName: function() {

return this._firstName;

},

getLastName: function() {

return this._lastName;

},

getEmailAddress: function() {

return this._emailAddress;

},

setEmailAddress: function(emailAddress) {

this._emailAddress = emailAddress;

},

getName: function() {

return this._firstName + ' ' + this._lastName;

},

dispose: function() {

alert('bye ' + this.getName());

},

sendMail: function() {

var emailAddress = this.getEmailAddress();

if (emailAddress.indexOf('@') < 0) {

emailAddress = emailAddress + '@example.com';

}

alert('Sending mail to ' + emailAddress + ' ...');

},

toString: function() {

return this.getName() + ' (' + this.getEmailAddress() + ')';

}

}

Demo.Person.registerClass('Demo.Person', null, Sys.IDisposable);

Demo.Employee = function(firstName, lastName, emailAddress, team, title) {

Demo.Employee.initializeBase(this, [firstName, lastName, emailAddress]);

this._team = team;

this._title = title;

}

Demo.Employee.prototype = {

getTeam: function() {

return this._team;

},

setTeam: function(team) {

this._team = team;

},

getTitle: function() {

return this._title;

},

setTitle: function(title) {

this._title = title;

},

toString: function() {

return Demo.Employee.callBaseMethod(this, 'toString') + '\r\n' + this.getTitle() + '\r\n' + this.getTeam();

}

}

Demo.Employee.registerClass('Demo.Employee', Demo.Person);

Inheritance.js脚本文件中定义了两个类:Person和Employee,Employee是从Person继承而来。每个类都有字段、公共属性和方法。另外,Employee类重写了toString的实现,并在重写的代码中调用了基类的功能。在这个例子中把类Person的名字空间设定为"Demo"。运行页面Inheritance.aspx,点击“创建对象”、“对象释放”、“公共和私有属性”、“对象方法”、“重写方法”,“对象类型检查”体验一下。

4.接口

接口是类要实现的逻辑协议,是对类进行集成的公共遵守的规范。它能使多个类和同一个接口把实现定义和类的具体实现结合起来。下面的例子定义了一个基类Tree和接口IFruitTree,Apple和Banana这两个类实现了接口IFruitTree,但Pine类没有实现接口IFruitTree。

Type.registerNamespace("Demo.Trees");

Demo.Trees.IFruitTree = function() {}

Demo.Trees.IFruitTree.Prototype = {

bearFruit: function(){}

}

Demo.Trees.IFruitTree.registerInterface('Demo.Trees.IFruitTree');

Demo.Trees.Tree = function(name) {

this._name = name;

}

Demo.Trees.Tree.prototype = {

returnName: function() {

return this._name;

},

toStringCustom: function() {

return this.returnName();

},

makeLeaves: function() {}

}

Demo.Trees.Tree.registerClass('Demo.Trees.Tree');

Demo.Trees.FruitTree = function(name, description) {

Demo.Trees.FruitTree.initializeBase(this, [name]);

this._description = description;

}

Demo.Trees.FruitTree.prototype.bearFruit = function() {

return this._description;

}

Demo.Trees.FruitTree.registerClass('Demo.Trees.FruitTree', Demo.Trees.Tree, Demo.Trees.IFruitTree);

Demo.Trees.Apple = function() {

Demo.Trees.Apple.initializeBase(this, ['Apple', 'red and crunchy']);

}

Demo.Trees.Apple.prototype = {

makeLeaves: function() {

alert('Medium-sized and desiduous');

},

toStringCustom: function() {

return 'FruitTree ' + Demo.Trees.Apple.callBaseMethod(this, 'toStringCustom');

}

}

Demo.Trees.Apple.registerClass('Demo.Trees.Apple', Demo.Trees.FruitTree);

Demo.Trees.GrannySmith = function() {

Demo.Trees.GrannySmith.initializeBase(this);

// You must set the _description feild after initializeBase

// or you will get the base value.

this._description = 'green and sour';

}

Demo.Trees.GrannySmith.prototype.toStringCustom = function() {

return Demo.Trees.GrannySmith.callBaseMethod(this, 'toStringCustom') + ' ... its GrannySmith!';

}

Demo.Trees.GrannySmith.registerClass('Demo.Trees.GrannySmith', Demo.Trees.Apple);

Demo.Trees.Banana = function(description) {

Demo.Trees.Banana.initializeBase(this, ['Banana', 'yellow and squishy']);

}

Demo.Trees.Banana.prototype.makeLeaves = function() {

alert('Big and green');

}

Demo.Trees.Banana.registerClass('Demo.Trees.Banana', Demo.Trees.FruitTree);

Demo.Trees.Pine = function() {

Demo.Trees.Pine.initializeBase(this, ['Pine']);

}

Demo.Trees.Pine.prototype.makeLeaves = function() {

alert('Needles in clusters');

}

Demo.Trees.Pine.registerClass('Demo.Trees.Pine', Demo.Trees.Tree);

Interface.js脚本文件中定义了一个Tree基类和一个IFruitTree接口。Apple和Banana两个继承类实现了IFruitTree接口,而Pine类没有实现IFruitTree接口。运行Interface.aspx,点击“对象创建”、“接口检查”、“调用接口方法”体验一下。

5.枚举

枚举是包含一组被命名的正整数常数的类。你可以像访问属性一样访问它的值。例如:

myObject.color = myColorEnum.red,枚举提供了一种很容易理解的整数表示。下面的例子定义了一个以十六进制数表示的颜色被命名为有意义的名字的枚举类型。

Type.registerNamespace("Demo");

// Define an enumeration type and register it.

Demo.Color = function(){};

Demo.Color.prototype =

{

Red: 0xFF0000,

Blue: 0x0000FF,

Green: 0x00FF00,

White: 0xFFFFFF

}

Demo.Color.registerEnum("Demo.Color");

运行Enumeration.aspx,选择下拉框中的颜色,脚本程序把背景色设为选中的枚举类型Demo.Color中的颜色。

6.反射

反射用于检查一个运行期程序的结构和组成,是通过类Type的API来实现反射的,这些方法使你能够收集一个对象的信息,例如:它是继承于哪个类、它是否实现类某个特指的接口、它是否是某个类的实例等。

下面的例子用反射的API测试GrannySmith类是否实现了前面的接口。运行Reflection.aspx,点击“检查类型”、“检查继承”、“检查接口”体验一下。

另外需要说明的一点是,Microsoft AJAX Library是基于开放体系架构而开发的,不仅仅用于asp.net,还能用于其它体系架构中,例如用在java中。

ASP.NET AJAX组件开发一点通


来源:CSDN 作者:whandsome 发布时间:2007-3-26 人气:5

随着越来越多的asp.net开发者使用Ajax技术,自己动手从Sys.Component基类继承开发一个ASP.NET AJAX 非可视客户端组件和如何使用它变得越来越重要。在本文中将讨论如何编写一个ASP.NET AJAX 非可视客户端组件并注册使用它。

一、概述

非可视组件都继承于组件,它们都没有界面表现,这是本文讨论的重点。另外ASP.NET AJAX还有两个新增的ASP.NET AJAX 客户端组件对象类型:Sys.UI.Behavior和Sys.UI.Control,它们扩充了基本组件的功能,开发行为类组件要继承于Sys.UI.Behavior,开发控件要继承于Sys.UI.Control,它们与非可视组件不同。虽然本文主要讨论非可视组件,但搞清除它们的内在区别很有必要,下面概括了组件、行为和控件的不同之处:

组件:没有界面表现,例如timer组件,它通过时间间隔触发事件但在界面上不显示;没有关联的DOM元素;封装了客户端代码以便于重用;从组件基类继承而来。

行为:扩展了DOM元素的行为,例如给文本框加水印效果;虽然不能修改和DOM元素相关联的基本行为,但可以创建界面元素;如果给ID赋值,通过它能够直接访问DOM元素的自定义属性(expando);不需要关联到另外的客户端对象,例如一个类从控件或行为类继承而来;在它们的element属性中能够参照一个控件或者一个不是控件的元素;是从扩展了组件基类的行为基类继承而来。

控件:把DOM 元素表现为一个客户端对象, 特别是能改变原始的DOM 元素的普通行为为它添加新的功能;直接通过控件expando访问DOM元素;继承自扩展了组件基类的控件基类。

用JavaScript代码封装一个ASP.NET AJAX 非可视客户端组件目的是为了能多次被重用,如timer组件就是一个较典型的非可视组件例子,它通过时间间隔来触发事件。通过继承组件基类,你的自定义组件自动继承了许多基类支持的特征。包括如下:

1.以交互的浏览模式来管理邦定到客户端对象事件的处理。

2.在客户端应用中注册的组件实现了Sys.IDisposable接口,是一个可释放的对象。

3.当属性被改变时触发通知事件。

4.完成组件属性设置的批处理过程,不仅减少了脚本代码量,而且在处理时间上比单独的get和set访问器有更高的效率。

5.对Sys.UI.Control.initialize方法的重写能初始化任何属性和事件监听器。

通过继承组件来开发一个客户端组件大致需要如下步骤:

1.用原型设计模式定义一个组件类。

2.初始化组件的基组件实例。

3.公布所有属性访问器,如果需要也可以激活propertyChanged的通知事件。

4.重写dispose方法用来释放资源,例如清除时间处理句柄。

二、用原型设计模式定义一个组件类

使用JavaScript的原型设计模式定义一个ASP.NET AJAX客户端类或者一个组件类要经过如下步骤:

1.为你的组件类注册名字空间。

2.创建组件的构造器函数,并在构造器函数里定义所有私有字段和设置他们的初始值。

3.使用原型设计模式定义组件的原形。

4.注册这个组件类并指定继承类。

下面的代码例子中定义了一个简单的客户端组件类,这个类虽然没有什么实用功能,但它展示了如何用原形定义一个从Component基类继承而来的类代码框架。

// 声明一个名字空间

Type.registerNamespace("Samples");

//定义一个简单的组件.

Samples.SimpleComponent = function()

{

Samples.SimpleComponent.initializeBase(this);

// 在构造器中初始化数组和对象

//使它们对于每个实例保持唯一.

//所有的字段在这里定义.

this._arrayField = [];

this._objectField = {};

this._aProp = 0;

}

//创建原型.

Samples.SimpleComponent.prototype =

{

// 为属性定义 set 和 get 访问器.

Set_Aprop: function(aNumber)

{

this._aProp = aNumber;

},

Get_Aprop: function()

{

return this._aProp;

},

//定义一个方法.

DoSomething: function()

{

alert('A component method was called.');

}

} //原型定义结束.

//声明要继承的基类.

Samples.SimpleComponent.inheritsFrom(Sys.Component);

// 注册这个类,以 Sys.Component为基类.

Samples.SimpleComponent.registerClass('Samples.SimpleComponent', Sys.Component);

从上面的代码例子可以得出,定义一个ASP.NET AJAX的客户端类或控件类大致要遵循如下几项规则:

1.如果要把类放到一个名字空间中,要调用Type.registerNamespace方法来注册这个名字空间。

2.通过声明构造器函数名来定义类的构造器函数和构造器函数的名字空间,在构造器中声明了所有的私有字段,构造器中的所有私有变量通过this指针被声明为实例字段。约定私有字段名前要加下划线前缀。

Samples.SimpleComponent = function()

{

Samples.SimpleComponent.initializeBase(this);

this._arrayField = [];

this._objectField = {};

this._aProp = 0;

}

3.定义类原型。在原型中,定义了所有的公共和私有的方法,包括属性访问器方法和事件。一般所有的字段都在构造器中定义,如果在原型中定义字段而不是在构造器中定义在性能上会稍微有一些提高,但并不是所有的字段都可以定义在原型中,例如Array 和 Object字段类型必须定义在构造器中,为了使它们在每个实例中保持唯一,就不能在原型中定义。

4.引用原型中的成员都是通过this指针,用this指针有较好的性能,因为它使用了很少的内存。

5.通过调用方法Type.registerClass来注册类。

三、创建自定义ASP.NET AJAX非可视客户端组件步骤

1.初始化基类

任何从基础组件类继承而来的组件类,在构造器函数中必须初始化它的基类。在组件的构造器函数中,你要调用继承来的Type.initializeBase方法,在构造器函数中必须在任何代码执行前执行初始化方法。initializeBase方法初始化了一个注册类的基础类型,使一个非可视化组件类具备了组件类的基本类型。当组件基类被初始化后,组件基类的方法对这个组件也都是有效的,注册的这个组件会自动成为ASP.NET AJAX应用程序可释放的对象。

下面代码展示了在从组件继承来的非可视化组件的构造器函数中调用了继承来的initializeBase方法:

Samples.SimpleComponent = function()

{

Samples.SimpleComponent.initializeBase(this);

}

2.定义属性和激活属性变化通知事件

你可能希望在你的组件类中定义的属性能够让页面开发者邦定到它上面,如果需要你也可以为你的组件属性激活propertyChanged notification事件,页面开发者能够邦定到这些事件上,从组件、控件、行为继承而来的组件继承了raisePropertyChanged方法,当触发属性改变事件时这个方法就会被调用。

3.初始化属性和事件监听器

如果你的自定义组件必须初始化所有属性和事件监听器,你可以在组件原型中重写Sys.UI.Control.initialize方法。例如一个从组件继承来的非可视化组件可以把委托赋值给一个事件如window.onFocus。一个从控件基类继承来的客户端控件能够把任何委托邦定到DOM元素事件和给DOM元素属性设置初始值,最后,你调用基类的初始化方法使组件基类完成初始化。

4.释放资源

如果在你的自定义组件释放以前必须释放一些资源,就需要重写dispose方法,并在重写的dispose方法中释放这些资源,以确保在组件释放以前这些资源被立即释放。释放的资源包括清除邦定到DOM事件的程序句柄,DOM元素和组件对象之间的相互引用也要移除,以确保对象从内存中能够移除掉。

5.在页面中使用非可视化组件

在ASP.NET AJAX应用程序页面上使用一个用户客户端组件,首先要在Web页面上注册组件脚本库。然后要创建一个组件实例。

用页面上ScriptManager控件通过声明方式或者用编程方式注册客户端控件需要的脚本。声明格式如下:

<form >http://blog.csdn.net/chaomawen/archive/2007/03/24/1539721.aspx