Share via


Desarrollar un control enlazado a datos a partir de plantillas

Resulta muy sencillo enlazar una propiedad de un control a un solo dato (o expresión) mediante la sintaxis de enlace de datos de ASP.NET. En esta sección se trata el escenario más complejo de desarrollo de un control con propiedades basadas en una plantilla y enlazado a un origen de datos con un tipo de colección (System.Collections.ICollection o System.Collections.IEnumerable). Las plantillas permiten al programador de páginas personalizar la presentación de los datos enlazados al control. Repeater y DataList son ejemplos de controles basados en una plantilla y enlazados a datos.

Para obtener información general acerca del enlace a datos en páginas ASP.NET, vea Tutorial rápido de ASP.NET —> Formularios Web Forms ASP.NET —> Enlazar controles de servidor a datos. Para obtener información básica sobre cómo crear un control a partir de una plantilla, vea Desarrollar un control a partir de plantillas.

Un control enlazado a datos y basado en una plantilla tiene una propiedad de origen de datos de tipo ICollection o IEnumerable, y una o varias propiedades de tipo ITemplate. El contenedor de una de las propiedades de plantilla define una propiedad (que generalmente se denomina DataItem) con la que se enlazan los datos. El control implementa su lógica de enlace a datos en el método Databind que hereda de Control. También reemplaza el método CreateChildControls para recrear la jerarquía de controles secundarios tras la devolución. A continuación se explican estos pasos con más detalle.

