• 摘要

  已经有好几天没有写博客了,今天继续,前几天写到了注册自定义事件,今天我们来讲数据集绑定。   

  先把运行效果截个图给大家看,让大家心里也有个底。(大家要从第一章开始看起,我们每一章都是接着前面没做完的,一步步的完善)

  

  • 内容

  在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[] = "张三1";
dr[] = ;
dt.Rows.Add(dr);

DataRow dr1 = dt.NewRow();
dr1[] = "张三2";
dr1[] = ;
dt.Rows.Add(dr1); DataRow dr2 = dt.NewRow();
dr2[] = "张三3";
dr2[] = ;
dt.Rows.Add(dr2); DataRow dr3 = dt.NewRow();
dr3[] = "张三4";
dr3[] = ;
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提供的数据集绑定类的层次结构图


所以我们在原来项目的基础上做修改,继承DataBoundControl类,这个类里面有我们想要的DataSource和DataSourceID属性,提供DataBind方法,并且只要我们执行DataBind()方法时,自动执行绑定。

1.重新继承DataBoundControl,代码如下

public class DropDwonCheckList:DataBoundControl,IPostBackEventHandler,INamingContainer

IPostBackEventHandler 是用于处理点击回发事件的接口,我们在上一章的注册自定义事件里面详细介绍过了。INamingContainer接口只为解决控件ID命名冲突问题。
2.定义属性

[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"] ?? ); }
set { ViewState["DropDwonHeight"] = value; }
} [Description("下拉框的宽度"),Category("下拉框")]
public int DropdwonWidth
{
get { return ViewState["DropdwonWidth"] == null ? : Convert.ToInt32(ViewState["DropdwonWidth"]); }
set { ViewState["DropdwonWidth"] = value; }
}

