您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)ASP.NET中怎么實現(xiàn)一個數(shù)據(jù)綁定控件,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
ASP.NET數(shù)據(jù)綁定控件一.回顧
如果你使用過ASP.NET內(nèi)置的數(shù)據(jù)控件(如DataList,Repeater),你一定會這么做
1.設置數(shù)據(jù)源 DataSource屬性
2.調(diào)用數(shù)據(jù)綁定 DataBind方法
3.在控件的不同模板內(nèi)使用綁定語法顯示數(shù)據(jù)
這三步應該是必須要做的
其他更多的
你可能需要對綁定的數(shù)據(jù)進行統(tǒng)一的一些操作(如時間格式化),或者對數(shù)據(jù)的某一項進行操作(對某一項進行格式化),或者需要觸發(fā)模板控件內(nèi)的一些事件(如databound事件).
根據(jù)上面的一些需求,我們需要這樣做
1.對綁定的數(shù)據(jù)進行統(tǒng)一的一些操作: 為數(shù)據(jù)綁定控件定義Item項(表示列表的一條數(shù)據(jù), 如Repeater的RepeaterItem)
2.對數(shù)據(jù)的某一項進行操作: 因為定義了Item項,那你肯定需要一個ItemCollection集合,其可以方便的為你檢索數(shù)據(jù)
3.因為定義了RepeaterItem,原先的EventArgs和CommandEventArgs已經(jīng)無法滿足需求,我們需要自定義委托及其一個為控件提供數(shù)據(jù)的的ItemEventArgs
上面三點有些并非必須定義,如第2點,還需要根據(jù)具體需求來定.但一個完成的控件是需要的.
ASP.NET數(shù)據(jù)綁定控件二.為數(shù)據(jù)控件做好準備
這次的demo為不完整的Datalist控件,來源還是MSDN的例子,我們命名為TemplatedList,此控件未定義ItemCollection集合
好了,根據(jù)上面的分析我們先為TemplatedList提供項和委托及為事件提供數(shù)據(jù)的幾個EventArgs,請看下面類圖
1.TemplatedListCommandEventArgs為Command事件提供數(shù)據(jù)
2.TemplatedListItemEventArgs為一般項提供數(shù)據(jù)
3.TemplatedListItem表示TemplatedList的項
ASP.NET數(shù)據(jù)綁定控件三.編寫TemplatedList
1.TemplatedList主要功能簡介
提供一個ItemTemplate模板屬性,提供三種不同項樣式,ItemCommand 事件冒泡事件及4個事件
2.實現(xiàn)主要步驟
以下為必須
(1)控件必須實現(xiàn) System.Web.UI.INamingContainer 接口
(2)定義至少一個模板屬性
(3)定義DataSource數(shù)據(jù)源屬性
(4)定義控件項DataItem,即模板的一個容器
(5)重寫DataBind 方法及復合控件相關(guān)方法(模板控件為特殊的復合控件)
當然還有其他額外的屬性,樣式,事件
3.具體實現(xiàn)
下面我們來具體看實現(xiàn)方法
(1)定義控件成員屬性
#region 靜態(tài)變量 private static readonly object EventSelectedIndexChanged = new object(); private static readonly object EventItemCreated = new object(); private static readonly object EventItemDataBound = new object(); private static readonly object EventItemCommand = new object(); #endregion 成員變量#region 成員變量 private IEnumerable dataSource; private TableItemStyle itemStyle; private TableItemStyle alternatingItemStyle; private TableItemStyle selectedItemStyle; private ITemplate itemTemplate; #endregion 控件屬性#region 控件屬性 [ Category("Style"), Description("交替項樣式"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), NotifyParentProperty(true), PersistenceMode(PersistenceMode.InnerProperty), ] public virtual TableItemStyle AlternatingItemStyle { get { if (alternatingItemStyle == null) { alternatingItemStyle = new TableItemStyle(); if (IsTrackingViewState) ((IStateManager)alternatingItemStyle).TrackViewState(); } return alternatingItemStyle; } } [ Category("Style"), Description("一般項樣式"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), NotifyParentProperty(true), PersistenceMode(PersistenceMode.InnerProperty), ] public virtual TableItemStyle ItemStyle { get { if (itemStyle == null) { itemStyle = new TableItemStyle(); if (IsTrackingViewState) ((IStateManager)itemStyle).TrackViewState(); } return itemStyle; } } [ Category("Style"), Description("選中項樣式"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), NotifyParentProperty(true), PersistenceMode(PersistenceMode.InnerProperty), ] public virtual TableItemStyle SelectedItemStyle { get { if (selectedItemStyle == null) { selectedItemStyle = new TableItemStyle(); if (IsTrackingViewState) ((IStateManager)selectedItemStyle).TrackViewState(); } return selectedItemStyle; } } [ Bindable(true), Category("Appearance"), DefaultValue(-1), Description("The cell padding of the rendered table.") ] public virtual int CellPadding { get { if (ControlStyleCreated == false) { return -1; } return ((TableStyle)ControlStyle).CellPadding; } set { ((TableStyle)ControlStyle).CellPadding = value; } } [ Bindable(true), Category("Appearance"), DefaultValue(0), Description("The cell spacing of the rendered table.") ] public virtual int CellSpacing { get { if (ControlStyleCreated == false) { return 0; } return ((TableStyle)ControlStyle).CellSpacing; } set { ((TableStyle)ControlStyle).CellSpacing = value; } } [ Bindable(true), Category("Appearance"), DefaultValue(GridLines.None), Description("The grid lines to be shown in the rendered table.") ] public virtual GridLines GridLines { get { if (ControlStyleCreated == false) { return GridLines.None; } return ((TableStyle)ControlStyle).GridLines; } set { ((TableStyle)ControlStyle).GridLines = value; } } [ Bindable(true), Category("Data"), DefaultValue(null), Description("數(shù)據(jù)源"), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public IEnumerable DataSource { get { return dataSource; } set { dataSource = value; } } [ Browsable(false), DefaultValue(null), Description("項模板"), PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(TemplatedListItem)) ] public virtual ITemplate ItemTemplate { get { return itemTemplate; } set { itemTemplate = value; } } [ Bindable(true), DefaultValue(-1), Description("選中項索引,默認為-1") ] public virtual int SelectedIndex { get { object o = ViewState["SelectedIndex"]; if (o != null) return (int)o; return -1; } set { if (value < -1) { throw new ArgumentOutOfRangeException(); } //獲取上次選中項 int oldSelectedIndex = SelectedIndex; ViewState["SelectedIndex"] = value; if (HasControls()) { Table table = (Table)Controls[0]; TemplatedListItem item; //第一次選中項不執(zhí)行 if ((oldSelectedIndex != -1) && (table.Rows.Count > oldSelectedIndex)) { item = (TemplatedListItem)table.Rows[oldSelectedIndex]; //判斷項類型,為了將選中項還原為數(shù)據(jù)項 if (item.ItemType != ListItemType.EditItem) { ListItemType itemType = ListItemType.Item; if (oldSelectedIndex % 2 != 0) itemType = ListItemType.AlternatingItem; item.SetItemType(itemType); } } //第一次執(zhí)行此項,并一直執(zhí)行 if ((value != -1) && (table.Rows.Count > value)) { item = (TemplatedListItem)table.Rows[value]; item.SetItemType(ListItemType.SelectedItem); } } } } #endregion
成員如下(可以看上面類圖)
1.三個項樣式和三個樣式屬性
2.公開DataSource數(shù)據(jù)源屬性,一個模板屬性
3.SelectedIndex索引屬性
前面的相信大家都很容易明白,其中的三個項樣式我們需要為其重寫視圖狀態(tài)管理,不熟悉可以看以前的隨筆,這里不再重復.
SelectedIndex屬性比較復雜,這里重點介紹此屬性
SelectedIndex索引屬性默認為-1,
我給出了注釋,在賦值前先記錄下了上次的選中項,為恢復樣式而做準備
//獲取上次選中項 int oldSelectedIndex = SelectedIndex; ViewState["SelectedIndex"] = value;
當?shù)谝淮胃腟electedIndex屬性時只執(zhí)行下列代碼(將此項標記為選中項),因為初始化時的沒有oldSelectedIndex,不需要恢復樣式
//第一次執(zhí)行此項,并一直執(zhí)行 if ((value != -1) && (table.Rows.Count > value)) { item = (TemplatedListItem)table.Rows[value]; item.SetItemType(ListItemType.SelectedItem); }
再次執(zhí)行時,恢復oldSelectedIndex選中項樣式
//第一次選中項不執(zhí)行 if ((oldSelectedIndex != -1) && (table.Rows.Count > oldSelectedIndex)) { item = (TemplatedListItem)table.Rows[oldSelectedIndex]; //判斷項類型,為了將選中項還原為數(shù)據(jù)項 if (item.ItemType != ListItemType.EditItem) { ListItemType itemType = ListItemType.Item; if (oldSelectedIndex % 2 != 0) itemType = ListItemType.AlternatingItem; item.SetItemType(itemType); } }
相信這樣的解釋你會明白
(2)定義控件成員事件
我們可以用上剛才我們聲明的委托了,即然你定義了這么多事件,就該為其安排觸發(fā)的先后.所以這個要特別注意,等下會再次提到.
#region 事件 protected virtual void OnItemCommand(TemplatedListCommandEventArgs e) { TemplatedListCommandEventHandler onItemCommandHandler = (TemplatedListCommandEventHandler)Events[EventItemCommand]; if (onItemCommandHandler != null) onItemCommandHandler(this, e); } protected virtual void OnItemCreated(TemplatedListItemEventArgs e) { TemplatedListItemEventHandler onItemCreatedHandler = (TemplatedListItemEventHandler)Events[EventItemCreated]; if (onItemCreatedHandler != null) onItemCreatedHandler(this, e); } protected virtual void OnItemDataBound(TemplatedListItemEventArgs e) { TemplatedListItemEventHandler onItemDataBoundHandler = (TemplatedListItemEventHandler)Events[EventItemDataBound]; if (onItemDataBoundHandler != null) onItemDataBoundHandler(this, e); } protected virtual void OnSelectedIndexChanged(EventArgs e) { EventHandler handler = (EventHandler)Events[EventSelectedIndexChanged]; if (handler != null) handler(this, e); } [ Category("Action"), Description("Raised when a CommandEvent occurs within an item.") ] public event TemplatedListCommandEventHandler ItemCommand { add { Events.AddHandler(EventItemCommand, value); } remove { Events.RemoveHandler(EventItemCommand, value); } } [ Category("Behavior"), Description("Raised when an item is created and is ready for customization.") ] public event TemplatedListItemEventHandler ItemCreated { add { Events.AddHandler(EventItemCreated, value); } remove { Events.RemoveHandler(EventItemCreated, value); } } [ Category("Behavior"), Description("Raised when an item is data-bound.") ] public event TemplatedListItemEventHandler ItemDataBound { add { Events.AddHandler(EventItemDataBound, value); } remove { Events.RemoveHandler(EventItemDataBound, value); } } [ Category("Action"), Description("Raised when the SelectedIndex property has changed.") ] public event EventHandler SelectedIndexChanged { add { Events.AddHandler(EventSelectedIndexChanged, value); } remove { Events.RemoveHandler(EventSelectedIndexChanged, value); } } #endregion
(3)關(guān)鍵實現(xiàn)
我們?yōu)榭丶峁┝诉@么多東西,剩下的事情就是要真正去實現(xiàn)功能了
1.重寫DataBind方法
當控件綁定數(shù)據(jù)時首先會執(zhí)行此方法觸發(fā)DataBinding事件
//控件執(zhí)行綁定時執(zhí)行 public override void DataBind() { base.OnDataBinding(EventArgs.Empty); //移除控件 Controls.Clear(); //清除視圖狀態(tài)信息 ClearChildViewState(); //創(chuàng)建一個帶或不帶指定數(shù)據(jù)源的控件層次結(jié)構(gòu) CreateControlHierarchy(true); ChildControlsCreated = true; TrackViewState(); }
2.CreateControlHierarchy方法
/**//// <summary> /// 創(chuàng)建一個帶或不帶指定數(shù)據(jù)源的控件層次結(jié)構(gòu) /// </summary> /// <param name="useDataSource">指示是否要使用指定的數(shù)據(jù)源</param> //注意:當?shù)诙螆?zhí)行數(shù)據(jù)綁定時,會執(zhí)行兩遍 private void CreateControlHierarchy(bool useDataSource) { IEnumerable dataSource = null; int count = -1; if (useDataSource == false) { // ViewState must have a non-null value for ItemCount because this is checked // by CreateChildControls. count = (int)ViewState["ItemCount"]; if (count != -1) { dataSource = new DummyDataSource(count); } } else { dataSource = this.dataSource; } //根據(jù)項類型開始創(chuàng)建子控件 if (dataSource != null) { Table table = new Table(); Controls.Add(table); //選中項索引 int selectedItemIndex = SelectedIndex; //項索引 int index = 0; //項數(shù)量 count = 0; foreach (object dataItem in dataSource) { ListItemType itemType = ListItemType.Item; if (index == selectedItemIndex) { itemType = ListItemType.SelectedItem; } else if (index % 2 != 0) { itemType = ListItemType.AlternatingItem; } //根據(jù)不同項索引創(chuàng)建樣式 CreateItem(table, index, itemType, useDataSource, dataItem); count++; index++; } } //執(zhí)行綁定時執(zhí)行時執(zhí)行 if (useDataSource) { //保存項數(shù)量 ViewState["ItemCount"] = ((dataSource != null) ? count : -1); } } //創(chuàng)建項 private TemplatedListItem CreateItem(Table table, int itemIndex, ListItemType itemType, bool dataBind, object dataItem) { TemplatedListItem item = new TemplatedListItem(itemIndex, itemType); TemplatedListItemEventArgs e = new TemplatedListItemEventArgs(item); if (itemTemplate != null) { itemTemplate.InstantiateIn(item.Cells[0]); } if (dataBind) { item.DataItem = dataItem; } //注意事件觸發(fā)順序 OnItemCreated(e); table.Rows.Add(item); if (dataBind) { item.DataBind(); OnItemDataBound(e); item.DataItem = null; } return item; }
CreateItem方法輔助用于創(chuàng)建項模板,此處注意事件觸發(fā)順序,上面已經(jīng)提到過
此方法根據(jù)項索引創(chuàng)建控件中不同的Item項 ,ViewState["ItemCount"]表示項的數(shù)量,第一次觸發(fā)時或者重新執(zhí)行DataBind方法時方法參數(shù)為true,并在初始化以后(回發(fā)期間)CreateChildControls方法會調(diào)用此方法,其參數(shù)為false
數(shù)據(jù)源不再是實際的數(shù)據(jù)源,而是新定義的DummyDataSource,其主要實現(xiàn)了一個迭代
internal sealed class DummyDataSource : ICollection { private int dataItemCount; public DummyDataSource(int dataItemCount) { this.dataItemCount = dataItemCount; } public int Count { get { return dataItemCount; } } public bool IsReadOnly { get { return false; } } public bool IsSynchronized { get { return false; } } public object SyncRoot { get { return this; } } public void CopyTo(Array array, int index) { for (IEnumerator e = this.GetEnumerator(); e.MoveNext(); ) array.SetValue(e.Current, index++); } public IEnumerator GetEnumerator() { return new DummyDataSourceEnumerator(dataItemCount); } private class DummyDataSourceEnumerator : IEnumerator { private int count; private int index; public DummyDataSourceEnumerator(int count) { this.count = count; this.index = -1; } public object Current { get { return null; } } public bool MoveNext() { index++; return index < count; } public void Reset() { this.index = -1; } } }
原因很明顯,為了減少對數(shù)據(jù)源的訪問,所以我們平時操作數(shù)據(jù)的時候,必須重新執(zhí)行DataBind方法,原因就在此
好了,到了這里差不多主要的事情我們已經(jīng)完成.接著把剩下的也完成
3.呈現(xiàn)
又到了Render方法這里了
此方法體只要執(zhí)行了PrepareControlHierarchy方法,不同的方法做不同的事情,CreateControlHierarchy方法根據(jù)索引值指定了不同的項,PrepareControlHierarchy則為不同項呈現(xiàn)不同的樣式效果
//為不同類型項加載樣式 private void PrepareControlHierarchy() { if (HasControls() == false) { return; } Debug.Assert(Controls[0] is Table); Table table = (Table)Controls[0]; table.CopyBaseAttributes(this); if (ControlStyleCreated) { table.ApplyStyle(ControlStyle); } // The composite alternating item style; do just one // merge style on the actual item. Style altItemStyle = null; if (alternatingItemStyle != null) { altItemStyle = new TableItemStyle(); altItemStyle.CopyFrom(itemStyle); altItemStyle.CopyFrom(alternatingItemStyle); } else { altItemStyle = itemStyle; } int rowCount = table.Rows.Count; for (int i = 0; i < rowCount; i++) { TemplatedListItem item = (TemplatedListItem)table.Rows[i]; Style compositeStyle = null; //根據(jù)不同項加載不同樣式 switch (item.ItemType) { case ListItemType.Item: compositeStyle = itemStyle; break; case ListItemType.AlternatingItem: compositeStyle = altItemStyle; break; case ListItemType.SelectedItem: { compositeStyle = new TableItemStyle(); if (item.ItemIndex % 2 != 0) compositeStyle.CopyFrom(altItemStyle); else compositeStyle.CopyFrom(itemStyle); compositeStyle.CopyFrom(selectedItemStyle); } break; } if (compositeStyle != null) { item.MergeStyle(compositeStyle); } } } //控件呈現(xiàn) protected override void Render(HtmlTextWriter writer) { // Apply styles to the control hierarchy // and then render it out. // Apply styles during render phase, so the user can change styles // after calling DataBind without the property changes ending // up in view state. PrepareControlHierarchy(); RenderContents(writer); }
終于差不多了,經(jīng)過這么多步驟,我們終于完成了,讓我們來使用控件,看一下效果
以上就是ASP.NET中怎么實現(xiàn)一個數(shù)據(jù)綁定控件,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學到更多知識。更多詳情敬請關(guān)注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。