Para crear un control enlazado a datos a partir de una plantilla

  1. Defina un control que implemente la interfaz System.Web.UI.INamingContainer.

    public class TemplatedList : WebControl, INamingContainer {...}
    [Visual Basic]
    Public Class TemplatedList
       Inherits WebControl
       Implements INamingContainer
       ...
    End Class
    
  2. Defina una propiedad de tipo System.Web.UI.ITemplate.

    [TemplateContainer(typeof(TemplatedListItem))]
            public virtual ITemplate ItemTemplate {
                get {
                    return itemTemplate;
                }
                set {
                    itemTemplate = value;
                }
            }
    [Visual Basic]
    <TemplateContainer(GetType(TemplatedListItem))> _
    Public Overridable Property ItemTemplate() As ITemplate
       Get
          Return _itemTemplate
       End Get
       Set
          _itemTemplate = value
       End Set
    End Property
    

    El contenedor lógico de la plantilla (especificado en el atributo TemplateContainerAttribute) debe tener una propiedad a la que enlazar los datos. Por convención, esta propiedad se denomina DataItem. Para obtener información detallada acerca de los contenedores lógicos de las propiedades de plantilla, vea Desarrollar un control a partir de plantillas. En el ejemplo siguiente se define un contenedor para la propiedad de plantilla.

    public class TemplatedListItem : TableRow, INamingContainer {
            private object dataItem;
            public virtual object DataItem {
                get {
                    return dataItem;
                }
                set {
                    dataItem = value;
                }
    }
    [Visual Basic]
    Public Class TemplatedListItem
       Inherits TableRow
       Implements INamingContainer
       Private _dataItem As Object
       Public Overridable Property DataItem() As Object
          Get
             Return _dataItem
          End Get
          Set
             _dataItem = value
          End Set
       End Property
    End Class
    
  3. Reemplace el método DataBind (heredado de Control) para incluir la lógica de enlace a datos. Esta lógica debe contener los pasos siguientes:

    1. Invocar el método OnDataBinding de la clase base para llamar a los controladores (asociados por la página) que evalúan las expresiones de enlace a datos en el control.
    2. Borrar la colección Controls.
    3. Borrar la propiedad ViewState en los controles secundarios.
    4. Crear los controles secundarios utilizando el origen de datos.
    5. Señalizar al área de trabajo de la página ASP.NET que haga un seguimiento de la propiedad ViewState del control.

    En el código siguiente se siguen estos pasos.CreateChildControlsHierarchy es un método auxiliar para crear los controles secundarios. Para obtener información, vea el paso 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();
    }
    [Visual Basic]
    Public Overrides Sub DataBind()
       ' Controls with a data-source property perform their custom data 
       ' binding by overriding DataBind.
       ' Evaluate any data-binding expressions on the control itself.
       MyBase.OnDataBinding(EventArgs.Empty)
    
       ' Reset the control state.
       Controls.Clear()
       ClearChildViewState()
    
       '  Create the control hierarchy using the data source.
       CreateControlHierarchy(True)
       ChildControlsCreated = True
    
       TrackViewState()
    End Sub
    
  4. Reemplace CreateChildControls para recrear los controles secundarios en caso de devolución. Ello implica borrar la colección Controls y crear la jerarquía de controles con el estado de la vista, en lugar de hacerlo con el origen de datos. La creación real de los controles secundarios no aparece en el método CreateControlHierarchy descrito en el paso 5.

    protected override void CreateChildControls() {
        Controls.Clear();
    
        if (ViewState["ItemCount"] != null) {
        // Create the control hierarchy using the view state, 
        // not the data source.
        CreateControlHierarchy(false);
        }
    }
    [Visual Basic]
    Protected Overrides Sub CreateChildControls()
       Controls.Clear() 
       If Not (ViewState("ItemCount") Is Nothing) Then
          ' Create the control hierarchy using the view state, 
          ' not the data source.
          CreateControlHierarchy(False)
       End If
    End Sub
    
  5. Defina un origen de datos con elementos nulos y úselo en lugar del real al crear la jerarquía del control para devolver los datos. En los pasos 3 y 4 se crea la jerarquía del control con el origen de datos y con el estado de vista guardado, respectivamente. El origen de datos ficticio permite al control implementar una única ruta de acceso a código para los elementos comunes de los dos pasos.

    Nota   Este paso (el 5) describe los detalles de implementación que usan los controles ASP.NET enlazados a datos en .NET Framework. La clase DummyDataSource y el método CreateControlHierarchy mostrados en el fragmento de código siguiente no se encuentran en .NET Framework, sino que deben ser definidos por un programador de controles. No es indispensable implementar estos elementos, pero sí es recomendable aplicar esta técnica u otra similar con el fin de proporcionar una ruta de acceso a código común para crear la jerarquía del control.

    En el fragmento de código siguiente se define un origen de datos ficticio.

    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.
            }
        }
    [Visual Basic]
    NotInheritable Friend Class DummyDataSource
       Implements ICollection
    
       Private dataItemCount As Integer
    
       Public Sub New(dataItemCount As Integer)
          Me.dataItemCount = dataItemCount
       End Sub
    
       ' Implement other methods of the ICollection interface.
       ...
    
       Public Function GetEnumerator() As IEnumerator Implements ICollection.GetEnumerator
          Return New DummyDataSourceEnumerator(dataItemCount)
       End Function
    
       Private Class DummyDataSourceEnumerator
          Implements IEnumerator
    
          Private count As Integer
          Private index As Integer
    
          Public Sub New(count As Integer)
             Me.count = count
             Me.index = - 1
          End Sub
    
          Public ReadOnly Property Current() As Object Implements IEnumerator.Current
             Get
                Return Nothing
             End Get
          End Property
          ' Define other methods of the IEnumerator interface.
          ...
       End Class
    End Class
    

    DummyDataSource puede usarse para definir el método CreateControlHierarchy como se indica a continuación:

    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);
                }
            }
    
    [Visual Basic]
    Private Sub CreateControlHierarchy(useDataSource As Boolean)
       Dim dataSource As IEnumerable = Nothing
       Dim count As Integer = - 1
    
       If useDataSource = False Then
          ' ViewState must have a non-null value for ItemCount because this is checked 
          '  by CreateChildControls.
          count = CInt(ViewState("ItemCount"))
          If count <> - 1 Then
             dataSource = New DummyDataSource(count)
          End If
       Else
          dataSource = Me._dataSource
       End If
    
       If Not (dataSource Is Nothing) Then
          Dim table As New Table()
          Controls.Add(table)
    
          Dim selectedItemIndex As Integer = SelectedIndex
          Dim index As Integer = 0
    
          count = 0
          Dim dataItem As Object
          For Each dataItem In  dataSource
             Dim itemType As ListItemType = ListItemType.Item
             If index = selectedItemIndex Then
                itemType = ListItemType.SelectedItem
             Else
                If index Mod 2 <> 0 Then
                   itemType = ListItemType.AlternatingItem
                End If
             End If 
             CreateItem(table, index, itemType, useDataSource, dataItem)
             count += 1
             index += 1
          Next dataItem
       End If
    
       If useDataSource Then
          ' Save the number of items contained for use in round trips.
          If Not (dataSource Is Nothing) Then
             ViewState("ItemCount") = count
          Else
             ViewState("ItemCount") = -1
          End If
       End If
    End Sub
    

    El método CreateItem se encarga de crear en realidad la plantilla y enlazar la propiedad DataItem al origen de datos. En el fragmento de código siguiente se muestra cómo se implementa el método en Ejemplo de control enlazado a datos a partir de plantillas. Observe que el método CreateItem es un detalle de implementación y no está definido en .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;
            }
    [Visual Basic]
    Private Function CreateItem(table As Table, itemIndex As Integer, itemType As ListItemType, dataBind As Boolean, dataItem As Object) As TemplatedListItem
       Dim item As New TemplatedListItem(itemIndex, itemType)
       Dim e As New TemplatedListItemEventArgs(item)
    
       If Not (_itemTemplate Is Nothing) Then
          _itemTemplate.InstantiateIn(item.Cells(0))
       End If
       If dataBind Then
          item.DataItem = dataItem
       End If
       OnItemCreated(e)
       table.Rows.Add(item)
    
       If dataBind Then
          item.DataBind()
          OnItemDataBound(e)
    
          item.DataItem = Nothing
       End If
    
       Return item
    End Function
    

Para consultar un ejemplo de un control enlazado a datos que sigue los pasos expuestos en este tema, vea Ejemplo de control enlazado a datos a partir de plantillas.

Vea también

Ejemplo de control enlazado a datos a partir de plantillas