ASP.NET AJAX组件开发一点通

随着越来越多的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 id="form1" runat="server">
<asp:ScriptManager runat="server" ID="ScriptManager01">
  <scripts>
    <asp:ScriptReference path="DemoTimer.js" />
  </scripts>
</asp:ScriptManager>
</form>

    <asp:ScriptManager>中的<scripts>节点中有一个<asp:ScriptReference>元素,<asp:ScriptReference>节点的path设置为DemoTimer.js从而定义了一个组件类。所有单独的脚本文件用ScriptManager控件注册时,脚本文件最后必须调用notifyScriptLoaded方法来通知应用程序脚本已被载入。

    通过调用Sys.Component.create方法或$create简写形式来实例化一个客户端组件,把参数传入$create方法来指定组件类型,你也可以传入一个包含ID值、属性默认值和事件邦定的JSON对象。调用$create方法实例化一个组件实例的脚本格式如下:


var app = Sys.Application;
app.add_init(applicationInitHandler);

function applicationInitHandler(sender, args)
{
  $create(Demo.Timer, {enabled:true,id:"demoTimer1", interval:2000},
    {tick:OnTick}, null);
}


四、开发一个用户组件和注册使用它
    在这里,我们开发一个客户端组件Demo.Timer并在页中使用它。
1.编写客户端组件Demo.Timer
    Demo.Timer是一个很简单的组件,它定义了一个tick事件和声明了enabled属性和interval属性,为interval属性唤醒了变化通知事件,页面开发者可以使用Demo.Timer组件处理tick事件,开发者也能帮定到属性变化事件,在interval属性每次被修改时处理一些活动。
    为Demo.Timer编写代码步骤:
      1.在ASP.NET AJAX应用程序根目录下创建文件DemoTimer.js
      2.在文件中增加下面的代码:


Type.registerNamespace("Demo");

Demo.Timer = function()
{
  Demo.Timer.initializeBase(this);

  this._interval = 1000;
  this._enabled = false;
  this._timer = null;
}

Demo.Timer.prototype = {
 

  get_interval: function()
  {
    return this._interval;
  },
  set_interval: function(value)
  {
    if (this._interval !== value)
    {
        this._interval = value;
        this.raisePropertyChanged('interval');

        if (!this.get_isUpdating() && (this._timer !== null))
        {
          this._restartTimer();
        }
    }
  },

  get_enabled: function()
  {
    return this._enabled;
  },
  set_enabled: function(value)
  {
    if (value !== this.get_enabled())
    {
        this._enabled = value;
        this.raisePropertyChanged('enabled');
        if (!this.get_isUpdating())
        {
          if (value)
          {
            this._startTimer();
          }
          else
          {
            this._stopTimer();
          }
        }
    }
  },

  //事件
  add_tick: function(handler)
  {
    /// 为tick事件增加一个事件句柄。
    this.get_events().addHandler("tick", handler);
  },
  remove_tick: function(handler)
  {
    /// 从tick事件移除一个事件句柄。
    this.get_events().removeHandler("tick", handler);
  },

  dispose: function()
  {
    this.set_enabled(false);
    this._stopTimer();
    // 调用基类的dispose()
    Demo.Timer.callBaseMethod(this, 'dispose');
  },

  updated: function()
  {
    Demo.Timer.callBaseMethod(this, 'updated');
    if (this._enabled)
    {
        this._restartTimer();
    }
  },

  _timerCallback: function()
  {
    var handler = this.get_events().getHandler("tick");
    if (handler)
    {
        handler(this, Sys.EventArgs.Empty);
    }
  },

  _restartTimer: function()
  {
    this._stopTimer();
    this._startTimer();
  },

  _startTimer: function()
  {
    this._timer = window.setInterval(Function.createDelegate(this, this._timerCallback), this._interval);
  },

  _stopTimer: function()
  {
    if(this._timer)
    {
        window.clearInterval(this._timer);
        this._timer = null;
    }
  }
}

// JSON对象描述了组件所有的属性、事件和方法
Demo.Timer.descriptor = {
  properties: [   {name: 'interval', type: Number},
            {name: 'enabled', type: Boolean} ],
  events: [ {name: 'tick'} ]
}

