架构深渊

慢慢走进程序的深渊……关注领域驱动设计、测试驱动开发、设计模式、企业应用架构模式……积累技术细节,以设计架构为宗。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

开发模板化数据绑定控件(转)

Posted on 2009-03-09 18:12  chen eric  阅读(416)  评论(0编辑  收藏  举报
使用 ASP.NET 数据绑定语法很容易将控件的属性绑定到一个数据项(或表达式)。本节处理更复杂的控件开发方案,该控件具有绑定到是集合类型(System.Collections.ICollection 或 System.Collections.IEnumerable)的数据源的模板化属性。模板使页开发人员可以自定义绑定到控件的数据的表现形式。Repeater 和 DataList 控件是模板化数据绑定控件的示例。
有关 ASP.NET 页中数据绑定的概述,模板化数据绑定控件具有 ICollection 或 IEnumerable 类型的数据源属性以及 ITemplate 类型的一个或多个属性。这些模板属性的容器定义要将数据绑定到的属性(通常名为 DataItem)。控件在它从 Control 继承的 Databind 方法中实现其数据绑定逻辑。它在回发时重写 CreateChildControls 方法以重新创建子控件的层次结构。在以下讨论中将更为详细地解释这些步骤。


定义实现 System.Web.UI.INamingContainer 接口的控件。 
public class TemplatedList : WebControl, INamingContainer {}

定义 System.Web.UI.ITemplate 类型的属性。 
[TemplateContainer(
typeof(TemplatedListItem))]
        
public virtual ITemplate ItemTemplate {
            
get {
                
return itemTemplate;
            }
            
set {
                itemTemplate 
= value;
            }
        }

模板的逻辑容器(在 TemplateContainerAttribute 属性中指定)必须具有要将数据绑定到的属性。根据约定,该属性名为 DataItem。有关模板属性的逻辑容器的详细信息,请参见开发模板化控件。以下示例定义模板属性的容器。 
public class TemplatedListItem : TableRow, INamingContainer {
        
private object dataItem;
        
public virtual object DataItem {
            
get {
                
return dataItem;
            }
            
set {
                dataItem 
= value;
            }
}

重写 DataBind 方法(继承自 Control)以提供数据绑定逻辑。此操作必须包括以下步骤: 
调用基类的 OnDataBinding 方法以调用对您的控件计算数据绑定表达式的处理程序(由该页附加)。 
清除 Controls 集合。 
清除子控件的 ViewState。 
使用数据源创建子控件。 
向 ASP.NET 页框架发出信号以跟踪控件的 ViewState。 
以下代码将执行这些步骤。CreateChildControlsHierarchy 是用于实际执行创建子控件的工作的帮助器方法。有关详细信息,请参见步骤 
5。 
public override void DataBind() {
    
// Controls with a data-source property perform their 
    
// custom data binding by overriding DataBind to
    
// evaluate any data-binding expressions on the control    
    
// itself.
    base.OnDataBinding(EventArgs.Empty);

    
// Reset the control's state.
    Controls.Clear();
    ClearChildViewState();

    
// Create the control hierarchy using the data source.
    CreateControlHierarchy(true);
    ChildControlsCreated 
= true;

    TrackViewState();
}

重写 CreateChildControls 以在回发方案中重新创建子控件。这涉及清除 Controls 集合以及使用视图状态而不是数据源创建控件层次结构。实际创建子控件的工作隐藏在步骤 
5 中所描述的 CreateControlHierarchy 方法中。 
protected override void CreateChildControls() {
    Controls.Clear();

    
if (ViewState["ItemCount"!= null) {
    
// Create the control hierarchy using the view state, 
    
// not the data source.
    CreateControlHierarchy(false);
    }
}

定义一个具有空元素的数据源,并且在回发期间创建控件层次结构时使用该数据源而不是实际数据源。步骤 
3 和步骤 4 分别使用该数据源和保存的视图状态创建控件层次结构。虚拟数据源使控件能够为这两个步骤的公共元素实现单个代码路径。 
注意   该步骤(步骤 
5)描述由 .NET Framework 中数据绑定 ASP.NET 控件使用的实现信息。以下代码段中所示的 DummyDataSource 类和 CreateControlHierarchy 方法不在 .NET Framework 中,必须由控件开发人员定义它们。不要求您实现这些元素;但是,建议您使用此技术或类似技术为创建控件层次结构提供公共代码路径。
以下代码段定义虚拟数据源。

internal sealed class DummyDataSource : ICollection {

        
private int dataItemCount;

        
public DummyDataSource(int dataItemCount) {
            
this.dataItemCount = dataItemCount;
        }
// Implement other methods of the ICollection interface.

        
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;
                }
            }
// Define other methods of the IEnumerator interface.
        }
    }

DummyDataSource 可用于定义 CreateControlHierarchy 方法,如下所示。 
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;
            }

            
if (dataSource != null) {
                
int index = 0;
                count 
= 0;
                
foreach (object dataItem in dataSource) {

// Invoke a private helper method to create each item. 
                    CreateItem();
                    count
++;
                    index
++;
                }
            }

            
if (useDataSource) {
                
// Save the number of items contained for use in round trips.
                ViewState["ItemCount"= ((dataSource != null? count : -1);
            }
        }


CreateItem 方法执行创建模板并将 DataItem 属性绑定到数据源的实际工作。以下代码段说明如何在模板化数据绑定控件示例中实现 CreateItem 方法。请注意,CreateItem 方法是实现详细信息,并且没有在 .NET Framework 中定义。

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;
            }
            OnItemCreated(e);
            table.Rows.Add(item);

            
if (dataBind) {
                item.DataBind();
                OnItemDataBound(e);

                item.DataItem 
= null;
            }

            
return item;
        }