拥抱变化——从Atlas到ASP.NET AJAX(4):大大简化的了的Extender扩展器控件

阅读本文之前,您需要安装完成Microsoft ASP.NET AJAX v1.0 Beta(详见拥抱变化——AtlasASP.NET AJAX1):下载安装总览)。安装完成之后,Visual Studio中新建Web Site的时候会多出一个模版:ASP.NET AJAX Enabled Web Site。接下来的内容均将基于新建的ASP.NET AJAX Enabled Web Site

 

摘要

ASP.NET AJAX中,Extender Control(扩展器控件)同样很重要。如果说UpdatePanel只是将Ajax的核心概念和基本特性——局部更新和异步回送引入了ASP.NET的话,那么扩展器控件则在这个基本特性上迈出了新的一步——为页面添加丰富的客户端功能,让用户一眼就能够看出来:噢,这个网站真的太“Ajax”了!

本文将分析相对于从Atlas到ASP.NET AJAX扩展器控件使用方法的变化。

 

扩展器控件介绍

ASP.NET AJAX提供了两种内建的扩展器控件:DragOverlayExtender和AutoCompleteExtender,前者让用户可以将页面中的某个部分在其中任意拖动并排布,而后者可以为某个TextBox添加自动完成的功能。这样,该TextBox将拥有类似浏览器地址栏的行为,用户输入一段文字后会自动弹出与之匹配的提示选项。关于CTP版本的AutoCompleteExtender使用方法,请参考使用ASP.NET Atlas AutoComplete Behavior或AutoComplete Extender实现自动完成功能(上)以及使用ASP.NET Atlas AutoComplete Behavior或AutoComplete Extender实现自动完成功能(下)

扩展器控件的实现原理也不复杂:ASP.NET AJAX将在生成HTML时将ASP.NET页面中定义的扩展器控件转化为ASP.NET AJAX客户端行为(Behavior)的声明,并添加到页面中,除此之外的所有实现均由ASP.NET AJAX的客户端运行时处理。当页面到达客户端之后,客户端的ASP.NET AJAX框架将在客户端运行该行为。关于ASP.NET AJAX客户端行为,请参考在ASP.NET Atlas中创建自定义的Behavior(注意,该文章基于CTP版本书写,部分内容已经过时)。

另一个微软公司和社区共同开发的免费开源的第三方控件包——ASP.NET AJAX Control Toolkit则提供了更多、甚至可谓包罗万象的扩展器控件,几乎覆盖了常用的各种富客户端功能,并且还在迅速增加中。而这些控件使用起来又和ASP.NET AJAX内建的扩展器控件完全一致,让开发人员倍感亲切。ASP.NET AJAX Control Toolkit中提供的这些扩展器控件将在本卷第7、8、9、10章中详细介绍。ASP.NET AJAX Control Toolkit还提供了扩展器控件的基础开发设施(控件基类等)以及Visual Studio中的项目模版等。借助于这些便利的设施,我们也可以容易地进行自定义控件的开发。关于服务器端扩展器控件的开发,请参考开发ASP.NET Atlas服务器端Extender控件——基本概念以及预先需求开发ASP.NET Atlas服务器端Extender控件——编写客户端Behavior开发ASP.NET Atlas服务器端Extender控件——编写服务器端Extender & Dflying近期动向以及开发ASP.NET Atlas服务器端Extender控件——在实际开发中使用编写好的控件(注意,该文章基于CTP版本书写,部分内容已经过时)。

 

CTP版本的扩展器控件使用方法

一般来讲,我们可以采用如下方法使用CTP版本中的扩展器控件:

  1. 在将要应用扩展器控件的页面上要保证有一个ScriptManager控件,并且其EnableScriptComponents属性值要设定为true(这也是其默认值)。因为扩展器控件将生成Atlas的客户端行为(Behavior),然后委托给这个行为去完成真正的客户端扩展工作,而行为的运行则需要有完整的Atlas客户端框架支持。
  2. 页面源代码中,在ScriptManager控件声明的后面添加扩展器控件的声明。即添加类似<atlas:[扩展器名称]Extender>的标签,并为其指派一个ID,当然还有必不可少的runat="server"。这个标签可以被认为是该Atlas页面中所有同样类型的扩展器控件的归类。你同样可以在该标签中指定扩展器的某些属性,和AutoCompleteExtender扩展器的MinimumPrefixLength、ServiceMethod、ServicePath等属性一样。
  3. 在扩展器控件的声明标签内添加扩展其属性声明标签,即在<atlas:[扩展器名称]Extender>标签内添加类似<atlas:[扩展器名称]Properties>标签。该扩展器的扩展目标(TargetControlID属性,这是每一种<atlas:[扩展器名称]Properties>标签都支持的。)以及其专有的属性(例如<atlas:AutoCompleteProperties>的Enabled属性)可以在该标签中设定。
  4. 对于<atlas:[扩展器名称]Properties>标签和<atlas:[扩展器名称]Extender>标签中暴露出的相同的属性,我们可以任选一处进行设定。但如果两处都设定了的话,那么将取<atlas:[扩展器名称]Properties>标签中的设定值,也就是说<atlas:[扩展器名称]Properties>标签中的设定将覆盖<atlas:[扩展器名称]Extender>标签中的设定。合理选择某个重复出现在<atlas:[扩展器名称]Properties>标签和<atlas:[扩展器名称]Extender>标签中属性的设定位置将有利于在页面中包含有多个同样类型扩展器控件时减少扩展器声明部分的代码量。
  5. 一个<atlas:[扩展器名称]Extender>标签下可以定义多个<atlas:[扩展器名称]Properties>标签。换句话说,若要使用已经在页面中使用过的扩展器控件,我们既可以重新添加一个<atlas:[扩展器名称]Extender>标签并在其中添加相应的<atlas:[扩展器名称]Properties>标签,也可以在原有的<atlas:[扩展器名称]Extender>标签内添加一个新的<atlas:[扩展器名称]Properties>标签。一般情况下,我们应该尽力使用后一种方法,因为这样可以让代码简洁易懂。然而,不是所有的扩展器控件,也不是在每种情况下我们都可以使用这种方法的,某个扩展器控件是否能够完全支持这种只添加另一个额外<atlas:[扩展器名称]Properties>标签的方法来在页面中添加多个扩展器,将取决于控件设计时<atlas:[扩展器名称]Properties>标签中所暴露出的属性能否完全覆盖<atlas:[扩展器名称]Extender>标签中暴露的属性。
  6. 一个现有的ASP.NET控件可以添加多个扩展器控件,分别使用不同的<atlas:[扩展器名称]Extender>标签声明。

 

