周三中午吃完饭,雨辰从食堂回来,发现开发小组中的一位姓黄的同事(组里管他叫老黄牛)正在看一本.net的书,因为这几年雨辰已基本上不再买技术类的书了,因为真正看的上眼有价值、有水准的书籍前些年基本都翻遍了。雨辰带着好奇心走过去问老黄关于该书的一些情况,因为已过了对那些所谓标以‘入门’、‘精通’,‘深入浅出’,‘宝典’之类图书的‘感冒期’,所以就单刀直入,问了老黄正在看的部分。
    老黄也很高兴有人一起交流,就说:“目前正在看关于控件开发方面的内容,该书用了近50页的篇幅来介绍控件的原理,开发流程,相关函数方法,示例啥的。” 
    雨辰笑着对他说:“具我所知,在.net中控件分为自定义控件和用户控件,其中用户控件我接触的比较早,这些年也一直在用,特别是web用户控件。而web自定义控件是加入到产品组之后才开始设计使用的。不过这些年走过来倒是有些心得,不知道黄兄有兴趣听不。” 
    老黄点点头:“当然了,我之前在上家公司也开发设计过一些用户控件,感觉还是挺容易上手的。” 
    雨辰拍拍老黄的肩膀,笑着说:“的确如此,其实我感觉控件的好处有几方面,首先就是提升代码的可复用性,进而封装可复用的业务逻辑(这主要体现在用户控件上)。其次是缓存,其提供了一种可以缓存页面局部信息的方法,比如在web用户控件头添加声明:
      <%OutputCache Duration= "300 " VaryByParam= "none "%>
 
    而第三点是用户控件也提供了一个不错的AJAX调用方式,之前就曾有人在网上使用ajaxHelper的方式来实现加载ascx文件并获取相应的返回信息之后,加载并刷新当面页面指定区域(通常是div元素)。” 
    “你前两点我认同,但第三点是什么意思呀,我怎么听不太明白?”老黄不解的问。 
    雨辰解释说:“其实原理很简单,就是你在一个叫ajax.aspx的页面上放一个form标签,其runat"server",然后在ajax.aspx.cs文件的Page_Load事件中使用该formcontrols属性,并调用该属性的add方法来加载指定的用户控件(作为参数传入),这时该ajax.aspx页面就会显示指定的用户控件的输出内容了。当然在ajax.aspx页面中会有一个标签用于记录当前的页面中用户控件显示信息的‘区域’,这样在使用prototype.js这类框架中的ajax功能时,就可以直接向ajax.aspx页面传递要调用的用户控件‘参数’,并将控件加载后返回的页面信息截取出来,做为ajax调用的返回结果,用于刷新指定页面元素(通常是div)中的innerHtml属性。说了这些,我给你个链接,你可以看看其最终的实现代码(代码链接 
    听了雨辰这么说,老黄才明白了到底怎么回事,说:“不过我感觉这个ajax.aspx就像个代理一样,呵呵,不过这也算是提供了一个不错的思路了。因为我以前都是用ajax请求一个可执行的页面或链接,比如 aspxashxasmx。” 
    雨辰笑着说:“这也说明了web用户控件的开发与web页面还是很接近的,呵呵。另外就是在刚学习和开发用户控件时往往还会犯一些小毛病。” 
    老黄听雨辰这么说,问道:“哪些毛病?!” 
    “首先就是将一些特殊的业务逻辑加入到用户控件代码中,降低了控件的通用性。其次就是有些人习惯于在web用户控件上只放一个LABLE,然后就用CS文件来填充其.text属性。这不是什么好习惯。对于复杂的数据展示就应该使用Repeater,Datagrid等控件,甚至要复合几个控件来满足需求,而这种将数据和显示控件代码硬编码到一起的做法,会让后续的代码分离,阅读理解都会带来不少的麻烦。另外我发现有些开发者习惯于把web用户控件的所有CS代码都放到Page_load事件中,但因为用户控件的CS文件中也有OnInit 、InitializeComponent等方法(与page相似),应根据当前要实现的功能将代码均匀分布相应的事件代码中,比如初始化组件代码、数据绑定到控件的代码,页面显示代码等。这三个毛病只是其中的一部分,应该有一定的代表性了。” 雨辰边想边说。 
    老黄点了点头。不过我还有个问题,之前我曾在项目中为所有的web用户控件构造了一个基类,在这个基类中提供了一些公有属性,比如控件版本号,控件名称以及一些常用属性的初始化操作等等,这样在其派生子类(控件)中可以直接使用这些被初始化好的属性了。当然我还想一些跳转逻辑写到这个控件基类中,比如在管理后台操作中,如用户身份验证出现问题时,就可以完成跳转,而不能等执行到主页面page_load时再跳转,不知道这个做有什么问题没有。” 
    雨辰笑着说:“你抽取出一个控件基类的作法我没什么异议,只是在‘是否在用户控件中提供跳转操作’的想法有些差别。当然你这样写也能完成你想要实现的功能。但我认为对用户身份权限校验这类的操作还是应该放到控件所属的主页面或业务逻辑层为宜,因为控件完成的只是做数据绑定,显示数据这类工作,如果掺杂过多的特殊业务逻辑会降低控件的通用性。另外如果让控件基类做这种权限类的操作时,你必须还要提供相应的‘开关’属性,因为不是所有的应用场景都需要对用户权限进行校验,这无形中也增加了使用控件的‘难度系数’,甚至让人产生歧义。当然如果你的这类控件只想用于我之前所说的那种ajax调用的话,那还说得过去,因为它很类似于直接调用aspx页面,这样当客户端请求用户控件时,通过对其所提供的身份信息进行验证,返回相应的结果或校验错误信息,这倒是一个不错的思路。” 
    老黄笑着说:“你说要将页面跳转这类的代码放到什么地方呢?” 
    雨辰想了想,在旁边的黑板上写了一些东西,内容如下: 
    主页面属性 -->主页面的构造函数--> 用户控件属性-->用户控件的构造函数-->用户控件的OnInit事件-->用户控件的InitializeComponent方法-->主页面的OnInit事件-->主页面的InitializeComponent方法-->主页面的Page_Load()-->用户控件页的Page_Load()
 
    然后转过身对老黄说:“这是对有用户控件的页面的执行流程,如果我要写权限验证的逻辑,我会将其放到第二步,也就是主页面的构造函数中,这样就可以在‘验证操作’不通过的情况下,不再依次执行后续的控件初始以及page_load方法了,相信在速度上也会快许多,不过如果在构造方法中使用Response.Redirect(…)这类的写法可能会出问题,我建议使用System.Web.HttpContext.Current.Response.Redirect(),这样就差不多了。 
    老黄看着这个执行流程,想了想自己开发的代码,说:“一会我回去测一下这个流程,如果如你所说的话,那我要改的地方就多了,呵呵。” 
    雨辰接着说:“其实开发控件是需要对页面和控件生命周期有一定了解的。特别是开发自定义控件。因为用户控件提供了.ascx和相应的.ascx.cs文件,这样就很便于将数据绑定与显示逻辑进行代码分离,同时其开发起来与进行webform页面开发有很多相似之处,甚至在刚上手时没什么区别,所以难度不是很大。而自定义控件就有所不同了,其自身没有前台界面文件(比如ascx等),所有的代码都是在相应.cs文件中提供的,比如要开发在web页面中使用的自定义控件,就需要继承WebControlControl,并实现Render(HtmlTextWriter pOutPut) 方法。” 
    雨辰把水杯拿过来喝了一口水然后继续说道:“另外我更倾向于在用户控件中加入更多的业务逻辑,而在自定义控件中的业务逻辑就没那么重要了,因为自定义控件应该更强调其通用性、易用性的一面,比如之前在用户控件中拖入lable这类自定义控件等。另外在自定义控件开发中要考虑webpage和控件生命周期、事件机制回传事件视图状态等问题及与之相关的流程函数。所以我感觉自定义控件开发要将更多的精力放在控件的通用性(比如可以在winformwebform中同时使用),还有控件底层实现机制上的理解上。有人说开发自定义控件要比用户控件复杂,原因就在于此。” 
    老黄说:“的确,我也认为做web型的自定义控件也要了解页面生命周期相关的内容,目前我还在开发设计用户控件阶段,没有涉足到自定义控件方面,呵呵。” 
    雨辰笑着说:“这只是时间问题,呵呵。另外就是我这些年开发控件时大体经历了四个阶段:
    1.上手阶段,这一阶段把什么代码都放在一个cs或一个类中,并不根据其控件行为,结构的复杂性进行类层面上的定义分解,比如要开发tab控件时,就应该将相应的属性页分解成一个cs类文件,这样就可以把整个tab控件看成是一个个属性页的‘集合’来进行‘复合处理’(使用CreateChildControls创建子控件)。
    2.看开源或破解的第三方商业控件库代码(有些不道德,但是个学习途径),这时才发现自己写的就是垃圾,看出了与别人的差距。感觉自己的水平就像是小学生,自信心受到了空前的打击,甚至不敢再进行开发。
    3.收拾起‘残破’的自信心,研究相应的优秀代码,同时深研底层实现(比如用reflector.net中的控件‘源码’,同时再看一些.net出现之前的那些控件及其实现代码(比如MFC,DELPHI控件库),从对底层的理解上更上一层楼。
    4.为控件提供界面丰富的‘设计时支持’,比如使用PropertyGrid(属性表)以及继承并实现ControlDesigner(位于System.Web.UI.Design名空间)并重写GetDesignTimeHtml()方法,这样就可以允许用户在VS中的设计器中对控件‘手动’进行初始化,这一点可以想想我们以前使用TreeView这类控件时,手工添加树形结点的情况。 
    当然要开发好web控件,对cssjs还要有一定理解,必定有时用样式或JS实现要比用cs代码要更方便,灵活,可定制性更好。只不过这方面商业控件要考虑js脚本加密、混淆等问题了。” 
    老黄点了点头说:“其实介绍如何开发控件就可以写上几本书了,看老外那边光一个列表控件就可以卖几百美刀,感觉真有大有‘钱途’呀。” 
    雨辰苦笑着说:“谁说不是呢,哎! 另外目前国外的控件开发商都有功能丰富、界面华丽的产品,比如 ComponentArt, telerik , devexpress, componentone, infragistics, netikatech等等,并且其研发步伐往往还紧随像微软这样的IT巨头,比如silverlight这类技术的相关控件研发,我看其活得都挺‘滋润’。但国内控件开发者的生存状况就不太乐观了,必定这一产品是要以技术为支撑的,其使用的‘客户’往往也是开发者,比行业软件开发者还要‘幕后’。另外大家对软件‘免费’有过多的依赖和‘误解’。所以国内通过开发控件走商业化,要比软件商业化更难,甚至是‘无解’。比如大家对一款商业化好用的控件库往往抱着下面的看法:
    1.首先是想到破解版(最好还能有源码)。
    2.考虑其它可以免费使用该控件的途径。
    3.如没有其它路子,就想自己能否开发一款相类似的控件出来 (往往开发周期长或功能过于复杂而作罢)
    4.如果此路不通,再看看有无开源的项目来做替代品。有时最后因为不愿付费,而最终改变了软件的界面设计,给用户使用上造成困扰,用户体验也不好,甚至很BT。”
    老黄笑着说:“目前你说的这些我基本赞同,但我还是希望国内商业控件公司能有不错的发展,必定这是一个方向呀,之前曾在csdn上看过一家国内公司提出基于业务组件(实际也就是控件)开发,换句话说只要把几个组件(或控件)拖拽到窗体或页面中,简单设置一个属性,开发就完成了,多方便呀!” 
    雨辰坏笑着说:“如果真要有那一天,那你我就都TM失业了,因为到那时老板或业务专家,就可以完成软件开发,还要你我干什么,呵呵。” 
    这时雨辰看了一下时间差不多了,寒暄几句之后,就回座位工作了。