Inside ASP.NET 2.0 – Controls Model

摘要: Inside ASP.NET 2.0 – Controls Model

Inside ASP.NET 2.0 – Controls Model /黄忠成 (原文刊登于Run! PC)

读者基础需求:ASP.NET 控件设计技巧考书目:深入剖析ASP.NET 组件设计(碁峯)

Developing ASP.NET Server Controls And Components(MS Press)

Controls Model 的变革

如你所知晓,ASP.NET 1.x 提供设计师丰富且用的控件群,藉由这些控件,设计师可以快速的建构网页程序,但除这些表面上看得到的控件外,为实现组件化设计的想,ASP.NET 1.x 还提供简单但完整的Controls Model ,让设计师可以自撰写控件增加开发速,并藉由组件化开发模式简化程序的复杂低出错。在ASP.NET 2.0 中,这个Controls Model 相当大幅的延伸,除相容于原Controls Model 外,新的Controls Model 还提供比以往加丰富的基础类别。在1.x 时代,控件的开发模式并没有太严谨的规则,设计师只要满足最基础的要求,继承至System.Web.UI.Control 或是System.ComponentModel.Component 就能够撰写控件与组件,虽然这给设计师高的自由,但也间接的加重组件设计师的工作如撰写资感知控件(Data Bound Control)这种很常的想法,每个设计师硬是得先清楚DataSource DataField 属性该如何与IDE Designer 互动,然后再撰写特定的ControlDesigner 才能完成这样的控件。为日后再重新撰写这些变动低、又常用到的程序代码,有经验的设计师就会架构自己的一群基础类别。在2.0,这件事设计师费心ASP.NET Team 已经为常用、可制式化的控件建一群基础类别,现在要建一个资感知控件,设计师只要继承至DataBoundControl 即可,需撰写ControlDesigner ,要建复合式控件也只要继承至CompositeControl 行了,这个设计但简化设计师的工作,同时也为控件设计模式定下一个基本的标准,可以让初学者上手,让设计师将时间花费在组件真正的功能上。

Adapter Model

Adapter Model 首次出现于ASP.NET Mobile Controls 中,当时主要的设计概是为ASP.NET 网页能够适用于同的动装置,图1 是该设计的概。图1 Mobile Controls

 

Adapter Model 采取Adapter 设计模式,将原本应该由Control 负责的Rending 动作交给另一个对象,也就是ControlAdapter 完成,当要求到达服务器端时,ASP.NET 会判断客户端的装置类别,建对应的ControlAdapter 绘制控件,当然!在为达到Rending 的动作前提下,除真正的Rending 动作外,ControlAdapter Control 还建立了其它的通道,同的Rending 动作需要有同的Initialize 动作,也可能需要同的Pre-Rending 动作,因此ControlAdapter 中定义一群与Control 几乎相同的函式,如InitPreRenderRender 等等。在ASP.NET 2.0 中,Adapter Model 已经被整合入Control 类别中,现在设计师可以为所有的控件建ControlAdapter再仅限于Mobile Control。这个设计的目的分成远景与近需,近的是为解决同浏览器需要使用同的HTML&JavaScript绘制控件,远的是为MobilePC共享一个网页,当然!实务上这是很难达到的。

Base Control Classes

那到底有多少基础类别供我们应用呢?图2 出目前笔者所观察到的类别。图2 ASP.NET 2.0 的控件基础类别Control WebControl 都是组件设计师熟悉的类别,接下DataSourceControl ASP.NET 2.0 新引入的Data-Binding 技术所用的基础类别,在2.0 中,DataSet 已经被SqlDataSourceAccessDataSourceOracleDataSource DataSource Control 所取代,而她们的基础类别就是DataSourceControlBaseDataBoundControl 就是先前所提及的资感知控件的基础类别,她提供预定义的DataSourceID 属性,并为设计师预先建构专属的ControlDesigner,因此只要继承至此类别,设计师只需专心撰写控件的程序代码,须再耗费时间处IDE 的相关细节。DataBoundControl 具体化些的基础类别,除原有的DataSourceID 之外,另外还提供DataMember 属性,她应该是最常用的资感知控件基础类别,其子嗣CompositeDataBoundControl 则是用于复合式资感知组件,如DetailsViewFormViewGridView的基础类别都是源自于此,HierarchicalDataBoundControl 是另一个支线,她是TreeViewMenu 的基础类别。最后一个基础类别是CompositeControl,用于撰写简单、含资感知能的复合式控件。

 

Base Control Designer Classes

基础类别除提供一致的实作标准外,好的是她们预先配备标准的ControlDesigner IDE 细节,图3 是这些基础类别所配备的基础ControlDesigner 。。图3 ASP.NET 2.0 的控件基础ControlDesigner 读者们应该可以由名称对应出那个基础类别所用的ControlDesigner

Designer Actions

初用VWD VS.NET 2005 的设计师应该都对其新的设计接口感到方或是累赘管是那种感觉,至少新的设计模式真的让我们减少在WebForm 与属性盘上回的次数, 4 是这个新花招的截图。图4 ASP.NET 2.0 的新界面