CTP到Beta中扩展器控件使用方法的变化

Beta版本中,ASP.NET AJAX的扩展器控件概念被大大简化,取消了CTP版本中的<atlas:[扩展器名称]Extender>和<atlas:[扩展器名称]Properties>这两个标签,而是统一使用<asp:[扩展器名称]Extender>标签。所有的属性也都在<asp:[扩展器名称]Extender>标签中设置。

 

Beta版本的扩展器控件使用方法

一般来讲,我们可以采用如下方法使用Beta版本中的扩展器控件(同样适用于ASP.NET AJAX Control Toolkit中的扩展器控件):

  1. 在将要应用扩展器控件的页面上要保证有一个ScriptManager控件。因为扩展器控件将生成ASP.NET AJAX的客户端行为,然后委托给这个行为去完成真正的客户端扩展工作,而行为的运行则需要有完整的ASP.NET AJAX客户端框架支持。
  2. 页面源代码中,在ScriptManager控件声明的后面添加扩展器控件的声明。即添加类似<asp:[扩展器名称]Extender>的标签,并为其指派一个ID,当然还有必不可少的runat="server"。
  3. 该扩展器的扩展目标控件(由TargetControlID属性指定)是必不可少的,任何一种扩展器控件都支持该属性。我们应该将其指向需要被“扩展”的服务器端控件,例如上面的AutoCompleteExtender示例程序中就将该属性指向了页面中的一个TextBox。
  4. 设置该扩展器控件专有的属性,例如AutoCompleteExtender的ServicePath和ServiceMethod属性等。
  5. 我们可以为某个现有的ASP.NET控件添加多个扩展器控件,这些功能将叠加到一起,并应用到目标控件上。

 

扩展器控件与装饰模式(Decorator Pattern)

ASP.NET AJAX中的扩展器控件是装饰模式的一次非常典型且完美的应用。Design Pattern一书中提到,装饰模式有如下的几种使用情况:

  1. 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  2. 处理那些可以撤消的职责。
  3. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

对于这三种使用情况,ASP.NET AJAX中的扩展器控件均有良好体现。我们就以应用于ASP.NET中TextBox控件的AutoCompleteExtender扩展器控件来举例:

  1. 不是应用程序中所有的TextBox都需要自动完成功能的。一般来讲,需要自动完成功能的TextBox在整个应用程序中所占的比例非常少。若使用继承来实现该功能,则势必导致自动完成功能在大多数TextBox中都没有使用。而且,这种继承的实现方式也需要对现有的代码进行修改(将<asp:TextBox>标签修改为类似<asp:AutoCompleteTextBox>的标签)。既提高了对现有程序扩展的难度,也增大了在修改过程中发生错误的可能性。
  2. 对于程序中需要暂时控制自动完成功能启用与否的需求,我们只要简单地改变是否为其添加AutoCompleteExtender扩展器控件既可。若采用继承的方式在程序中动态创建新的TextBox,则需要选择不同的对象(TextBox或AutoCompleteTextBox)进行创建,比较麻烦;而若采用应用了装饰模式的扩展器控件方式,就可以首先总是创建同一类TextBox控件,然后根据是否需要自动完成功能来决定是否创建AutoCompleteExtender。即使程序运行中该TextBox不再需要自动完成功能了,我们也无需对TextBox做任何的修改,只要移除其附加的AutoCompleteExtender即可。
  3. 对于ASP.NET的TextBox控件来说,我们既可以使用AutoCompleteExtender让其拥有自动完成的功能,也可以使用ASP.NET AJAX Control Toolkit中的FilteredTextBox扩展器让用户只能在其中以一种指定模式输入文本。这样,若使用继承的方式来实现这两种扩充,我们的系统中则必须维护四种对象:TextBox、AutoCompleteTextBox、FilteredTextBox以及FilteredAutoCompleteTextBox。如果某一天我们还要给TextBox添加水印的功能(即ASP.NET AJAX Control Toolkit中的TextBoxWatermark扩展器所实现的功能),则需要维护的TextBox的种类则将增加为8种!长此以往,这种“类爆炸”的结果将让系统非常难以扩展或维护。若采用应用了装饰模式的扩展器控件的方式,则只要维护这几个扩展器,并在需要时从中选择并添加到某个TextBox上。

 

写作感想

  1. 结构化、有组织的论述问题
  2. 文章要符合读者的阅读习惯(重复好多遍了)
  3. 我似乎变得越来越能写了,可是语言越来越苍白
  4. 拥抱变化有点累
  5. (还没想好……)

(PS:本文部分内容选自我的Atlas著作——《ASP.NET AJAX程序设计——第I卷:服务器端ASP.NET 2.0 AJAX Extensions与ASP.NET AJAX Control Toolkit》,将于明年一月出版,希望大家支持。)

posted on 2006-11-12 20:38  Dflying Chen  阅读(4404)  评论(21编辑  收藏  举报