asp.net控件開發基礎(19)

作者: Clingingboy  來源: 博客園  發布時間: 2010-10-02 19:47  閱讀: 1152 次  推薦: 0   原文鏈接   [收藏]  

  上兩篇討論了基本數據綁定控件的實現步驟,基本上我們按著步驟來就可以做出簡單的數據綁定控件了。過年前在看DataGrid的實現,本來想寫這個的,但2.0出了GridView了,再說表格控件實現比較復雜,所以先放著。我們一起打開MSDN來看點別的,當然主題還是離不開數據綁定控件。

  一.數據綁定控件的模板

  打開MSDN一看,我們會發現DataList和DataGrid都不是直接繼承自WebControl類的,而是繼承自一個叫BaseDataList的類。唯獨Repeater是直接繼承自WebControl類的,Repeater的簡單也就代表定義樣式的靈活。DataList和DataGrid則是規規矩矩的經過加工的列表控件。

  再看看BaseDataList,其是一個抽象類。其為數據列表控件提供了公共的列表樣式,屬性,布局。并定義了兩個抽象方法CreateControlHierarchy方法和PrepareControlHierarchy方法,留給子類實現,這兩個方法上兩篇,我們都認識過了。主要是因為定義了不同模板和樣式。可以說是一個典型的模板類。

  如果你也需要寫一個基于表格的數據綁定控件,可以跳過從WebControl繼承,優先考慮從BaseDataList開始。如果這個抽象類無法滿足需求,那你便放棄他。自己定義一個抽象類,定義公共的屬性,方法等,這樣對以后的擴展有利。當然一般情況下,我們的需求就夠用了。這里我們可以結合設計模式的學習得出的一個結論:把公用的成員抽象出來。說到這里,我們漏掉了一個數據綁定控件的一個大話題,列表綁定控件,DropDownList,ListBox,CheckBoxList等

  下面來看看Repeater版本的DropDownList

 
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
SelectCommand="SELECT top 3 [ProductID], [ProductName] FROM [Alphabetical list of products]">
</asp:SqlDataSource>
<asp:Repeater ID="Repeater1" runat="server" DataSourceID="SqlDataSource1">
<HeaderTemplate>
<select id="Select1">
</HeaderTemplate>
<ItemTemplate>
<option><%# Eval("ProductName")%></option>
</ItemTemplate>
<FooterTemplate>
</select>
</FooterTemplate>
</asp:Repeater>
<asp:DropDownList ID="DropDownList2"
DataTextField="ProductName"
runat="server" DataSourceID="SqlDataSource1">
</asp:DropDownList>
  其實現效果和DropDownList一模一樣。Repeater靈活,但這種做法并不優雅。列表控件也有一個抽象類ListControl。列表控件從此類派生。2.0新加了一個控件BulletedList.相信大家對這幾個控件是絕對的很熟悉,常與其打交道,我們就一起來看看他們是怎么實現的。

 

  System.Web.UI.WebControls.ListControl

  System.Web.UI.WebControls.BulletedList 

  System.Web.UI.WebControls.CheckBoxList 

  System.Web.UI.WebControls.DropDownList 

  System.Web.UI.WebControls.ListBox 

  System.Web.UI.WebControls.RadioButtonList

  二.列表綁定控件

  (1)抽象類ListControl及相關類

  像BaseDataList一樣ListControl也為列表控件提供的公共成員。根據我們的平時使用,列表控件都具有以下功能

  1.提供DataTextFormatString屬性,可以對綁定數據文本進行格式化

  2.提供數據源屬性DataSource和DataMember屬性

  3.提供DataTextField屬性和DataValueField屬性,分別為列表控件數據項提供列表顯示文本和值的數據源字段

  4.提供了ListItem,代表列表控件的數據項,此需要實現一個迭代,比數據綁定的做法更加靈活

  5.提供ListItemCollection,代表ListItem項集合

  6.提供SelectedIndex屬性和SelectedItem屬性進行索引

  7.提供SelectedIndexChanged事件并實現IEditableTextControl接口,實現TextChanged事件

  8.提供AutoPostBack屬性當用戶更改列表中的選定內容時可以向服務器自動回發

  其他還有2.0新增的一些功能,就別再介紹了,大家可以看看MSDN。做了上面這么多工作,接下來的工作就比較的輕松了。

  (2)具體子類控件

  根據功能的不同,可以把內置的5個控件歸為三類,為什么這么分,可以看看此類圖

  1.ListBox和DropDownList 

  2.CheckBoxList和RadioButtonList

  3.BulletedList

  這三類控件從ListControl派生,并根據自身功能的不同進行了一些調整

  第一類實現最簡單,ListControl本身為其默認實現了很多,其只需要根據自身需求,重寫幾個方法就可以了

  第二類控件為復合控件,其實現了IRepeatInfoUser接口,此接口任何重復項列表的列表控件實現的屬性和方法,大多為空實現,主要實現了RenderItem方法。其還定義了控件的布局和現實方法并直接重寫了Render方法,然后用RepeatInfo類來根據RepeatDirection的不同呈現項信息。

  第三類控件為新增控件,顯示一個項列表。

  要看出不同,則可以根據生成的html代碼進行比較

  (3)具體實現

  1.簡單實現一個DropDownList,可能就LoadPostData方法稍微復雜點,其他的應該都沒什么

 