Microsoft 将此技术称为Smart Task(Smart Tag 之毒?),简单的就是将常用的属性与工作放到这个容器中,让设计者可以方的设定她们,须在属性表及WebForm 來來去去,当然!这也有缺点,这个窗口显示的速考验着开发者的计算机速,何时该缩、何时该展开也考验着接口设计人员的智能。那如何让自己的控件拥有这种效果呢?說來也简单,程序1 程序1 含有Smart Task 的控件

#region Using directives using System; using System.Collections.Generic; using System.Text; using System.Web.UI.WebControls; using System.ComponentModel;

using System.ComponentModel.Design; using System.Web.UI.Design; using System.Web.UI.Design.WebControls; #endregion

namespace ClassLibrary1{

public class SCCDesignerActionList : DesignerActionList {private ControlDesigner _designer;public bool ShowText {

get {

return ((SimpleDesignerTest)_designer.Component).ShowText; } set {

((SimpleDesignerTest)_designer.Component).ShowText = value; _designer.UpdateDesignTimeHtml();

} } public SCCDesignerActionList(ControlDesigner designer):base() {

_designer = designer; }public void FireShowText() {

ShowText = true; }public void FireHideText() {

ShowText = false; } public override DesignerActionItem[] GetSortedActionItems() {

DesignerActionPropertyItem item = new DesignerActionPropertyItem("ShowText",

"Show Text","Appearence"); DesignerActionMethodItem m_item; if (!ShowText)

m_item = new DesignerActionMethodItem(this, "FireShowText", "Show Text","Actions");

else m_item = new DesignerActionMethodItem(this, "FireHideText", "Hide Text", "Actions"); return new DesignerActionItem[] {item,m_item};

}
}

public class SCCControlDesigner : System.Web.UI.Design.ControlDesigner { public SCCControlDesigner() : base() { } public override DesignerActionListCollection ActionLists {

get

{DesignerActionListCollection actions =

new DesignerActionListCollection(); actions.AddRange(base.ActionLists); actions.Add(new SCCDesignerActionList(this)); return actions;

}

} } [DesignerAttribute(typeof(SCCControlDesigner), typeof(IDesigner))]public class SimpleDesignerTest:WebControl {

public bool ShowText {

get { object o = ViewState["ShowText"];if (o != null)

return (bool)o;

return true; }set {

ViewState["ShowText"] = value;

} } protected override void Render(System.Web.UI.HtmlTextWriter writer) {

if(ShowText)

writer.WriteLine("TEST"); }public SimpleDesignerTest() { }

} }

DesignerActionPropertyItem 指的是一个属性型态的Smart Tas kIDE 会将指定的属性显示成Smart Task,并套用该属性所有的属性编辑器。DesignerActionMethodItem 指的是一

个选项,供使用者点选后执某些动作,与以前的DesignerVerbs 功能相同。

New Data Binding System

对于ASP.NET 的使用者而言,新的Data Binding 系统无疑是2.0 中变动最大、影响也最深的设计,原DataSet 已经被DataSource Control 所取代,这个变动将ASP.NET RAD 设计模式推进一大步,DataSource 模式讲求将资操作完全封装在DataSource Control 中,控件与其的连结仅止与资的交换,设计师也再需要烦恼分页、快取等琐碎的问题,甚至可以使用ObjectDataSource 建构分布式系统。对于组件设计师來說 新的Data Binding 模式影响大,因为2.0 已经建构完整的基础类别,程序2 是一个简单的Table 控件(因为Beta 1 DataBoundControlDesigner 标示为抽象类别,因此我们得继承她才能使用)

程序2 SimpleTable 控件

#region Using directivesusing System;using System.Collections; using System.Collections.Generic; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using System.ComponentModel; using System.Web.UI.Design.WebControls; #endregion

namespace ClassLibrary1 {

public class SimpleTableDesigner : DataBoundControlDesigner { } [Designer(typeof(SimpleTableDesigner))]public class SimpleTable:DataBoundControl {

private IEnumerable _dataRecs; protected override HtmlTextWriterTag TagKey { get { return HtmlTextWriterTag.Table;

} }protected virtual void RenderHeader() {

if (_dataRecs != null) {

foreach (object item in _dataRecs) { HtmlTableRow row = new HtmlTableRow(); foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(item))

{ HtmlTableCell col = new HtmlTableCell(); col.Controls.Add(new LiteralControl(prop.Name)); row.Controls.Add(col);

}
Controls.Add(row);
break;

}

}

}

protected virtual void RenderRows() { if (_dataRecs != null) { foreach (object item in _dataRecs) { HtmlTableRow row = new HtmlTableRow(); foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(item)) { HtmlTableCell col = new HtmlTableCell(); object v = prop.GetValue(item); if (v != null) col.Controls.Add(new LiteralControl(v.ToString())); row.Controls.Add(col); } Controls.Add(row); } }

}

private void OnSelectCallBack(IEnumerable data) { _dataRecs = data;

}

protected void RetrieveData() { DataSourceView view = GetData(); if(view != null) view.Select(DataSourceSelectArguments.Empty, new DataSourceViewSelectCallback(OnSelectCallBack));

}

public override void DataBind() { ClearChildControlState(); Controls.Clear(); RetrieveData(); RenderHeader(); RenderRows(); ChildControlsCreated = true; TrackViewState();

}

protected override void OnPreRender(EventArgs e) { if (_dataRecs == null)

DataBind();

base.OnPreRender(e);

}

public SimpleTable() { }

} }

Non Visual Controls

ASP.NET 2.0 取消Component Tray ,这代表着Component ASP.NET 2.0 中将完全失去RAD ,取而代之的是类似DataSource Control 类的非可视型组件,就实务上來說Non Visual Control 还是一个控件,差别在于IDE 如何看待她,要将某个控件标示为Non Visual Control ,只要配上NonVisualControl Attribute 即可。

[NonVisualControl(true)]

截至目前为止,我仍未发现该Attribute 会对IDE 造成何种影响,或许日后正式版时该Attribute 会用于另一种设计,让Non Visual Control 会影响到设计版面。

Web Parts

2.0 将原本于SharePoint 上的Web Parts 整合进,许多初次接触Web Parts 的朋友反应都很正面,这个技术可以让我们与组件化设计模式贴近,在组件设计上,撰写一个自订的Web Parts 也很容,程序3 是个简单的范,她也同时展示如何使用Web Parts Connection 技术。程序3

#region Using directives

using System;

using System.Collections.Generic;

using System.Text;

using System.Security.Permissions;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using System.ComponentModel;

#endregion

namespace ClassLibrary1{

public class MySimpleWebPart:WebPart

{

public override bool AllowEdit

{

get {

return true; }set {

base.AllowEdit = value;

}

}

public string Text { get { object o = ViewState["Text"]; if (o != null) return (string)o; return String.Empty; } set { ViewState["Text"] = value; }

}

[WebBrowsable,Personalizable(PersonalizationScope.User)]

public string TestVeriable { get { return "TEST"; } set { ;}

}

[WebBrowsable,Personalizable,Category("Behavior")]

public string TestVeriable2 { get { return "TEST"; } set { ;}

}

[ConnectionProvider("provider","provider")]

public object GetData() { return Text;

}

[ConnectionConsumer("consumer","consumer")] public void SetData(object data) {

Text = (string)data;

}

protected override void RenderContents(HtmlTextWriter writer) {

writer.WriteLine(Text);

}

public MySimpleWebPart() { }

} }

Client Callback

ASP.NET PostBack 技术使网页程序的功能贴近窗口程序,但仍然无法避免于PostBack 发生时画面的闪烁情况,其实这个问题一直都困扰着许多的设计师,为让网页能在最小闪烁情况下新,XMLHTTP 技术就应运而生,此技术运用XML HTTP协议,再整合JavaScript 让网页可以在某个事件发生时新一部份的网页,由于并非是重新向Web Server 要求网页,因此画面的闪烁情况可以减到最轻。2.0 整合此技术,提供一个标准的JavaScript ICallbackEventHandler 接口,控件只要实作此接口,并搭配上适当的JavaScript ,就能达到网页部份刷新的效果,程序10是一个简单的范LowcaseTextBox会将所有输入的字转成小写,读者可以放上她及几个RadioButton或是CheckBox,当焦点由LowcaseTextBox 开时,就可发现字都被转成小写,你也会发现,浏览器并未重新刷新网页,也没有闪烁的情况。程序10 LowcaseTextBox #region Using directives using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Web.UI; using System.Web.UI.WebControls; #endregion namespace ClassLibrary1{

public class LowcaseTextBox:TextBox,ICallbackEventHandler {

private const string SCRIPT =

"<script language='JavaScript'>\n"+

"function SetText(eventarg){\n"+

"document.getElementById('%ID%').value = eventarg;\n"+

"}</script>";

public LowcaseTextBox() { }protected override void AddAttributesToRender(

HtmlTextWriter writer) { base.AddAttributesToRender(writer); writer.AddAttribute(

HtmlTextWriterAttribute.Onchange, Page.GetCallbackEventReference(

this,ClientID+".value","SetText",null)); } protected override void OnPreRender(EventArgs e) {

base.OnPreRender(e); Page.ClientScript.RegisterClientScriptBlock( GetType(), ClientID,

SCRIPT.Replace("%ID%",ClientID)); } #region ICallbackEventHandler Members public string RaiseCallbackEvent(string eventArgument) {

return eventArgument.ToLower(); }

#endregion

} }

Still Running

ASP.NET 2.0 仍在开发中,可以预期的是Web Parts 部份会持续的加强,许多的基础类别也会一一的加入,相信在2.0 推出后,在控件市场上一定会出现相当多且方的控件