作者:Dflying Chen ( http://dflying.cnblogs.com/ )
了解了基本概念並完成了預先需求後(請參考:開發ASP.NET Atlas伺服器端Extender控制項-基本概念以及預先需求),我們可以開始開發這個ValidateUserNameExtender了。
首先,在Visual Studio中新建一個Atlas Control Project,我們將它取名為ValidateUserName。在新建以後,解決方案應該如下圖:
可以看到,Project Template中自動為我們引用瞭如下程序集:
Microsoft.Web.Atlas.dll,這是Atlas的核心程序集,將被下面的Microsoft.AtlasControlExtender.dll用到。
Microsoft.AtlasControlExtender.dll,這是Microsoft為我們提供的Atlas中Extender的基底類別所在的組件,我們自訂的Extender中的幾個必須Class都繼承於這個程式集中提供的基底類別。
同時,這個Project Template也為我們創建了一個JavaScript檔案以及三個C#檔案:
ValidateUserNameBehavior.js,這是我們的Extender中的核心部分,也是容納一切客戶端腳本的文件,其中的內容將基本上等同於使用ASP.NET Atlas開發即時驗證使用者名稱是否已註冊的自訂Behavior中的ValidateUserNameBehavior.js檔案內容,稍後會有詳細分析。 Atlas的Extender實際上就是對這個客戶端Behavior的封裝,使其成為伺服器端控件,以簡化網站程式開發人員在使用時的工作。而作為控制開發人員,卻增加了不少工作。
ValidateUserNameDesigner.cs,這裡用來書寫在Visual Studio中提供設計期間支援的程式碼。
ValidateUserNameExtender.cs,這裡用來定義我們的Extender。
ValidateUserNameProperties.cs,這裡用來定義我們的Extender中所用到的屬性,這些屬性的值將會被對應到客戶端Behavior的屬性上。
讓我們先從ValidateUserNameBehavior.js 這個JavaScript檔案開始,打開ValidateUserNameBehavior.js文件,我們會發現Template已經為我們添加了77行程式碼,並且在程式碼中有很多TODO,在程式碼的頭部read me部分還有3個step:
建立局部變數用來儲存屬性值。
將這些局部變數加入到Type Descriptor中,這一步是為了讓Atlas了解你的類別。
為局部變數加上訪問器(getter和setter)。
在開始step 1之前,我們先把Template中預設的命名空間改稱我們需要的,這裡我用的是Dflying.Atlas.ControlTookit.ValidateUserName,並更新所有在程式碼中出現的相關類別名稱。
然後對於step 1和step 3,我們放在一起做,參考使用ASP.NET Atlas開發實時驗證用戶名是否被註冊的自定義Behavior中的ValidateUserNameBehavior.js,應該加入CheckResultLabelID,ServiceName,MethodName,ValidMessage以及InvalidMessage五個屬性,這裡只舉出一個例子:
var _MethodName;
this.get_MethodName = function() {
return _MethodName;
}
this.set_MethodName = function(value) {
if (_MethodName != value) {
_MethodName = value;
this.raisePropertyChanged('MethodName');
}
}
然後是step 2: this.getDescriptor = function() {
var td = Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'getDescriptor');
td.addProperty('CheckResultLabelID', String);
td.addProperty('ServiceName', String);
td.addProperty('MethodName', String);
td.addProperty('ValidMessage', String);
td.addProperty('InvalidMessage', String);
return td;
}
最後是建構子與析夠函數,同樣出現在TODO:
this.initialize = function() {
Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'initialize');
_blurHandler = Function.createDelegate(this, blurHandler);
this.control.element.attachEvent('onblur', _blurHandler);
}
this.dispose = function() {
if (_blurHandler) {
this.control.element.detachEvent('onblur', _blurHandler);
_blurHandler = null;
}
Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'dispose');
}
再加上呼叫Web Service的Handler以及對應的CallBack:
this.initialize = function() {
Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'initialize');
_blurHandler = Function.createDelegate(this, blurHandler);
this.control.element.attachEvent('onblur', _blurHandler);
}
this.dispose = function() {
if (_blurHandler) {
this.control.element.detachEvent('onblur', _blurHandler);
_blurHandler = null;
}
Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'dispose');
}
還有一部分Template自動產生的Code,用來連接伺服器端與客戶端的狀態交互,在這個範例中我們不需要使用它,當然,留著也沒什麼壞處:
this.getClientState = function() {
var value = Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'get_ClientState');
if (value == '') value = null;
return value;
}
this.setClientState = function(value) {
return Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'set_ClientState',[value]);
}
最後要注意的是該JavaScript的最後一行:
Sys.TypeDescriptor.addType('dflying', 'ValidateUserNameBehavior', Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior);
其中dflying代表我們的Extender產生的客戶端控制項宣告的前綴,ValidateUserNameBehavior代表客戶端控制項的標記名稱,這兩個名字要取好並記牢,在接下來的CS檔案中也會用到。
這樣,完整的ValidateUserNameBehavior.js原始碼如下:
ValidateUserNameBehavior.js
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Permissive License.
// Seehttp://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx .
// All other rights reserved.
Type.registerNamespace('Dflying.Atlas.ControlTookit.ValidateUserName');
Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior = function() {
Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.initializeBase(this);
var _blurHandler;
var _CheckResultLabelID;
var _checkResultLabel;
var _ServiceName;
var _MethodName;
var _ValidMessage = "You can use this user name.";
var _InvalidMessage = "This user name has already been used, please choose another.";
this.get_CheckResultLabelID = function() {
return _CheckResultLabelID;
}
this.set_CheckResultLabelID = function(value) {
if (_CheckResultLabelID != value) {
_checkResultLabel = $(value);
debug.assert(_checkResultLabel != null, "CheckResultLabelID must be set to a valid DOM element.");
_CheckResultLabelID = value;
this.raisePropertyChanged('CheckResultLabelID');
}
}
this.get_ServiceName = function() {
return _ServiceName;
}
this.set_ServiceName = function(value) {
if (_ServiceName != value) {
_ServiceName = value;
this.raisePropertyChanged('ServiceName');
}
}
this.get_MethodName = function() {
return _MethodName;
}
this.set_MethodName = function(value) {
if (_MethodName != value) {
_MethodName = value;
this.raisePropertyChanged('MethodName');
}
}
this.get_ValidMessage = function() {
return _ValidMessage;
}
this.set_ValidMessage = function(value) {
if (_ValidMessage != value) {
_ValidMessage = value;
this.raisePropertyChanged('ValidMessage');
}
}
this.get_InvalidMessage = function() {
return _InvalidMessage;
}
this.set_InvalidMessage = function(value) {
if (_InvalidMessage != value) {
_InvalidMessage = value;
this.raisePropertyChanged('InvalidMessage');
}
}
this.initialize = function() {
Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'initialize');
_blurHandler = Function.createDelegate(this, blurHandler);
this.control.element.attachEvent('onblur', _blurHandler);
}
this.dispose = function() {
if (_blurHandler) {
this.control.element.detachEvent('onblur', _blurHandler);
_blurHandler = null;
}
Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'dispose');
}
this.getDescriptor = function() {
var td = Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'getDescriptor');
td.addProperty('CheckResultLabelID', String);
td.addProperty('ServiceName', String);
td.addProperty('MethodName', String);
td.addProperty('ValidMessage', String);
td.addProperty('InvalidMessage', String);
return td;
}
function blurHandler() {
if (this.control.element.value == '') {
_checkResultLabel.innerHTML = '';
return;
}
Sys.Net.ServiceMethod.invoke(
_ServiceName,
_MethodName,
'',
{ userNameCandidate : this.control.element.value},
_onMethodComplete
);
}
function _onMethodComplete(result)
{
_checkResultLabel.innerHTML = result ? _ValidMessage : _InvalidMessage;
}
this.getClientState = function() {
var value = Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'get_ClientState');
if (value == '') value = null;
return value;
}
this.setClientState = function(value) {
return Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.callBaseMethod(this, 'set_ClientState',[value]);
}
}
Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior.registerSealedClass('Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior', Microsoft.AtlasControlExtender.BehaviorBase);
Sys.TypeDescriptor.addType('dflying', 'ValidateUserNameBehavior', Dflying.Atlas.ControlTookit.ValidateUserName.ValidateUserNameBehavior);
值此為止,客戶端部分已經完成,接下來是伺服器端,也就是還剩下的那三個CS檔案的編寫。