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
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
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
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:
- 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.
- Borrar la colección Controls.
- Borrar la propiedad ViewState en los controles secundarios.
- Crear los controles secundarios utilizando el origen de datos.
- 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
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
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étodoCreateControlHierarchy
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étodoCreateControlHierarchy
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 propiedadDataItem
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étodoCreateItem
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.