Asp.net自定義控件開發任我行(8)-數據集綁定

  • 摘要

  已經有好幾天沒有寫博客了,今天繼續,前幾天寫到了註冊自定義事件,今天咱們來說數據集綁定。   css

  先把運行效果截個圖給你們看,讓你們內心也有個底。(你們要從第一章開始看起,咱們每一章都是接着前面沒作完的,一步步的完善)面試

  

  • 內容

  在ASP.NET數據綁定控件分爲三種:數組

    1. 簡單數據綁定:簡單數據綁定將一個對象與某個控件的屬性綁定在一塊兒。數據源只是綁定單個數據項,而不是綁定一個數據項列表。簡單數據綁定使用數據綁定表達式完成,數據綁定表達式是用<%#...%>封裝的任何可執行代碼。
    2. 列表控件:列表控件是經過一個固定不變的用戶界面顯示一個數據項列表的控件。常見的列表控件包含RadioButtonList控件、CheckBoxList控件和ASP.NET2.0中新引入的BulletedList控件。
    3. 複雜數據綁定:複雜數據綁定控件一般是顯示一組數據項的組合控件,它們有着靈活的呈現機制,例如GridView控件就是一個複雜數據綁定控件。

  咱們這裏只講第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;
   
    }

 

  • 下集預告

  上面的代碼就是我所有的頁面代碼。運行起來的效果就是摘要的截圖,下一章,全選與反選

相關文章
相關標籤/搜索