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是在頁面腳本程序中定義的。
    你可以運行這個例子測試一下,這樣有助於能更好的理解這種組件開發技術。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章