3.实现IPostBackEventHandler 接口,我在这里就直接把代码粘出来了,这个不是我们本章的重点,旨在读者看到后,有个印象,起到复习的作用,最后大家不要忘记导火线,用这个导火线来产生事件回发。我们只需为控件在页面类中注册onclick事件。

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 = ; 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 = ; 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>)
{ 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:%;
} #XYB_Controls_DropDwonFooterLeftPanelID,#XYB_Controls_DropDwonFooterRightPanelID{
width:%;float:left;text-align:center;
}
#XYB_Controls_DropDownHeaderPanelID
{
border-bottom:1px solid #ccc; }
#XYB_Controls_DropDwonFooterPanelID
{
padding-top:10px;
height:30px;
line-height:30px; }
  • 下集预告

  上面的代码就是我全部的页面代码。运行起来的效果就是摘要的截图,下一章,全选与反选

  

  

    

Asp.net自定义控件开发任我行(8)-数据集绑定的更多相关文章

  1. Asp.net自定义控件开发任我行(附1)-属性一览众山小

    元数据属性应用于服务器控件及其成员,从而提供由设计工具.ASP.NET 页分析器.ASP.NET 运行库以及公共语言运行库使用的信息.当页开发人员在可视化设计器中使用控件时,设计时属性能改进开发人员的 ...

  2. Asp.net自定义控件开发任我行(4)-ViewState保存控件状态

    摘要 上一篇我们实现了下拉框的效果,此章的目的主要是保存控件属性状态 内容 我们先来看一个例子,后台代码不变,我们只改UI页面的代码,先在页面上拖放两个控件,一个是我们现在要开发的这个控件,另一个是按 ...

  3. Asp.net自定义控件开发任我行(2)-TagPrefix标签

    摘要 前面我们已经做了一个最简单的TextBox的马甲,此篇文章,我们来讲讲自定义控件的标签.大家可能看到了上一篇中拖放进来的代码是 <cc1:TextEdit ID="TextEdi ...

  4. Asp.net自定义控件开发任我行(7)-注册自定义事件

    摘要 前面我们已经把嵌入资源讲完了,不知道大家有没有得到收益,本章主要讲自定义事件,也就是给TextBox注册一个点击事件. 引言 不知道道上的朋友有没有注意到TextBox控件没有点击事件,就连网上 ...

  5. Asp.net自定义控件开发任我行(3)-Render

    摘要 上一篇我们讲到了自定义标签TagPrefix用法,此篇我们来讲一下控件的呈现,主要是呈现下拉框 内容 呈现的方法有,Render,RenderControl,RenderChildren,这三个 ...

  6. Asp.net自定义控件开发任我行(1)-笑傲江湖

    1.引言 参加工作5个月了,来到一家小公司,有几只老鸟带我,但不是我公司的,几个礼拜才来一次.来到公司做的第一个项目是web项目,里面有很多的重复代码,页面代码都是千篇一律,你这人也太水了吧,垃圾代码 ...

  7. Asp.net自定义控件开发任我行(5)-嵌入资源上

    摘要 上一篇我们讲了VitwState保存控件状态,此章我们来讲讲嵌入css文件,js文件,嵌入Image文件我也一笔带过. 内容 随着我的控件的完善,我们目标控件DropDwonCheckList最 ...

  8. Asp.net自定义控件开发任我行(6)-嵌入资源下

    摘要 上一章,我们讲了嵌入.css文件,这一章,我们来讲一下嵌入.js文件,也顺带一个嵌入Image文件 内容 我们前面的几章,一运行,下拉框就显示出来了,但是DropDwonList的下拉框是被隐藏 ...

  9. Asp.net 自定义控件开发相关的几种嵌入资源解决方案

    前提: 如下将要介绍的几种类型资源都要在其属性页窗口, 将 <生成操作> 属性, 设置为[嵌入的资源], 如图:   ► 给自定义控件添加自定义图标的几种方案   方法一: 直接在自定义控 ...

随机推荐

  1. Compaq Visual Fortran生成静态库的方法及使用

    Compaq Visual Fortran 6.5生成lib静态库详细方法: 打开Compaq Visual Fortran,新建Fortran Static Library工程,命名为ForLib: ...

  2. ArcSDE空间数据库中SDE用户使用探讨 (转载)

    ArcSDE作为空间数据库解决方案,应用非常广泛,本短文将尝试描述SDE的工作机制,简要说明空间数据库中SDE用户的使用方法.ArcSDE如何工作ArcSDE属于中间件技术,其本身并不能够存储空间数据 ...

  3. Remote System Explorer Operation在eclipse后台一直跑 解决办法

    在用eclipse开发时,经常遇到卡死的情况,其中一种就是右下角出现:“Remote System Explorer Operation”,解决方案如下: 第一步:Eclipse -> Pref ...

  4. 简单的RelativeLayout布局

    简单的RelativeLayout布局实例 <?xml version="1.0" encoding="utf-8"?> <RelativeL ...

  5. 查看mysql历史命令

    默认情况下操作mysql会在家目录下创建一个隐藏的mysql历史命令文件.mysql_history 在管理授权mysql账户时也会记录这些明文密码到这个文件,非常的不安全 [root@localho ...

  6. cms-登陆

    先介绍下登陆的思路: 1.在登陆页面首先前端验证用户名和密码是否正确,如果验证通过,则ajax的方式向后台提交数据. 2.在controller层,将得到的用户名名和密码封装进shiro的token, ...

  7. COGS 1453. [USACO NOV]空牛栏

    ★★   输入文件:empty.in   输出文件:empty.out   简单对比时间限制:1 s   内存限制:64 MB [题目描述] FJ建的新牛棚里有N(2<=N<=3,000, ...

  8. Django Form 表单

    Form 表单功能 生成HTML表单元素检查表单元素的合法性验证如果错误,重复显示表单数据类型转换 Form相关的对象 Widget 渲染成HTML元素的工具Field Form对象中的一个字段For ...

  9. 解决ssh登录慢,等待时间长的问题

    有时候在ssh远程登录到其他主机上时发现登录时间太长,经过亲自测试,发现主要有两个问题会导致ssh登录慢: 1.使用了dns反查,这样的话当ssh某个IP时,系统会试图通过DNS反查相对应的域名,如果 ...

  10. Java后台工程师的3次面试

    第一次面试 我面的是一个中小公司,在BOSS直聘上面找的,去之前看了看关于Java的一些基础知识,在牛客网上面看的,也做了一下牛客网的题目.然后跟HR约了一个时间就去面试了.因为第一次面试,一点经验都 ...