public class CustomDropDownList : ListControl, IPostBackDataHandler
{

[DefaultValue(
0),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

public override int SelectedIndex
{

get
{
int selectedIndex = base.SelectedIndex;
if ((selectedIndex < 0) && (this.Items.Count > 0))
{

this.Items[0].Selected = true;
selectedIndex
= 0;
}

return selectedIndex;
}

set
{
base.SelectedIndex = value;
}
}



protected override void AddAttributesToRender(HtmlTextWriter writer)
{

string uniqueID = this.UniqueID;
if (uniqueID != null)
{
writer.AddAttribute(HtmlTextWriterAttribute.Name, uniqueID);
}

base.AddAttributesToRender(writer);
}


protected override ControlCollection CreateControlCollection()
{

base.CreateControlCollection();
}

IPostBackDataHandler 成員
}
2. 第二類控件比較復雜,如CheckBoxList是一個CheckBox項列表,其實現了IRepeatInfoUser接口,實現此接口的有如CheckBoxList、DataList、RadioButtonList。下面說明實現步驟
   public class CustomCheckBoxList: ListControl, IRepeatInfoUser,
                    INamingContainer, IPostBackDataHandler
    
{
   }
2.1 實現IRepeatInfoUser接口

 

  IRepeatInfoUser接口定義了重復項列表的列表控件實現的屬性和方法

  RenderItem方法用于呈現其中的一項信息。如下代碼

protected virtual void RenderItem(ListItemType itemType,
                    
int repeatIndex,
                    RepeatInfo repeatInfo,
                    HtmlTextWriter writer)
           
{
               ListItem item 
= Items[repeatIndex];
               check_box.Attributes.Clear();
               
if (item.Attributes.Count>0)
               
{
                   
foreach (string text in item.Attributes.Keys)
                   
{
                       
this.check_box.Attributes[text] = item.Attributes[text];
                   }

               }


               check_box.ID 
= repeatIndex.ToString(CultureInfo.InvariantCulture);
               check_box.Text 
= item.Text;
               check_box.Checked 
= item.Selected;
               check_box.TextAlign 
= TextAlign;
               check_box.Enabled 
= Enabled;
               check_box.RenderControl(writer);
           }
  2.2呈現

 

