这是我自己写的第一个基于Microsoft AJAX Library的Ajax控件。
这个是一个类似google输入框的AJAX控件。应该是很常见的,网上也有不少用别的AJAX类库实现的例子。
JS组件的代码是从《ASP.Net3.5控件和组件开发技术》提供的一个ajaxpro实现的示例的版本上改进过来的。
毕竟这也不是什么新功能了,能复用就复用吧:)。
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
几个关键属性
- ServiceMethodName表示要调用的WebService的方法
- DataSourceURI表示WebService的地址
- PreGetDataHandle表示调用WebService前为组件提供WebService所需参数的方法
- GetDataSucceededHandle表示成功调用WebService后客户端对返回数据处理的方法
这几个属性基本上都是为了使控件的使用更加灵活方便,使控件的使用者能参与Ajax过程中的相关处理,提供一定的扩展性。
到此为止此控件中与ASP.NET Ajax相关的部分都已经说了。至于其他部分的代码基本属于普通的js和服务器控件编程,这里就不一一详说了。
对于ASP.NET Ajax服务器控件的开发我也是新手,欢迎大家批评。
源码: