已经有好几天没有写博客了,今天继续,前几天写到了注册自定义事件,今天咱们来说数据集绑定。 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; }
上面的代码就是我所有的页面代码。运行起来的效果就是摘要的截图,下一章,全选与反选