williambirkin

恭喜发财!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

这是我自己写的第一个基于Microsoft AJAX Library的Ajax控件。

 

这个是一个类似google输入框的AJAX控件。应该是很常见的,网上也有不少用别的AJAX类库实现的例子。
JS组件的代码是从《ASP.Net3.5控件和组件开发技术》提供的一个ajaxpro实现的示例的版本上改进过来的。
毕竟这也不是什么新功能了,能复用就复用吧:)。

 

solution

 

AutoFilterTextBoxACT.cs是控件的服务端组件代码,autoFilterTextBoxACT.js是客户端组件代码。
setTimeoutExtend.js是为了扩展setTimeout的js代码。
WSUrlEditor.cs是组件的属性用到的一个设计器。

1.服务器端代码
服务器端的基本代码比较简单,与普通的asp.net控件开发相比这里的变化主要是派生控件继承了新的控件类型ScriptControl。

ScriptControl 主要是用来在派生的控件的PreRender 阶段检查Page上是否包含ScriptManager控件。在Render时期使用ScriptManager为派生类注册脚本。
ScriptControl继承了WebControl和IScriptControl。IScriptControl接口使派生控件提供启用Ajax功能所必须的JavaScript资源。
IScriptControl.GetScriptDescriptors方法返回ScriptDescriptor对象集合。ScriptDescriptor用来提供创建客户端类的一个实例的脚本。
IScriptControl.GetScriptReferences方法返回用于定义控件需要的脚本资源的 ScriptReference 对象的集合。

实现代码如下:

        protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            //因为只有一个需要实例化的客户端组件,所以只需要添加一个ScriptControlDescriptor实例。
            ScriptControlDescriptor descriptor = new ScriptControlDescriptor("AutoFilterTextBox.AutoFilterTextBoxACT", this.ClientID);

            //添加实例客户端组件时需要初始化的实例的属性。
            descriptor.AddProperty("clientID", this.ClientID);
            descriptor.AddProperty("queryFieldName", this.ClientID + "_" + this.txt.ClientID);
            descriptor.AddProperty("dataSourceURI", this.Page.ResolveUrl(this.DataSourceURI));
            descriptor.AddProperty("getDataSucceededHandle", this.GetDataSucceededHandle);
            descriptor.AddProperty("preGetDataHandle", this.PreGetDataHandle);
            descriptor.AddProperty("serviceMethodName", this.ServiceMethodName);

            return new ScriptDescriptor[] { descriptor };    
        }

        protected override IEnumerable<ScriptReference> GetScriptReferences()
        {
            //使用ScriptManager获得资源中的js文件
            string autoFilterTextBoxACT =
                Page.ClientScript.GetWebResourceUrl(this.GetType(), "AutoFilterTextBox.autoFilterTextBoxACT.js");
            string setTimeoutExtend =
                Page.ClientScript.GetWebResourceUrl(this.GetType(), "AutoFilterTextBox.setTimeoutExtend.js");

            //提供所有用到的资源文件的引用。这些引最终用会被添加到Page.ClientScript中,并被输出到客户端。
            return new ScriptReference[] { new ScriptReference(autoFilterTextBoxACT), new ScriptReference(setTimeoutExtend) };
        }

 

ScriptDescriptor生成的初始化js组件的代码:
<script type="text/javascript"> //<![CDATA[ Sys.Application.initialize(); 
Sys.Application.add_init(function() { 
$create(AutoFilterTextBox.AutoFilterTextBoxACT, {"clientID":"AutoFilterTextBoxACT1","dataSourceURI":"/TestWebSite/Services/BookNameListService.asmx","getDataSucceededHandle":"OnClientGetDataSucceeded","preGetDataHandle":"OnPreGetDataHandle","queryFieldName":"AutoFilterTextBoxACT1_tb","serviceMethodName":"GetBookNameArray"}, null, null, 
$get("AutoFilterTextBoxACT1")); }); //]]> </script>

 

其他的部分请看源码。

 

2.客户端代码
与普通的js组件编程相比也没什么特别的。主要是下面五个步骤:
第一步:注册命名空间;

/// <summary>
/// 注册命名空间:AutoFilterTextBox
/// </summary>
Type.registerNamespace("AutoFilterTextBox");


第二部:定义类、构造函数和初始化代码;

/// <summary>
/// 客户端对象AutoFilterTextBox类实现
/// </summary>
//构造方法
AutoFilterTextBox.AutoFilterTextBoxACT = function(element) {
    AutoFilterTextBox.AutoFilterTextBoxACT.initializeBase(this, [element]); //初始化基类    

    this.DIV_BG_COLOR = "#c0c1f9";
    this.DIV_HIGHLIGHT_COLOR = "#7d84ef"
    this.DIV_FONT = "Arial";
    this.DIV_PADDING = "2px";
    this.DIV_BORDER = "1px solid #020086";
    this._clientID;
    this._queryField;
    this._queryFieldName;
    this._divName;
    this._ifName;
    this._lastVal = "";
    this._val = "";
    this._globalDiv;
    this._divFormatted = false;
    this._dataSourceURI = "";
    this._getDataSucceededHandle;
    this._preGetDataHandle;
    this._serviceMethodName = "";
}


第三步:编写类的属性和方法;

AutoFilterTextBox.AutoFilterTextBoxACT.prototype =
{
    ///方法
    initialize: function() {
        AutoFilterTextBox.AutoFilterTextBoxACT.callBaseMethod(this, "initialize");
        this.doInitialize();
        //注册初始化事件
    },
    doInitialize: function() {
        this._queryField = document.getElementById(this._queryFieldName);
        this._queryField.autocomplete = "off";

        if (this.hiddenDivName) {
            this._divName = this.hiddenDivName;
        }
        else {
            this._divName = this._clientID + "_querydiv";
        }

        this._ifName = this._clientID + "_queryiframe";

        this._lastVal = this._val = escape(this._queryField.value);
        //注册初始化事件
        $addHandler(this._queryField, "blur", Function.createDelegate(this, this.hideDiv));
        $addHandler(this._queryField, "keydown", Function.createDelegate(this, this.keypressHandler));

        setTimeout(this.mainLoop, 100, this, null);
    },
    Dispose: function() {
        AutoFilterTextBox.AutoFilterTextBoxACT.callBaseMethod(this, 'dispose');
    },
    get_queryFieldName: function() {
        return this._queryFieldName;
    },
    set_queryFieldName: function(value) {
        this._queryFieldName = value;
    },
    get_clientID: function() {
        return this._clientID;
    },
    set_clientID: function(value) {
        this._clientID = value;
    },
    get_dataSourceURI: function() {
        return this._dataSourceURI;
    },
    set_dataSourceURI: function(value) {
        this._dataSourceURI = value;
    },
    get_getDataSucceeded: function() {
        return this._getDataSucceeded;
    },
    set_getDataSucceeded: function(value) {
        this._getDataSucceeded = value;
    },
    …省略
}


第四步:注册类;

/// <summary>
/// 注册类, 并继承Sys.UI.Control
/// </summary>
AutoFilterTextBox.AutoFilterTextBoxACT.registerClass('AutoFilterTextBox.AutoFilterTextBoxACT', Sys.UI.Control);


第五步:通知脚本加载完成。

/// <summary>
/// 通知脚本加载完成
/// 说明: 若要向 ScriptManager 控件注册的所有独立脚本文件必须调用 notifyScriptLoaded 方法以通知应用程序,脚本已完成加载。
/// 嵌入到程序集中的脚本在大多数情况下不应调用此方法。有关更多信息,请参见 Sys.Application.notifyScriptLoaded 方法。
/// </summary>
if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
 


 

 

有几个地方需要注意:

(1)创建DOM对象的事件首先要使用Function.createDelegate方法,创建一个event delegate。需要注意一下Function.createDelegate的两个参数。第一个参数instance,表示事件中this引用的对象。第二个参数表示事件的handle。如果传入的instance是某个dom元素那么事件中的this将是该dom元素。为了方便的访问组件中的属性和方法,我将组件实例(this)作为instance参数。


(2)在创建完代理类之后使用$addHandler方法注册dom元素的事件。注册事件的时候,事件的名称不要带“on”。因为$addHandler方法会自动帮你加上。
例如
$addHandler(this._queryField, "onkeydown", Function.createDelegate(this, this.keypressHandler));
实际上注册的是ononkeydown事件。


(3)需要暴露给服务器端代码初始化的js组件的属性必须使用get_和set_进行封装。

 

3.组件的使用

aspx

 

几个关键属性

  1. ServiceMethodName表示要调用的WebService的方法
  2. DataSourceURI表示WebService的地址
  3. PreGetDataHandle表示调用WebService前为组件提供WebService所需参数的方法
  4. GetDataSucceededHandle表示成功调用WebService后客户端对返回数据处理的方法

这几个属性基本上都是为了使控件的使用更加灵活方便,使控件的使用者能参与Ajax过程中的相关处理,提供一定的扩展性。
到此为止此控件中与ASP.NET Ajax相关的部分都已经说了。至于其他部分的代码基本属于普通的js和服务器控件编程,这里就不一一详说了。
对于ASP.NET Ajax服务器控件的开发我也是新手,欢迎大家批评。

源码:

posted on 2009-12-26 22:04  williambirkin  阅读(328)  评论(0编辑  收藏  举报