惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hacker News: Ask HN
Hacker News: Ask HN
H
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog RSS Feed
Jina AI
Jina AI
Stack Overflow Blog
Stack Overflow Blog
量子位
博客园_首页
Vercel News
Vercel News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Forbes - Security
Forbes - Security
IT之家
IT之家
N
News and Events Feed by Topic
S
Security Affairs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Webroot Blog
Webroot Blog
Recorded Future
Recorded Future
L
LangChain Blog
Y
Y Combinator Blog
AI
AI
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Know Your Adversary
Know Your Adversary
AWS News Blog
AWS News Blog
Help Net Security
Help Net Security
Cyberwarzone
Cyberwarzone
L
Lohrmann on Cybersecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google Online Security Blog
Google Online Security Blog
V2EX - 技术
V2EX - 技术
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
PCI Perspectives
PCI Perspectives
I
Intezer
T
Tenable Blog
G
Google Developers Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Troy Hunt's Blog
L
LINUX DO - 最新话题
云风的 BLOG
云风的 BLOG
C
CXSECURITY Database RSS Feed - CXSecurity.com
有赞技术团队
有赞技术团队
O
OpenAI News
P
Proofpoint News Feed
TaoSecurity Blog
TaoSecurity Blog
C
Check Point Blog
Last Week in AI
Last Week in AI
S
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
Blog — PlanetScale
Blog — PlanetScale

博客园 - williambirkin

.NET平台自带的AOP机制 转自《设计模式--基于c#的工程化实现及扩展》 今天看PolicyInjection遇到的问题 转 事务隔离级别 转 UML 基础: 类图 关于Form.show的问题 关于where中子查询的性能问题 请高手指教 模式 转 SQL Server 索引结构及其使用 (三) 转 SQL Server 索引结构及其使用 (二) 转 SQL Server 索引结构及其使用 (一) 泛型的一点思考 摘自《.NET 2.0 模式开发实战》 转 《为Windows应用创建简单的异步调用模式》 转 C#格式化数值结果表(格式化字符串) SQL游标的简单使用 转“理解VC# 2005中的字符串和正规表达式” 2个IP输入控件 Windows Forms中禁用窗体的关闭按钮 回车移动焦点 文本框事件顺序
我的第一个ASP.NET Ajax控件
williambirkin · 2009-12-26 · via 博客园 - 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服务器控件的开发我也是新手,欢迎大家批评。

源码: