设为首页收藏本站language 语言切换
查看: 758|回复: 0
收起左侧

ASP.NET 定制控件的开发(二)

[复制链接]
发表于 2010-2-25 10:23:43 | 显示全部楼层 |阅读模式
<p ><ccid_nobr><strong>复合控件的创建</strong></ccid_nobr><p >    创建定制控件的第三种方法是组合二个或二个以上的现有的控件。在下面的例子中,读者将以合同编程人员的身份出现,而我则是客户,我希望读者能够开发一个稍微复杂一些的控件,使我能够用来记录收到的对我的书的询价。<p >    作为客户,我将要求读者开发一个控件,使我能够输入一本或多本书籍,每当点击一本书时,控件就会记录下对该书的点击次数,如下图所示:<p ><ccid_nobr><center><img  src="http://www.hh010.com/upload_files/article/244/9_qh2iqi15279.gif"></center></ccid_nobr><p >    这一程序的.aspx文件如下所示,除@ Page命令外,该程序的C#和VB程序是相同的:<p ><ccid_nobr><strong>利便控件的.aspx文件</strong></ccid_nobr><p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     &lt;%@  Page  language="c#" <br>Codebehind="WebForm1.aspx.cs" <br>AutoEventWireup="false" <br>Inherits="CustomControlWebPage.WebForm1"  %><br>  <br>  &lt;%@  Register  TagPrefix="OReilly"  Namespace="CustomControls"  Assembly="CustomControls"  %><br>  <br>  &lt;!DOCTYPE  HTML  PUBLIC  "-//W3C//DTD  HTML  4.0  Transitional//EN"  ><br>  &lt;HTML><br>      &lt;HEAD><br>  &lt;meta  content="Microsoft  Visual  Studio  7.0"  name=GENERATOR><br>  &lt;meta  content=C#  name=CODE_LANGUAGE><br>  &lt;meta  content="JavaScript  (ECMAScript)"  name=vs_defaultClientScript><br>  &lt;meta  content=http://schemas.microsoft.com/intellisense/ie5  name=vs_targetSchema><br>      &lt;/HEAD><br>  &lt;body  MS_POSITIONING="GridLayout"><br>  &lt;form  id=Form1  method=post  runat="server"><br>  <br>              &lt;OReilly:BookInquiryList<br>              Runat="Server"<br>              id="bookInquiry1"><br>  <br>                    &lt;OReilly:BookCounter <br>                  Runat="server" <br>                  BookName="rogramming  ASP.NET" <br>                  ID="Bookcounter1"/><br><br>                  &lt;OReilly:BookCounter <br>                  Runat="server" <br>                  BookName="rogramming  C#" <br>                  ID="Bookcounter2"  /><br>  <br>                    &lt;OReilly:BookCounter <br>                  Runat="server" <br>                  BookName="Teach  Yourself  C++  21  Days" <br>                  ID="BookCounter3"  /><br>  <br>                    &lt;OReilly:BookCounter <br>                  Runat="server" <br>                  BookName="Teach  Yourself  C++  24  Hours" <br>                  ID="Bookcounter4"  /><br>  <br>                    &lt;OReilly:BookCounter <br>                  Runat="server" <br>                  BookName="Clouds  To  Code" <br>                  ID="Bookcounter5"  /><br>  <br>                    &lt;OReilly:BookCounter <br>                  Runat="server" <br>                  BookName="C++  From  Scratch" <br>                  ID="Bookcounter6"  /><br>  <br>                    &lt;OReilly:BookCounter <br>                  Runat="server" <br>                  BookName="Web  Classes  From  Scratch" <br>                  ID="Bookcounter7"  /><br>  <br>                    &lt;OReilly:BookCounter <br>                  Runat="server" <br>                  BookName="XML  Web  Documents  From  Srcatch" <br>                  ID="Bookcounter8"  /><br>  <br>              &lt;/OReilly:BookInquiryList> <br><br>&lt;/FORM> <br>    &lt;/body><br>&lt;/HTML></td></tr></table></ccid_nobr><p >    在上面的代码中需要注意的是,BookInquiryList组件中包含许多BookCounter元素,其中有一个BookCounter元素是对应着我希望记录的书籍。这个控件非常灵活,我可以对任意数量的书进行记录。每个BookCounter元素有一个用来显示被记录书籍名字的BookName属性。<p >    从图9中我们可以看到,每本书都由一个CountedButton定制控件进行记录,但.aspx文件中没有CountedButton控件的定义,它被完整地封装在了BookCounter定制控件中。<p >整个体系结构如下所示:<p ><ccid_nobr><ol><li>BookInquiry利便控件是由WebControl派生的,实现了INamingContainer,在下面我们会提到。        </li><li>BookInquiry控件有一个由Control类派生的Controls特性。</li><li>在控件集合中有数量不等的BookCounter控件。</li><li>BookCounter本身也是一个由WebControl派生得来的复合控件,WebControl也实现了INamingContainer。</li><ol><li>BookContainer的每个实例有二个特性:BookName和Count。</li><li>Name特性是由Viewstate支持的,而且通过.aspx文件中的BookName BookName初始化。</li><li>Count特性授权给private性质的CountedButton对象</li></ol></ol></ccid_nobr><p >    使用BookInquiry对象的目的有二个:它是BookCounter对象的容器;它负责绘制它本身并确保它包含的BookCounter对象能够按需求绘制自己。<p ><ccid_nobr><strong>CountedButton控件的修改</strong></ccid_nobr><p >    我们需要对CountedButton控件进行一些很小的修改,下面分别是C#和VB.NET版的CountedButton控件。<p >修改后的CountedButton.cs文件<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>using  System;<br>  using  System.Web.UI;<br>  using  System.Web.UI.WebControls;<br>  using  System.ComponentModel;<br>  <br>  namespace  CustomControls<br>  {<br>        //  由System.Web.UI.WebControls.Button派生出的定制控件<br>        public  class  CountedButton  :  System.Web.UI.WebControls.Button<br>        {<br>  <br>              private  string  displayString;<br>  <br>              //  缺省的构造器<br>              public  CountedButton(    )<br>              {<br>                    displayString  =  "clicks";<br>                    InitValues(    );<br>              }<br>  <br>              //  重载,显示字符串<br>              public  CountedButton(string  displayString)<br>              {<br>                    this.displayString  =  displayString;<br>                    InitValues(    );<br>              }<br>  <br>              //  由构造器调用的函数<br>              private  void  InitValues(    )<br>              {<br>                    if  (ViewState["Count"]  ==  null)<br>                          ViewState["Count"]  =  0;<br>                    this.Text  =  "Click  me";<br>              }<br>  <br>              //  Count是ViewState中的一个特性<br>              public  int  Count <br>            {<br>                  get<br>                  {<br>                        //  在构造器中初始化,不能是NULL<br>                          return  (int)  ViewState["Count"];<br>                    }<br>  <br>                    set<br>                    {<br>                          ViewState["Count"]  =  value;<br>                    }<br>              }<br>  <br>              //  覆盖OnClick事件处理程序,增大Count变量的值,并在更新按钮上的文本后调用基础类中的方法<br>              protected  override  void  OnClick(EventArgs  e)<br>              {<br>                    ViewState["Count"]  =    ((int)ViewState["Count"])  +  1;<br>                    this.Text  =  ViewState["Count"]  +  "  "  +  displayString;<br>                    base.OnClick(e);<br>              }<br>        }<br>  }    </td></tr></table></ccid_nobr><p >修改后的CountedButton.vb文件<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>Imports  System.ComponentModel<br>  Imports  System.Web.UI<br>  Imports  System.Web.UI.WebControls<br>  <br>  '  从System.Web.UI.WebControls.Button中派生的定制控件<br>  Public  Class  CountedButton<br>        Inherits  System.Web.UI.WebControls.Button<br>  <br>        Private  displayString  As  String<br>  <br>        '  构造器对ViewState进行初始化<br>        Public  Sub  New(    )<br>              displayString  =  "clicks"<br>              Init(    )<br>        End  Sub<br>  <br>      '  重载,显示字符串<br>        Public  Sub  New(ByVal  displayString  As  String)<br>              Me.displayString  =  displayString<br>              Init(    )<br>        End  Sub<br>  <br>        '  由构造器调用的方法<br>        Private  Shadows  Sub  Init(    )<br>              If  ViewState("Count")  =  Is  Nothing  Then<br>                    ViewState("Count")  =  0<br>                    Me.Text  =  "Click  me"<br>              End  If<br>        End  Sub<br>  <br>        '  Count是ViewState中的一个特性<br>        Public  Property  Count(    )  As  Integer<br>              Get<br>                    Return  CInt(ViewState("Count"))<br>              End  Get<br>              Set(ByVal  Value  As  Integer)<br>                    ViewState("Count")  =  Value<br>              End  Set<br>        End  Property<br>  <br>        '    覆盖OnClick事件处理程序,增大Count变量的值,并在更新按钮上的文本后调用基础类中的方法<br>        Protected  Overrides  Sub  OnClick(ByVal  e  As  EventArgs)<br>              ViewState("Count")  =  CInt(ViewState("Count"))  +  1<br>              Me.Text  =  CStr(ViewState("Count")  &amp;  "  "  &amp;  displayString<br>              MyBase.OnClick(e)<br>        End  Sub<br>  End  Class?)    </td></tr></table></ccid_nobr><p >    由于我们希望按钮上显示“5 Inquiries”而不是“5 clicks”字符串,因此必须修改OnClick方法中修改按钮字符串文本的代码:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     this.Text = ViewState["Count"] + " " + displayString;    </td></tr></table></ccid_nobr><p >    相应的VB.NET代码是:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     Me.Text = ViewState("Count") & " " & displayString    </td></tr></table></ccid_nobr><p >    我们还使用了一个private性质的成员变量displayString来存储传递给构造器中的数值:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     private string displayString;    </td></tr></table></ccid_nobr><p >在VB.NET中的代码为:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     Private displayString As String    </td></tr></table></ccid_nobr><p >    我们必须在构造器中设置这一字符串。为了保护已经使用了缺省的构造器的代码,我们必须重载构造器,新增加一个带有字符串参数的构造器:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     public  CountedButton(string  displayString)<br>    {<br>          this.displayString  =  displayString;<br>          Init(    );<br>    }<br>      </td></tr></table></ccid_nobr><p >在VB.NET中的代码是:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     Public  Sub  New(ByVal  displayString  As  String)<br>        Me.displayString  =  displayString<br>        Initialize(    )<br>  End  Sub<br>      </td></tr></table></ccid_nobr><p >    我们可以对缺省的构造器进行修改,将displayString成员变量有值设置为一个合理的缺省值。其C#代码如下:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     public  CountedButton(    )<br>  {<br>        displayString  =  "clicks";<br>        InitValues(    );<br>  }    </td></tr></table></ccid_nobr><p >相应的VB.NET代码是:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>ublic  Sub  New(    )<br>        displayString  =  "clicks"<br>        Init(    )<br>  End  Sub</p>      </td></tr></table></ccid_nobr><p >    二个构造器的代码都没有考虑private性质的辅助方法Init,它能够保证Count特性被初始化为0,并设置最初时按钮显示的字符串:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     private  void  Init(    )<br>  {<br>        if  (ViewState["Count"]  ==  null)<br>              ViewState["Count"]  =  0;<br>        this.Text  =  "Click  me";<br>  }    </td></tr></table></ccid_nobr><p >在VB.NET中,相应的代码为:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>rivate  Shadows  Sub  Init(    )<br>        If  ViewState("Count")  =  Nothing  Then<br>              ViewState("Count")  =  0<br>              Me.Text  =  "Click  me"<br>        End  If<br>  End  Sub    </td></tr></table></ccid_nobr><p >    作了上述的修改后,我们就可以在第一个复合控件━━BookCounter中使用CountedButton了。<p ><ccid_nobr><strong>BookCounter复合控件的创建</strong></ccid_nobr><p >    BookCounter复合控件用于记录和显示对某一本书查询的次数,下面分别是C#和VB.NET版的BookCounter复合控件的源代码:<p >C#版本的BookCounter控件源文件:BookCounter.cs <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>using  System;<br>  using  System.Web.UI;<br>  using  System.Web.UI.WebControls;<br>  using  System.ComponentModel;<br>  <br>  namespace  CustomControls<br>  {<br>        public  class  BookCounter  : <br>            System.Web.UI.WebControls.WebControl, <br>            INamingContainer<br>      {<br><br>            //  初始化按钮成员<br>              CountedButton  btn  =  new  CountedButton("inquiries");<br>  <br>              public  string  BookName <br>            {<br>                  get<br>                  {<br>                        return  (string)  ViewState["BookName"];<br>                    }<br>  <br>                    set<br>                    {<br>                          ViewState["BookName"]  =  value;<br>                    }<br>              }<br>  <br>              public  int  Count<br>              {<br>                    get<br>                    {<br>                          return  btn.Count;<br>                    }<br>                    set<br>                    {<br>                          btn.Count  =  value;<br>                    }<br>              }<br>  <br>              public  void  Reset(    )<br>              {<br>                    btn.Count  =  0;<br>              }<br>  <br>              protected  override  void  CreateChildControls(    )<br>              {<br>                    Controls.Add(btn);<br>              }<br>        }<br>  }     </td></tr></table></ccid_nobr><p >VB.NET版的BookCounter控件的源代码: BookCounter.vb <p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>Imports  System<br>  Imports  System.Web.UI<br>  Imports  System.Web.UI.WebControls<br>  Imports  System.ComponentModel<br>  <br>  Public  Class  BookCounter<br>        Inherits  System.Web.UI.WebControls.WebControl<br>        Implements  INamingContainer<br>  <br>        '  初始化按钮成员<br>        Public  btn  As  CountedButton  =  New  CountedButton("inquiries")<br>  <br>        Public  Property  BookName(    )  As  String<br>              Get<br>                    Return  CStr(ViewState("BookName"))<br>              End  Get<br>              Set(ByVal  Value  As  String)<br>                    ViewState("BookName")  =  Value<br>              End  Set<br>        End  Property<br>  <br>        Public  Property  Count(    )  As  Integer<br>              Get<br>                    Return  btn.Count<br>              End  Get<br>              Set(ByVal  Value  As  Integer)<br>                    btn.Count  =  Value<br>              End  Set<br>        End  Property<br>  <br>        Public  Sub  Reset(    )<br>              btn.Count  =  0<br>        End  Sub<br>  <br>        Protected  Overrides  Sub  CreateChildControls(    )<br>              Controls.Add(btn)<br>        End  Sub<br>  <br>  End  Class<br>      </td></tr></table></ccid_nobr><p ><ccid_nobr><strong>INamingContainer</strong></ccid_nobr><p >    BookCounter类中首先需要注意的是它实现了INamingContainer界面,这是一个没有方法的“记分器”界面。这一界面的目的是识别创建新的ID名字空间的容器控件,保证所有的子控件都有对于应用程序是唯一的ID。<p ><ccid_nobr><strong>包含CountedButton</strong></ccid_nobr><p >    BookCounter类包含有CountedButton的一个实例:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     CountedButton btn = new CountedButton("inquiries");    </td></tr></table></ccid_nobr><p >或:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     Public btn As CountedButton = New CountedButton("inquiries")    </td></tr></table></ccid_nobr><p >btn成员是在由System.Control继承的CreateChildControls方法中被初始化的:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     protected  override  void  CreateChildControls(    )<br>  {<br>        Controls.Add(btn);<br>  }<br>      </td></tr></table></ccid_nobr><p >在VB.NET中,相应的代码为:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>rotected  Overrides  Sub  CreateChildControls(    )<br>        Controls.Add(btn)<br>  End  Sub    </td></tr></table></ccid_nobr><p >    CreateChildControls是在绘制的准备工作时被调用的,它使BookCounter类能够添加btn对象作为被包含的控件。<p >    BookCounter无需覆盖Render方法,它唯一需要绘制的是CountedButton。Render缺省的操作是绘制所有的子控件,因此无需对它作任何修改就能完成其功能。<p >    BookCounter还有二个特性:BookName和Count。BookName是在控件中显示的一个字符串,而且通过ViewState进行管理,其C#代码如下所示:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>public  string  BookName <br>{<br>        get<br>        {<br>                return  (string)  ViewState["BookName"];<br>          }<br>  <br>          set<br>          {<br>                  ViewState["BookName"]  =  value;<br>          }<br>  }    </td></tr></table></ccid_nobr><p >相应的VB.NET源代码为:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>ublic  Property  BookName(    )  As  String<br>        Get<br>              Return  CStr(ViewState("BookName"))<br>        End  Get<br>        Set(ByVal  Value  As  String)<br>              ViewState("BookName")  =  Value<br>        End  Set<br>  End  Property    </td></tr></table></ccid_nobr><p >    Count是对一本特定的书查询的数量,记录这一数量的任务由CountedButton完成。其C#代码如下所示:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     public  int  Count<br>  {<br>        get<br>        {<br>              return  btn.Count;<br>        }<br>        set<br>        {<br>              btn.Count  =  value;<br>        }<br>  }    </td></tr></table></ccid_nobr><p >相应的VB.NET源代码为:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>ublic  Property  Count(    )  As  Integer<br>        Get<br>              Return  btn.Count<br>        End  Get<br>        Set(ByVal  Value  As  Integer)<br>              btn.Count  =  Value<br>        End  Set<br>  End  Property    </td></tr></table></ccid_nobr><p >    无需将该值放在ViewState中,因为按钮本身就可以对其数据进行管理。<p ><ccid_nobr><strong> BookInquiryList复合控件的创建</strong></ccid_nobr><p >    每个BookCounter对象都被包含在BookInquiryList控件集合中,该控件没有特性,只有一个Render方法,其C#和VB.NET代码如下所示:<p ><ccid_nobr><I> BookInquiryList的C#代码:</I></ccid_nobr><p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><br>[ControlBuilderAttribute(typeof(BookCounterBuilder)),ParseChildren(false)]<br>public  class  BookInquiryList  :  System.Web.UI.WebControls.WebControl,  INamingContainer<br>  {<br>  <br>        protected  override  void  Render(HtmlTextWriter  output)<br>        {<br>              int  totalInquiries  =  0;<br>              BookCounter  current;<br>  <br>              //  输出头部<br>              output.Write("&lt;Table  border='1'  width='90%'  cellpadding='1'"  +<br>                    "cellspacing='1'  align  =  'center'  >");<br>              output.Write("&lt;TR>&lt;TD  colspan  =  '2'  align='center'>");<br>              output.Write("&lt;B>  Inquiries  &lt;/B>&lt;/TD>&lt;/TR>");<br>  <br>              //  如果没有被包含的控件,输出相应的信息<br>              if  (Controls.Count  ==  0)<br>              {<br>                    output.Write("&lt;TR>&lt;TD  colspan  =  '2'>  align='center'");<br>                    output.Write("&lt;B>  No  books  listed  &lt;/B>&lt;/TD>&lt;/TR>"); <br>            }<br>            //  否则绘制每个包含的控件<br>              else<br>              {<br>                    //  遍历控件集,显示每个控件的书名,然后让每个被包含的控件自己绘制自己<br>                    for  (int  i  =  0;  i  &lt;  Controls.Count;  i++)<br>                    {<br>                          current  =  (BookCounter)  Controls;<br>                          totalInquiries  +=  current.Count;<br>                          output.Write("&lt;TR>&lt;TD  align='left'>"  +<br>                                current.BookName  +  "&lt;/TD>");<br>                          output.RenderBeginTag("TD");<br>                          current.RenderControl(output);<br>                          output.RenderEndTag(    ); <br>                        output.Write("&lt;/tr>");<br>                  }<br>                  output.Write("&lt;TR>&lt;TD  colspan='2'  align='center'>  "  +<br>                          "  Total  Inquiries:  "  +<br>                          totalInquiries  +  "&lt;/TD>&lt;/TR>");<br>              }<br>              output.Write("&lt;/TABLE>");<br>        }<br>  }    </td></tr></table></ccid_nobr><p ><ccid_nobr><I> BookInquiryList的VB.NET源代码:</I></ccid_nobr><p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     Imports  System.ComponentModel<br>  Imports  System.Web.UI<br>  <br>  &lt;ControlBuilder(GetType(BookCounterBuilder)),  ParseChildren(False)>  _<br>  Public  Class  BookInquiryList<br>        Inherits  System.Web.UI.WebControls.WebControl<br>        Implements  INamingContainer<br>  <br>        Protected  Overrides  Sub  Render(ByVal  output  As  HtmlTextWriter)<br>  <br>              Dim  totalInquiries  As  Integer  =  0<br>  <br>              '  输出头部<br>              output.Write("&lt;Table  border='1'  width='90%'  cellpadding='1'"  &amp;  _<br>                    "cellspacing='1'  align  =  'center'  >")<br>              output.Write("&lt;TR>&lt;TD  colspan  =  '2'  align='center'>")<br>              output.Write("&lt;B>  Inquiries  &lt;/B>&lt;/TD>&lt;/TR>")<br>  <br>              '  如果没有被包含的控件,输出相应的信息<br>              If  Controls.Count  =  0  Then<br>                      output.Write("&lt;TR>&lt;TD  colspan  =  '2'>  align='center'")<br>                      output.Write("&lt;B>  No  books  listed  &lt;/B>&lt;/TD>&lt;/TR>")<br>                      '  否则绘制每个包含的控件<br>              Else<br>                    '  遍历控件集,显示每个控件的书名,然后让每个被包含的控件自己绘制自己<br>                    Dim  current  As  BookCounter<br>  <br>                    For  Each  current  In  Controls<br>                          totalInquiries  +=  current.Count<br>                          output.Write("&lt;TR>&lt;TD  align='left'>"  &amp;  _<br>                                current.BookName  +  "&lt;/TD>")<br>                          output.RenderBeginTag("TD")<br>                          current.RenderControl(output)<br>                          output.RenderEndTag() <br>                        output.Write("&lt;/tr>")<br>                  Next<br>                  Dim  strTotalInquiries  As  String<br>                    strTotalInquiries  =  totalInquiries.ToString<br>                    output.Write("&lt;TR>&lt;TD  colspan='2'  align='center'>  "  &amp;  _<br>                          "  Total  Inquiries:  "  &amp;  _<br>                          CStr(strTotalInquiries)  &amp;  "&lt;/TD>&lt;/TR>")<br>              End  If<br>              output.Write("&lt;/TABLE>")<br>        End  Sub<br>  <br>  End  Class<br>  <br>  Friend  Class  BookCounterBuilder<br>        Inherits  ControlBuilder<br>  <br>        Public  Overrides  Function  GetChildControlType(  _<br>                    ByVal  tagName  As  String,  ByVal  attributes  As  IDictionary)  As  Type<br>              If  tagName  =  "BookCounter"  Then<br>                    Dim  x  As  BookCounter<br>                    Return  x.GetType<br>              Else<br>                    Return  Nothing<br>              End  If<br>        End  Function<br>  <br>        Public  Overrides  Sub  AppendLiteralString(ByVal  s  As  String)<br>        End  Sub<br>  <br>  End  Class    </td></tr></table></ccid_nobr><p ><ccid_nobr><strong>ControlBuilder和ParseChildren属性</strong></ccid_nobr><p >    BookCounter类必须与BookInquiryClass联合使用,ASP.NET才能将.aspx网页中的元素转换为适当的代码。完成这一工作需要用到ControlBuilder属性:<p >[ControlBuilderAttribute(typeof(BookCounterBuilder)),ParseChildren(false)]<p >    ControlBuilderAttribute的参数是一个通过传递BookCounterBuilder类获取的Type对象。下面是使用C#和VB.NET编写的BookCounterBuilder源代码:<p ><ccid_nobr><I>C#语言版的BookCounterBuilder </I></ccid_nobr><p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     internal  class  BookCounterBuilder  :  ControlBuilder<br>  {<br>        public  override  Type  GetChildControlType(<br>              string  tagName,  IDictionary  attributes)<br>        {<br>              if  (tagName  ==  "BookCounter")<br>                    return  typeof(BookCounter);<br>              else<br>                    return  null;<br>        }<br>  <br>        public  override  void  AppendLiteralString(string  s)<br>        {<br>        }<br>  }<br>  <br>  VB.NET版的BookCounterBuilder <br><br>Friend  Class  BookCounterBuilder<br>          Inherits  ControlBuilder<br>  <br>          Public  Overrides  Function  GetChildControlType(_<br>                  ByVal  tagName  As  String,  ByVal  attributes  As  Idictionary)  As  Type<br>              If  tagName  =  "BookCounter"  Then<br>                  Dim  x  As  BookCounter<br>                  Return  x.GetType<br>              Else<br>                  Return  Nothing<br>              End  If<br>          End  Function<br>  <br>          Public  Overrides  Sub  AppendLiteralString(ByVal  s  As  String)<br>          End  Sub<br>  <br>  End  Class    </td></tr></table></ccid_nobr><p >    ASP.NET将使用由ControlBuilder中派生出的BookCounterBuilder来判断由BookCounter标记批指示的对象的类型。通过这种结合,每个BookCounter对象才能被实例化,并添加到BookInquiryClass的控件集合中。<p >    第二个参数ParseChildren必须被设置成false,让ASP.NET知道我们已经对子属性进行了处理,无需再对它进行进一步地解析了。false值表明子属性不是外部对象的特性,而仅仅是子控件的特性。<p ><ccid_nobr><strong> Render</strong></ccid_nobr><p >    BookInquiryClass中的唯一方法是覆盖Render的方法,Render的作用是使用由每个BookCounter子控件管理的数据绘制图9中的表格。<p >    BookInquiryClass提供了一个总的查询数字,如下图所示:<p ><ccid_nobr><center><img  src="http://www.hh010.com/upload_files/article/244/9_tawivi15280.gif"></center></ccid_nobr><p >Figure 14-16. Total inquiries displayed <p >    通过将totalInquiries整型变量初始化为0,然后依次遍历每个控件,将其Count特性与totalInquiries相加,就可以得到查询的总数了。除了C#中语句结束时的分号为,实现这一功能的C#和VB.NET语句相同:<p >totalInquiries += current.Count;<p ><ccid_nobr><strong>表现输出内容</strong></ccid_nobr><p >    通过遍历每个控件,下面的代码能够绘制出每个子控件:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     for  (int  i  =  0;  i  &lt;  Controls.Count;  i++)<br>  {<br>        current  =  (BookCounter)  Controls;<br>        totalInquiries  +=  current.Count;<br>        output.Write("&lt;TR>&lt;TD  align='left'>"  +<br>              current.BookName  +  "&lt;/TD>");<br>        output.RenderBeginTag("TD");<br>        current.RenderControl(output);<br>        output.RenderEndTag(    ); <br>      output.Write("&lt;/tr>");<br>}    </td></tr></table></ccid_nobr><p >相应的VB.NET代码为:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     For  Each  current  in  Controls<br>        totalInquiries  +=  current.Count<br>        output.Write("&lt;TR>&lt;TD  align='left'>"  &amp;  _<br>              current.BookName  +  "&lt;/TD>")<br>        output.RenderBeginTag("TD")<br>        current.RenderControl(output)<br>        output.RenderEndTag(    ) <br>      output.Write("&lt;/tr>")<br>Next<br></td></tr></table></ccid_nobr><p >    局部的BookCounter对象型current变量的值被赋为控件集合中的每个对象:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     for  (int  i  =  0;  i  &lt;  Controls.Count;  i++)<br>  {<br>        current  =  (BookCounter)  Controls;<br>      </td></tr></table></ccid_nobr><p >然后就可以利用下面的代码计算totalInquiries:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     totalInquiries += current.Count;    </td></tr></table></ccid_nobr><p >    然后我们就可以继续绘制对象了。通过利用current的BookName特性,HtmlTextWriter可以用来创建一个行并显示书的名字:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code"><ccid_code>output.Write(&quot;&lt;TR&gt;&lt;TD align='left'&gt;&quot; +   current.BookName + &quot;&lt;/TD&gt;&quot;);</ccid_code></td></tr></table></ccid_nobr><p >    接下来,我们绘制一个TD标记,在该标记内我们让BookCounter对象绘制自己。最后,使用RenderEndTag来绘制一个结束性的TD标记,并使用HTMLTextWriter的Write方法绘制行结束标记。<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     output.RenderBeginTag("TD");<br>  current.RenderControl(output);<br>  output.RenderEndTag(    ); <br>  output.Write("&lt;/tr>");    </td></tr></table></ccid_nobr><p >下面的代码使被包含的控件自己绘制自己:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     current.RenderControl(output);    </td></tr></table></ccid_nobr><p >    上面的代码会调用BookCounter的Render方法。由于我们没有覆盖该方法,调用的仍然是基础类中的Render 类。唯一被包含的对象是CountedButton,由于我们没有覆盖CountedButton中的Render方法,在绘制该按钮时调用的仍然是基础类Button中的Render方法。<p ><ccid_nobr><strong> 查询总数的绘制</strong></ccid_nobr><p >    所有的子控件绘制完毕后,BookInquiryList将创建一个新的行,显示总的查询次数:<p ><ccid_nobr><table width="550" border="1" cellspacing="0" cellpadding="0" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code">     output.Write("&lt;TR>&lt;TD  colspan='2'  align='center'>  "  +<br>        "  Total  Inquiries:  "  + <br>      totalInquiries  +  "&lt;/TD>&lt;/TR>");    </td></tr></table></ccid_nobr><p >    至此,作为合同编程人员的你,就完成了任务。<p ><ccid_nobr><strong>结束语</strong></ccid_nobr><p >    在上面的文章中,我们讨论了开发定制控件的三种途径,尤其是通过一个例子,详细论述了建立复合控件的过程。在具体的工作中,灵活地运用这三种方式创建定制控件,使我们的开发工作事半功倍。<p ><p >(责任编辑 <ccid_nobr>赵正北</ccid_nobr>)                                 <p align="center"></p></p>
您需要登录后才可以回帖 登录 | 论坛注册

本版积分规则

QQ|Archiver|手机版|小黑屋|sitemap|鸿鹄论坛 ( 京ICP备14027439号 )  

GMT+8, 2025-4-5 00:53 , Processed in 0.104233 second(s), 24 queries , Redis On.  

  Powered by Discuz!

  © 2001-2025 HH010.COM

快速回复 返回顶部 返回列表