  CheckBoxList為復合控件,本該重寫TagKey屬性和CreateChildControls方法等,而是在構造函數中添加了CheckBox。.net提供了一個RepeatInfo的輔助類,其與實現IRepeatInfoUser接口的控件搭配使用,此類的RenderRepeater方法會調用CheckBoxList的RenderItem方法,然后根據控件的布局自上而下呈現項列表信息。要區分清楚RenderItem方法位呈現一條項信息,RenderRepeater方法是呈現列表信息。此實現過程在Render方法中實現,而非RenderContents方法.

           protected override void Render(HtmlTextWriter writer)
           
{

               RepeatInfo ri 
= new RepeatInfo();
               
//設置呈現布局
               ri.RepeatColumns = RepeatColumns;
               ri.RepeatDirection 
= RepeatDirection;
               ri.RepeatLayout 
= RepeatLayout;

               
short ti = 0;
               
if (TabIndex != 0)
               
{
                   check_box.TabIndex 
= TabIndex;
                   ti 
= TabIndex;
                   TabIndex 
= 0;
               }


               
//呈現項列表信息
               ri.RenderRepeater(writer, this, ControlStyle, this);

               
if (ti != 0)
                   TabIndex 
= ti;
           }
2.3預呈現

 

  將CheckBoxList中屬性賦給子控件,在呈現之前執行必要的預呈現

 
protected override void OnPreRender(EventArgs e)
{

base.OnPreRender(e);

check_box.AutoPostBack
= AutoPostBack;
check_box.CausesValidation
= CausesValidation;
check_box.ValidationGroup
= ValidationGroup;


//自動回傳
for (int i = 0; i < Items.Count; i++)
{

if (Items[i].Selected)
{
check_box.ID
= i.ToString(CultureInfo.InvariantCulture);
Page.RegisterRequiresPostBack(check_box);
}
}
}
2.4實現IPostBackDataHandler,當選中時,postCollection[postDataKey]為"on"
 
protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{

int checkbox = -1;

try
{
string id = postDataKey.Substring(ClientID.Length + 1);
if (Char.IsDigit(id[0]))
checkbox
= Int32.Parse(id, CultureInfo.InvariantCulture);
}

catch
{
return false;
}


if (checkbox == -1)
return false;

string val = postCollection[postDataKey];
bool ischecked = val == "on";
ListItem item
= Items[checkbox];

if (item.Selected != ischecked)
{
item.Selected
= ischecked;
return true;
}


return false;
}
到這里實現的就差不多了,BulletedList的實現就不再寫了。總之控件在不同生命周期完成了不同的事,一步一步的下來就成就了一個控件。

 

  在模板控件中使用的注意點: 記得我以前在用radiobuttonlist時,遇到過一個問題.我想在一個表格中實現一個很簡單的效果,如下圖

  剛開始我以為很簡單,把radiobutton放在Repeater里面,radiobutton的GroupName是跟著ID變的。卻忘了服務器控件進了Repeater模板里面其ID屬性就會重命名,這帶來了很多的不便。于是我想用radiobuttonlist,radiobuttonlist呈現后則為一個表格,不夠靈活,我就不得不重寫其布局。
更討厭的是由于radiobutton需要Text屬性,其不同于DropDownList(其實DropDownList和ListBox才算的上是名副其實的列表控件),所以無法將input作為父標簽,為了共享WebControl成員,只得多加個span標簽,其重寫了最后呈現如下

<span style="color:Red;"><input id="RadioButton1" type="radio" name="RadioButton1" value="RadioButton1" /><label for="RadioButton1">測試</label></span>
  雖然2.0中添加了InputAttributesLabelAttributes集合屬性,但name屬性已經定死了。或者就是再添加一個重復的name屬性,或者就是再重新寫一個?這個算不算是缺點? 感覺用起來就是不順心。 感覺越到下面問題越多了,如果有錯誤還請指出。這次主要學習下如何自定義列表控件,接著打算開始記錄下2.0新增的數據源控件如何實現。

 

上一篇:asp.net控件開發基礎(18)

下一篇:asp.net控件開發基礎(20)
0
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()