已經有好幾天沒有寫博客了,今天繼續,前幾天寫到了註冊自定義事件,今天咱們來說數據集綁定。 css
先把運行效果截個圖給你們看,讓你們內心也有個底。(你們要從第一章開始看起,咱們每一章都是接着前面沒作完的,一步步的完善)面試
在ASP.NET數據綁定控件分爲三種:數組
咱們這裏只講第3種數據綁定,使用時數據綁定方法代碼看起來可能以下所示安全
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { DataTable dt = new DataTable(); dt.Columns.Add("name"); dt.Columns.Add("id"); DataRow dr = dt.NewRow(); dr[0] = "張三1"; dr[1] = 0; dt.Rows.Add(dr);
DataRow dr1 = dt.NewRow(); dr1[0] = "張三2"; dr1[1] = 1; dt.Rows.Add(dr1); DataRow dr2 = dt.NewRow(); dr2[0] = "張三3"; dr2[1] = 2; dt.Rows.Add(dr2); DataRow dr3 = dt.NewRow(); dr3[0] = "張三4"; dr3[1] = 3; dt.Rows.Add(dr3); DropDwonCheckList1.DataTextField = "name"; DropDwonCheckList1.DataValueField = "id"; DropDwonCheckList1.DataSource = dt; DropDwonCheckList1.DataBind();
} }
<form id="form1" runat="server"> <XYB:DropDwonCheckList ID="DropDwonCheckList1" runat="server" /> </form>
咱們都知道DropDwonList,CheckBoxList等這些控件均可以綁定數據源,咱們先看看他們底層是什麼樣子的,服務器
public class DropDownList : ListControl, IPostBackDataHandler public class CheckBoxList : ListControl, IRepeatInfoUser, INamingContainer, IPostBackDataHandler public abstract class ListControl : DataBoundControl, IEditableTextControl, ITextControl
咱們能夠看到,DropDwonList,CheckBoxList都繼承了ListControl,而ListControl繼承了DataBoundControl類。咱們再來看看他們的Asp.net2.0提供的數據集綁定類的層次結構圖ide
因此咱們在原來項目的基礎上作修改,繼承DataBoundControl類,這個類裏面有咱們想要的DataSource和DataSourceID屬性,提供DataBind方法,而且只要咱們執行DataBind()方法時,自動執行綁定。函數
1.從新繼承DataBoundControl,代碼以下ui
public class DropDwonCheckList:DataBoundControl,IPostBackEventHandler,INamingContainer
IPostBackEventHandler 是用於處理點擊回發事件的接口,咱們在上一章的註冊自定義事件裏面詳細介紹過了。INamingContainer接口只爲解決控件ID命名衝突問題。
2.定義屬性this
[Description("要顯示的文本項"), Category("數據")] public string DataTextField { get { return ViewState["DataTextField"].ToString() ?? "name"; } set { ViewState["DataTextField"] = value; } } [Description("文本關聯值"), Category("數據")] public string DataValueField { get { return ViewState["DataValueField"].ToString() ?? "id"; } set { ViewState["DataValueField"] = value; } } [Description("下拉框的高度"),Category("下拉框")] public int DropDwonHeight { get { return Convert.ToInt32(ViewState["DropDwonHeight"] ?? 0); } set { ViewState["DropDwonHeight"] = value; } } [Description("下拉框的寬度"),Category("下拉框")] public int DropdwonWidth { get { return ViewState["DropdwonWidth"] == null ? 150 : Convert.ToInt32(ViewState["DropdwonWidth"]); } set { ViewState["DropdwonWidth"] = value; } }
3.實現IPostBackEventHandler 接口,我在這裏就直接把代碼粘出來了,這個不是咱們本章的重點,旨在讀者看到後,有個印象,起到複習的做用,最後你們不要忘記導火線,用這個導火線來產生事件回發。咱們只需爲控件在頁面類中註冊onclick事件。spa
Control.Attributes["onclick"]=this.Page.GetPostBackEventReference(this);//引起回發
#region 行爲與事件 private static readonly object EventClick = new object();//鍵值對象,指明點擊事件,名稱隨便取 [Description("點擊文本框時發生"), Category("Action")] public event EventHandler Click { add { base.Events.AddHandler(EventClick, value); } remove { base.Events.RemoveHandler(EventClick, value); } } protected virtual void OnClick(EventArgs e) { EventHandler handler = (EventHandler)base.Events[EventClick]; if (handler != null) { handler(this, e); } } #endregion void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) { this.OnClick(new EventArgs()); }
4.定義數據項。
咱們都知道DropDwonList控件的數據項是ListItem,固然咱們DropDwonCheckList控件(咱們最終要實現的控件) 也要定義一個數據項。新建一個類,命名爲CheckDataItem,由於咱們要保存CheckDataItem的數據狀態,因此咱們還需繼承一個IStateManager接口,ViewState不能很好的保存一個對象,IStateManager 是保存對象狀態的最佳選擇,代碼以下。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.UI.WebControls; using System.Web.UI; using System.Runtime; namespace XYB.Controls { public class CheckDataItem:IStateManager { #region 字段與屬性 private bool _mark; /// <summary> /// 要顯示的文本項 /// </summary> public string ItemText { get; set; } /// <summary> /// 文本關聯值 /// </summary> public string ItemValue { get; set; } /// <summary> /// 該數據項是否被選中 /// </summary> public bool Checked { get; set; } #endregion #region 構造函數 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public CheckDataItem() : this(null, null) { } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public CheckDataItem(string text) : this(text, null) { } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public CheckDataItem(string text, string value) : this(text, value, false) { } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public CheckDataItem(string text, string value, bool isChecked) { this.ItemText = text; this.ItemValue = value; this.Checked = isChecked; } #endregion /// <summary> /// 指示服務器控件是否正在發生改變 /// </summary> public bool IsTrackingViewState { get { return _mark; } } /// <summary> /// 將服務器控件還原到保存前的視圖狀態 /// </summary> /// <param name="state"></param> public void LoadViewState(object state) { if (state != null) { Triplet t = state as Triplet; ItemText = t.First.ToString(); ItemValue = t.Second.ToString(); Checked = Convert.ToBoolean(t.Third); } } /// <summary> /// 將服務器控件的視圖狀態保存到 Object /// </summary> /// <returns></returns> public object SaveViewState() { return new Triplet(ItemText, ItemValue, Checked); } /// <summary> /// 指示服務器控跟蹤其視圖狀態更改 /// </summary> public void TrackViewState() { _mark = true; } } }
Triplet 類是用於保存三個相關聯的Object對象,多個對象用數組來保存,LoadViewState是還原狀態,先將其強制成Triplet對象,而後再從Triplet三個對象中取值,達到還原的目的,SaveViewState保存狀態,咱們直接返回一個Triplet對象。記住這三個關聯對象順序要一致。
5.定義數據集合。
ListItem保存在ListItemCollection中,因而咱們的CheckDataItem也須要保存在CheckDataItemColllection中。新建一個類,命名爲CheckDataItemCollection。CheckDataItemCollection須要繼承一個Collection泛型集合,使用泛型的好處在咱們找工做的時候常常在面試題中提到,好處就是隻讓添加一種類型,安全性類型,減小裝箱拆箱所帶來的開銷(順便在這裏扯一下,嘿嘿)。CheckDataItemCollection只讓添加CheckDataItem這一種類型,同時咱們也還須要實現IStateManager接口,來保存這個集合。代碼以下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.Collections.ObjectModel; using System.Web.UI; namespace XYB.Controls { public class CheckDataItemCollection : Collection<CheckDataItem>, IStateManager { private bool marked; public CheckDataItemCollection() { marked = false; } public bool IsTrackingViewState { get { return this.marked; } } public void TrackViewState() { marked = true; } public void LoadViewState(object state) { if (state == null) return; Clear(); Triplet trip = (Triplet)state; string[] ItemTexts = (string[])trip.First; string[] ItemValues = (string[])trip.Second; bool[] ItemCheckeds = (bool[])trip.Third; for (int i = 0; i < ItemCheckeds.Length; i++) { Add(new CheckDataItem(ItemTexts[i], ItemValues[i], ItemCheckeds[i])); } } public object SaveViewState() { int num = Count; object[] ItemTexts = new string[num]; object[] ItemValues = new string[num]; bool[] ItemCheckeds = new bool[num]; for (int i = 0; i < num; i++) { ItemTexts[i] = Items[i].ItemText; ItemValues[i] = Items[i].ItemValue; ItemCheckeds[i] = Items[i].Checked; } return new Triplet(ItemTexts, ItemValues, ItemCheckeds); } } }
咱們把CheckDataItemCollection定義好了,回到DropDwonCheckList.cs文件中來,如今咱們來定義一個變量Items,它就是用來保存全部的CheckDataItem數據項。
private CheckDataItemCollection _items; public CheckDataItemCollection Items { get { if (_items == null) { _items = new CheckDataItemCollection(); } if (IsTrackingViewState) { _items.TrackViewState(); } return _items; } }
5.從數據源中讀取數據,並進行綁定。
咱們可能在疑惑,咱們傳值給DataSouce,而DataSouce是一個Object類型,並且它又是如何把值給Items的呢,DataBoundControl類爲咱們提供了一個方法PerformDataBinding,此方法根據映射從數據源迭代讀取數據,並在執行了DataBind方法後自動執行。咱們只須要重寫這個方法便可,並在這裏做一些手腳。
/// <summary> /// 重寫PerformDataBinding方法根據映射從數據源迭代讀取數據,此方法在數據綁定時執行 /// </summary> /// <param name="data"></param> protected override void PerformDataBinding(IEnumerable data) { base.PerformDataBinding(data); if (data != null) { foreach (object o in data) { CheckDataItem item = new CheckDataItem(); item.ItemText = DataBinder.GetPropertyValue(o, DataTextField, null); item.ItemValue = DataBinder.GetPropertyValue(o, DataValueField, null); item.Checked = false; Items.Add(item); } } }
該方法接收IEnumerable類型的參數用於迭帶訪問數據源中的數據,在讀取數據源中的數據時使用了DataBinder類,該類上有一個實用的GetPropertyValue方法,用於根據屬性反射的讀取數據源中的值,此處咱們使用該方法並傳遞了DataTextField和DataValueField以讀取文本和文本所關聯的值。
6.重寫LoadViewState,和SaveViewState方法將CheckDataItem數據項保存到視圖狀態中並可以正確的恢復。
protected override object SaveViewState() { return new Pair(base.SaveViewState(), Items.SaveViewState()); } protected override void LoadViewState(object savedState) { if (savedState != null) { Pair p = (Pair)savedState; base.LoadViewState(p.First); Items.LoadViewState(p.Second); } }
Pair類是用於保存兩個相關聯的Object對象。
7.呈現控件,大功生成。
咱們能夠把咱們的最終控件分紅四部分,文本框部分,頭部,內容部,和尾部。
#region 重寫呈現 protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); //若是文件已經被加載了,就不用重複加載 if (!this.Page.ClientScript.IsClientScriptIncludeRegistered(this.GetType(), "XYB.Controls.JS.dropDwon.js")) { #region 加載嵌入資源.css文件 //加載嵌入資源.css文件 string cssHref = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "XYB.Controls.CSS.dropDwon.css"); string cssLink = string.Format("<link href='{0}' rel='stylesheet' type='text/css' />", cssHref); LiteralControl litLink = new LiteralControl(cssLink); litLink.ID = "XYB_Controls_dropDwonCss"; this.Page.Header.Controls.Add(litLink); #endregion //加載嵌入資源.js文件 this.Page.ClientScript.RegisterClientScriptResource(this.GetType(), "XYB.Controls.JS.dropDwon.js"); //this.Attributes["onclick"] = "dropDwonClick()";//給文本框註冊點擊事件 //調用GetPostBackEventReference方法,產生頁面回發。 //this.Attributes["onclick"] = this.Page.GetPostBackEventReference(this); } } protected override void Render(HtmlTextWriter writer) { HtmlInputText txt = new HtmlInputText(); txt.Style.Add(HtmlTextWriterStyle.Width, DropdwonWidth + "px"); this.Controls.Add(txt); Panel pnlDropDown = new Panel(); //名字寫這麼長,只爲防止別人與個人控件ID相同 pnlDropDown.ID = "XYB_Controls_DropDownPanelID"; pnlDropDown.Style["height"] = "auto"; pnlDropDown.Width = DropdwonWidth; pnlDropDown.Style["border"] = "1px solid #ccc"; CreateControlHierarchy(pnlDropDown); this.Controls.Add(pnlDropDown); base.Render(writer); } /// <summary> /// 建立控件層次結構,頭部,內容部,尾部 /// </summary> /// <param name="dropDwonPanel"></param> private void CreateControlHierarchy(Panel dropDwonPanel) { CreateHeaderTemplate(dropDwonPanel); CreateContentTemplate(dropDwonPanel); CreateFooterTemplate(dropDwonPanel); } /// <summary> /// 建立頭部 /// </summary> /// <param name="dropDwonPanel"></param> private void CreateHeaderTemplate(Panel dropDwonPanel) { StringBuilder strHeaderHtml = new StringBuilder(); strHeaderHtml.Append("<div id=\"XYB_Controls_DropDownHeaderPanelID\">"); strHeaderHtml.Append("<input type='checkbox' id='XYB_Controls_SelectAllItemCheckBox' /><label>全選</label>"); strHeaderHtml.Append("</div>"); dropDwonPanel.Controls.Add(new LiteralControl(strHeaderHtml.ToString())); } /// <summary> /// 建立內容部 /// </summary> /// <param name="dropDwonPanel"></param> private void CreateContentTemplate(Panel dropDwonPanel) { if (Items.Count>0) { foreach (CheckDataItem item in Items) { Panel contentPanel = new Panel(); contentPanel.Style["width"] = "100%"; contentPanel.Style["text-align"] = "left"; contentPanel.Style["padding-left"] = "5px"; CheckBox cbx = new CheckBox(); cbx.Text = item.ItemText; contentPanel.Controls.Add(cbx); dropDwonPanel.Controls.Add(contentPanel); } } } /// <summary> /// 建立尾部 /// </summary> /// <param name="dropDwonPanel"></param> private void CreateFooterTemplate(Panel dropDwonPanel) { StringBuilder strFooterHtml = new StringBuilder(); strFooterHtml.Append("<div id='XYB_Controls_DropDwonFooterPanelID'>"); strFooterHtml.Append("<div id=\"XYB_Controls_DropDwonFooterLeftPanelID\">"); strFooterHtml.Append("<input type=\"button\" id=\"XYB_Controls_btnSure\" value=\"肯定\" />"); strFooterHtml.Append("</div>"); strFooterHtml.Append("<div id=\"XYB_Controls_DropDwonFooterRightPanelID\">"); strFooterHtml.Append("<input type=\"button\" id=\"XYB_Controls_btnCancel\" value=\"取消\" />"); strFooterHtml.Append("</div>"); strFooterHtml.Append("</div>"); dropDwonPanel.Controls.Add(new LiteralControl(strFooterHtml.ToString())); }
dropDwon.js代碼
function dropDwonClick() { $("XYB_Controls_DropDownPanelID").style.display = "block"; } function $(obj) { return document.getElementById(obj); }
dropDwon.css代碼
#XYB_Controls_DropDownPanelID{ border:1px solid #ccc; display:block; } #XYB_Controls_DropDownHeaderPanelID,#XYB_Controls_DropDownFooterPanelID{ height:20px; line-height:20px;width:100%; } #XYB_Controls_DropDwonFooterLeftPanelID,#XYB_Controls_DropDwonFooterRightPanelID{ width:50%;float:left;text-align:center; } #XYB_Controls_DropDownHeaderPanelID { border-bottom:1px solid #ccc; } #XYB_Controls_DropDwonFooterPanelID { padding-top:10px; height:30px; line-height:30px; }
上面的代碼就是我所有的頁面代碼。運行起來的效果就是摘要的截圖,下一章,全選與反選