Demo.Timer.registerClass('Demo.Timer', Sys.Component);

//因为脚本没有通过System.Web.Handlers.ScriptResourceHandler载入
//所以在脚本文件最后调用Sys.Application.notifyScriptLoaded来通知ScriptManager
if (typeof(Sys) !== 'undefined')
Sys.Application.notifyScriptLoaded();


    在代码中调用了Type.registerNamespace方法注册了Demo名字空间,在构造器中声明和初始化了所有的私有字段,如例子中的interval。为了使组件基类的方法对该组件有效,构造器调用了从基类继承来的initializeBase方法,初始化时会依次注册Demo.Timer的实例使它在客户端应用中成为可释放的对象。
    在代码声明中,初始化了两个公共属性:interval和enabled,定义了私有字段并赋予了初始值,声明了每个属性的get和set访问器。在每个公共属性的set访问器方法中,代码通过调用raisePropertyChanged方法唤醒了propertyChanged事件,当每次属性改变时事件会通知页面开发者。
    add_tick和 remove_tick方法为了能够添加和移除监听tick事件的方法。通过对Sys.EventHandlerList集合的管理,这些方法将按次序添加和移除。EventHandlerList对象包含了一个组件的事件处理集合,它继承自Sys.Component.events,在例子中,代码调用了返回的 EventHandlerList对象的Sys.EventHandlerList.addHandler 和Sys.EventHandlerList.removeHandler 方法用于添加或移除特指的事件处理。
    Demo.Timer类重写了基类的dispose方法,在重写的dispose方法中修改了enabled属性使组件置为无效状态,enabled属性的set访问器唤醒了propertyChanged事件用于发送通知,代码调用了私有方法_stopTimer来停止被触发的tick事件。最后代码调用了基类的dispose方法通知应用程序释放组件。

2.在Web页面上使用Demo.Timer组件
    页面上的客户端组件实例能够通过一个用户服务器组件或使用客户端脚本来操纵。在这里我们用客户端脚本创建组件实例。
    在你放DemoTimer.js文件的Asp.Net AjAX应用程序目录下,创建一个DemoTimer.aspx 页面,把下面的代码放入页面文件中:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Demo.Timer组件演示</title>
</head>
<body>
  <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
          <Scripts>
            <asp:ScriptReference Path="DemoTimer.js"/>
          </Scripts>
        </asp:ScriptManager>

        Timer组件时钟频率数: <span id="result">0</span>
    </div>

    <script type="text/javascript">

            function OnTick(sender, args)
            {
          var result = $get("result");
          result.innerText = parseInt(result.innerText) + 1;
        }
           
            var app = Sys.Application;
        app.add_load(applicationLoadHandler);

        function applicationLoadHandler(sender, args)
        {
              // 创建Demo.Timer组件实例.
              // 为属性设置初始值和进行事件邦定.
              $create(Demo.Timer,
                {enabled:true,id:"demoTimer1",interval:2000},
                {tick:OnTick}, null, null);
            }

    </script>
  </form>
</body>
</html>

    页面的脚本代码中包含了两个函数:OnTick 和applicationLoadHandler。OnTick函数用于处理Demo.Timer组件的tick事件,用于修改<span>的HTML元素的计数值。applicationLoadHandler函数用于处理app_load事件。Demo.Timer组件通过脚本调用$create方法进行实例化,要传入如下参数:
    1.类型参数,就是你要创建的Demo.Timer类类型
    2.属性参数由JSON对象组成,包括组件ID值,通过名/值对来指定属性名和属性的初始化值,例子中把interval属性的初始值设置为2000毫秒,是为了timer组件每2秒发生一次tick事件。组件的enabled属性设置为true,为了使timer组件实例化后立刻开始监听事件。
    3.事件参数包含了一个对象,对象是由事件名和它对应的事件处理程序句柄组成,在这个例子中,onTick程序句柄赋给了tick事件,onTick是在页面脚本程序中定义的。
    你可以运行这个例子测试一下,这样有助于能更好的理解这种组件开发技术。
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值