Creación de controles personalizados de ASP.NET Server personalizados de DataBound
Scott Mitchell
4GuysFromRolla.com
Marzo de 2004
Se aplica a:
Microsoft® ASP.NET
Resumen: Examina el uso de plantillas en controles web de entrada de datos y crea un control de servidor personalizado que muestra elementos de contenido sindicados mediante la especificación RSS. También examina los temas de preocupación al desarrollar controles de entrada de datos con plantilla, incluida la creación del contenido del control basado en datos externos, la generación de eventos durante la construcción del control y los eventos de propagación que se producen en las plantillas. (27 páginas impresas)
Descargue el código fuente de este artículo.
Contenido
Introducción
RssFeed: un control de servidor personalizado para mostrar fuentes de distribución RSS
Creación de un control DataBound sin plantilla
Agregar plantillas al control DataBound
Conclusión
Acerca del autor
Introducción
Aunque los controles de servidor de Microsoft® ASP.NET son capaces de mantener su estado entre postbacks y de generar eventos del lado servidor en respuesta a eventos del lado cliente, el objetivo principal y la tarea más importante para los controles de servidor es representación. La representación es el proceso de generar el marcado HTML correspondiente y es algo que hacen todos los controles de servidor. De hecho, el CÓDIGO HTML devuelto al explorador desde una página web de ASP.NET es simplemente la suma del marcado de los controles de la página.
El CÓDIGO HTML emitido por un control de servidor suele basarse en los valores de sus propiedades. De hecho, el CÓDIGO HTML emitido por la mayoría de los controles web se basa solo en sus valores de propiedad. Por ejemplo, el control TextBox Web siempre emitirá un elemento HTML <entrada>. Los atributos de este elemento pueden diferir en función de los valores de propiedad, pero no hay ninguna manera de que un desarrollador de páginas pueda cambiar radicalmente el CÓDIGO HTML emitido del TextBox.
Sin embargo, hay controles que permiten un grado de control mucho más fino sobre el HTML emitido. Los más comunes son los controles web de datos ( los DataGrid, DataListy Repeater, que usan plantillas de para permitir que el desarrollador de páginas personalice su HTML representado. Las plantillas pueden contener una combinación de sintaxis HTML, controles web y sintaxis de enlace de datos.
Al crear sus propios controles de servidor personalizados, es posible que quiera proporcionar a los desarrolladores de páginas una mayor flexibilidad para determinar la salida representada del control. En un artículo anterior, Building Templated Custom ASP.NET Server Controls, he examinado los conceptos básicos de los controles con plantilla y he examinado un ejemplo de creación de un control con plantilla sin datos. En este artículo, examinaremos la creación de controles con plantilla de entrada de datos, la creación de un control de servidor personalizado para mostrar el contenido sindicado a través de RSS.
Nota Los controles web de datos son ejemplos de controles con plantilla de entrada de datos, ya que su contenido se construye a partir de un origen de datos y usan plantillas para personalizar y representar su marcado. Los controles con plantilla y los controles de entrada de datos son ortogonales; es decir, puede tener un control con plantilla, no de entrada de datos o de entrada de datos, no con plantilla. Por ejemplo, el
DropDownList , RadioButtonList yCheckBoxList son ejemplos de controles de entrada de datos y no con plantilla. En Building Templated Custom ASP.NET Server Controls, he creado un control con plantilla sin datos que muestra las estadísticas del sitio web.
Nota RSS es una especificación para la sindicación de contenido mediante un formato XML. Para obtener más información sobre lo que es RSS y dónde se usa, consulte un artículo anterior sobre la mía, Creación de un agregador de noticias RSS con ASP.NET.
RssFeed: un control de servidor personalizado para mostrar fuentes de distribución RSS
Para obtener un conocimiento profundo del proceso (y problemas comunes) de crear un control con plantilla de entrada de datos, se recorrerá paso a paso la creación de un control de servidor personalizado que llamaré a RssFeed. RssFeed es un control con plantilla de entrada de datos que muestra una fuente de distribución RSS en una tabla HTML. Es muy fácil de usar; en su forma más sencilla, solo tiene que quitar el control en una página web de ASP.NET y, a continuación, en la clase de código subyacente, establecer su propiedad DataSource en la dirección URL de la fuente RSS y, a continuación, llamar al método DataBind().
private void Page_Load(object sender, System.EventArgs e) { if (!Page.IsPostBack) { RssFeed1.DataSource = "http://dotavery.com/blog/Rss.aspx"; RssFeed1.DataBind(); } }
En la figura 1 se muestra la salida de RssFeed cuando se usa en su forma más sencilla. La apariencia de RssFeed se puede mejorar a través de sus numerosas propiedades estilísticas. Al igual que los controles web de datos, RssFeed tiene estilos como ItemStyle, AlternatingItemStyle, HeaderStyle, etc. La figura 2 muestra una vista más estéticamente agradable de RssFeed.
Figura 1. Salida sencilla de RssFeed
Figura 2. RssFeed con estilo
RssFeed no requiere que se usen plantillas. Las figuras 1 y 2 muestran RssFeedel resultado cuando no se especifica una plantilla.
RssFeed tiene dos plantillas opcionales: ItemTemplate y HeaderTemplate. Estas plantillas se pueden usar para personalizar opcionalmente la apariencia de los elementos de encabezado y RSS. Cada elemento RSS tiene varias propiedades: Title, Link, Description, PubDate, etc. Los valores de estas propiedades se pueden emitir en una plantilla mediante la siguiente sintaxis de enlace de datos: <%# Container.DataItem.PropertyName %>
. Por ejemplo, el siguiente control RssFeed usa una plantilla para personalizar la presentación de las propiedades Title y PubDate:
<skm:RssFeed ...> <ItemTemplate> <strong><%# Container.DataItem.Title %></strong> <br> <i><%# Container.DataItem.PubDate.ToShortDateString() %></i> </ItemTemplate> </skm:Rss>
La salida generada por esta versión con plantilla se puede ver en la figura 3.
Figura 3. RssFeed después de aplicar una plantilla
El control RssFeed también proporciona tres eventos que los desarrolladores de páginas pueden usar para pulsar mediante programación en el control. Los nombres y la semántica de estos eventos son idénticos a tres eventos comunes en los controles web de datos:
- ItemCreated. Se activa a medida que se crea cada elemento RSS.
- ItemDataBound. Se activa una vez para cada elemento RSS después de que el elemento haya sido databound.
- ItemCommand. Se desencadena si se generó un evento Command
en la plantilla ItemTemplate de button deRssFeed . Esto puede ocurrir si un desarrollador de páginas agregó un, LinkButton oImageButton control web a la plantilla que tiene suCommandName oCommandArgu ment establecido. Cuando se hace clic en este botón, se devuelve la página web y se desencadena el evento RssFeedItemCommand.
En este artículo, verá los pasos que debe seguir, como desarrollador de controles, para crear un control de entrada de datos con plantilla. ¡Hay muchos temas de carne que cubrir! Comencemos con un vistazo a la creación de un control de entrada de datos sin plantilla y, a continuación, vamos a agregar compatibilidad con plantillas al control de entrada de datos.
Nota El código fuente completo del control RssFeed dese puede descargar mediante el vínculo al principio de este artículo. También hay un área de trabajo de GotDotNet con la versión más reciente del código, disponible en http://workspaces.gotdotnet.com/RssFeed. También hay una amplia documentación en línea para desarrolladores de páginas que usan RssFeedy un artículo, Un control de servidor personalizado de ASP.NET para mostrar fuentes RSS, que describe el uso de RssFeed en una página web de ASP.NET.
Creación de un control DataBound sin plantilla
Hay numerosos controles web integrados, no con plantilla, ASP.NET web de entrada de datos, como el
Los controles de entrada de datos son controles que proporcionan una propiedad DataSource que se puede asignar a algún conjunto de datos, junto con un método DataBind() que, cuando se invoca, enlaza el DataSource al control. Este proceso de enlace se realiza normalmente mediante la enumeración de los datos, agregando algún tipo de "elemento" para cada registro de datos. Por ejemplo, la propiedad DropDownList control web DataSource se puede establecer en los resultados de una consulta de base de datos. Al llamar al método
Por lo tanto, un aspecto común de los controles de entrada de datos es que son una composición de controles "item". EldataGrid de
En Building Templated Custom ASP.NET Server Controls, he analizado las diferencias entre los controles representados y los controles compuestos. Los controles representados son aquellos cuyo marcado HTML se genera mediante la generación manual del marcado HTML adecuado. Los controles compuestos son controles que contienen un conjunto de controles secundarios, y estos controles secundarios se delegan la responsabilidad de generar el marcado HTML. Dado que los controles de entrada de datos se componen de una serie de "elementos", donde cada elemento se agrega a la jerarquía de controles del control de entrada de datos, los controles de entrada de datos son controles compuestos.
Es importante tener un conocimiento firme de los controles compuestos y los métodos implicados en trabajar con controles compuestos. Si aún no lo ha hecho, dedique un momento a leer creación de controles personalizados personalizados de ASP.NET server, especialmente la sección "Controles representados y controles compuestos".
La creación de un control de entrada de datos implica los tres pasos siguientes:
- Cree una propiedad
DataSource. - Invalide el método
DataBind() y cree la jerarquía de controles. - Invalide el CreateChildControls() para compilar la jerarquía de controles.
Echemos un vistazo a cada una de estas tareas individualmente.
Creación de la propiedad DataSource
Al crear la propiedad DataSource, es importante decidir qué, precisamente, constituye los datos que se enlazarán al control. Para los controles web de datos, cualquier origen de datos que implemente IEnumerable o IListSource se puede enlazar al control. Los objetos que implementan IEnumerable incluyen matrices, los objetos de la System.Collections espacio de nombres y DataReaders, entre otros. El DataSet de
La especificación RSS detalla un formato XML para codificar datos sindicados. Por lo tanto, los datos que procesa RssFeed serán un archivo XML. Normalmente, RSS se usa para distribuir contenido en línea, desde sitios web de noticias o blogs. Por lo tanto, el desarrollador de páginas debe poder especificar una dirección URL remota como origen de los datos. Dado que es posible que los datos no sean remotos, pero en realidad un archivo local, la propiedad DataSource también debe aceptar objetos XmlReader, objetos TextReader o objetos XmlDocument. Para controlar esto, nuestra propiedad DataSource
object dataSource; public virtual object DataSource { get { return dataSource; } set { // make sure we're working with a string, XmlReader, or TextReader if (value == null || value is string || value is XmlReader || value is TextReader || value is XmlDocument) dataSource = value; else throw new ArgumentException("DataSource must be assigned a string, XmlReader, or TextReader."); } }
Observe que si el desarrollador de páginas intenta asignar un objeto a la dataSource de
Invalidación del método DataBind()
Después de que un desarrollador de páginas asigne algunos datos al DataSource, llamará al método DataBind() del control para enlazar los datos al control. El
A continuación, la jerarquía de controles debe borrarse y, a continuación, volver a generarse. La razón por la que debe borrarse es que el método CreateChildControls() puede haber ejecutado ya en este punto, que ya habría creado la jerarquía de controles. Después de volver a generar la jerarquía de controles, el último paso es establecer la propiedad ChildControlsCreated del control en True, de modo que las llamadas futuras a EnsureChildControls() no vuelvan a generar la jerarquía.
public override void DataBind() { base.OnDataBinding(EventArgs.Empty); // Create the control hierarchy. First, clear out the child controls Controls.Clear(); ClearChildViewState(); TrackViewState(); // Create the control hierarchy CreateControlHierarchy(true); // Mark the hierarchy as having been created ChildControlsCreated = true; }
El método
Creación de la jerarquía de controles
La clase Control
Se encuentra en el método CreateChildControls() y, a continuación, debe construir la jerarquía de controles. En lugar de tener toda esta lógica dentro de este método, vamos a crear un método personalizado(CreateControlHiearchy(), que realiza esta tarea. Por lo tanto, nuestro primer intento en el método CreateChildControls() tendría el siguiente aspecto:
protected override void CreateChildControls() { // Clear out the control hiearchy Controls.Clear(); // Build up the control hierachy CreateControlHierarchy(); }
Cada vez que se visita la página web de ASP.NET, el control RssFeed debe construir su jerarquía de controles. Como vimos anteriormente, el controlador de eventos Page_Load de la página web de ASP.NET llama al método RssFeed del control DataBind() en la primera carga. Pero imagine, por un momento, si esto no era el caso. El método CreateChildControls() de RssFeed seguirá ejecutándolo y el método CreateControlHiearchy() seguiría compilando la jerarquía de controles, aunque no lo quisiera necesariamente.
Por lo tanto, ¿significa que no necesita CreateChildControls() llamar a CreateControlHierarchy()? ¿Puede dejar que el método
Nota Normalmente, el método DataBind() de controles de entrada de datos solo se llama en la primera carga de página o cuando se produce algún evento que requiere que los datos se vuelvan a enlazar al control. Un ejemplo de llamada a DataBind() de nuevo en respuesta a algún evento sería cuando se usa un DataGrid ordenable. Si el usuario opta por ordenar los datos de una manera diferente, el DataGridde evento SortCommand se activa y, en el controlador de eventos, el desarrollador de páginas vuelve a ordenar los datos y, a continuación, lo vuelve a enlazar a la DataGrid.
CreateChildControls(), a continuación, solo debe llamar a CreateControlHierarchy() cuando se vuelva a publicar la página. En este escenario, CreateControlHierarchy() tendrá que reconstruir los elementos de ViewState del control. Sin embargo, si se llama a CreateControlHierarchy() desde el método DataBind(), debe construir sus elementos a partir de la DataSource. Para determinar si CreateControlHierarchy() debe construir la jerarquía a partir de la DataSource, CreateControlHierarchy() aceptará un valor booleano: True para crear la jerarquía a partir de la DataSource, False para crearla a partir de ViewState.
A continuación se muestra el método final
protected override void CreateChildControls() { // Clear out the control hiearchy Controls.Clear(); // see if we need to build up the hierarchy if (ViewState["RssItemCount"] != null) CreateControlHierarchy(false); }
Nota Recuperación de la sección anterior "Invalidación del método DataBind(), que el métodoDataBind() llama a CreateControlHierarchy() , pasando un valor de true, ya que cuando se invoca este método desdeDataBind() , el contenido del control debe generarse a partir delDataSource .
Creación del método CreateControlHierarchy()
El último, y el paso más importante, en la creación de la jerarquía de controles es escribir el método CreateControlHierarchy(), que realiza todas las tareas reales de creación de la jerarquía de controles.
Antes de examinar el código algo largo, vamos a analizar primero lo que hace este método en inglés. El método comienza por determinar si el dataSource debe usarse para construir la jerarquía de controles o si la jerarquía debe compilarse desde ViewState. Si se va a usar el dataSource de
Un RssItem es una representación abstracta de un elemento RSS. Por ejemplo, un sitio de noticias podría usar RSS para sindicar sus últimas noticias. Cada artículo se considera un elemento RSS. Según la especificación RSS, los elementos RSS tienen propiedades como Title, Link, Description, Author, Category, PubDate (la fecha en que se publicó el elemento), etc. Recuerde que el dataSource de
Si la estructura del control se va a reconstruir a partir de ViewState, se crea un origen de datos ficticio, es decir, una matriz de tipo objeto con RssItemCount número de elemento.
Recuerde de nuestras discusiones anteriores de este artículo que los controles de entrada de datos están formados por "elementos", que normalmente son controles web derivados de controles web que proporcionan el comportamiento de representación deseado. Por ejemplo, DataGrid se representa como una tabla de <HTML> con cada elemento de DataGrid representado como una fila de la tabla. No es sorprendente que la clase DataGridItem se derive de TableRow. El control RssFeed se representa de forma similar al control de DataGrid, como una tabla <HTML>. El control rssFeed de
El control RssFeed de
En el código siguiente se muestran las partes alemanas del método CreateControlHierarchy(). Se han omitido algunos bits para mayor brevedad.
protected virtual void CreateControlHierarchy(bool useDataSource) { IEnumerable rssData = null; // Clear out and/or create the rssItemsArrayList if (rssItemsArrayList == null) rssItemsArrayList = new ArrayList(); else rssItemsArrayList.Clear(); // Get the rssData bool isValidXml = true; if (useDataSource) { // get the proper dataSource //(based on if the DataSource is a URL, // file path, XmlReader, etc.) rssData = GetDataSource(); } else { // Create a dummy DataSource rssData = new object[(int) ViewState["RssItemCount"]]; rssItemsArrayList.Capacity = (int) ViewState["RssItemCount"]; } if (rssData != null) { // create a Table Table outerTable = new Table(); Controls.Add(outerTable); // Add a header TableRow headerRow = new TableRow(); TableCell headerCell = new TableCell(); headerCell.Text = this.HeaderText; // Add the cell and row to the row/table headerRow.Cells.Add(headerCell); outerTable.Rows.Add(headerRow); int itemCount = 0; foreach(RssItem item in rssData) { // Determine if this item is an Item or AlternatingItem RssFeedItemType itemType = RssFeedItemType.Item; if (itemCount % 2 == 1) itemType = RssFeedItemType.AlternatingItem; // Create the RssFeedItem RssFeedItem feedItem = CreateRssFeedItem(outerTable.Rows, itemType, item, useDataSource); this.rssItemsArrayList.Add(feedItem); itemCount++; } // Instantiate the RssItems collection this.rssItemsCollection = new RssFeedItemCollection(rssItemsArrayList); // set the RssItemCount ViewState variable if needed if (useDataSource) ViewState["RssItemCount"] = itemCount; } }
Tenga en cuenta que la jerarquía de controles RssFeed de
El propósito de
protected virtual RssFeedItem CreateRssFeedItem(TableRowCollection rows, RssFeedItemType itemType, RssItem item, bool useDataSource) { RssFeedItem feedItem = new RssFeedItem(itemType); RssFeedItemEventArgs e = new RssFeedItemEventArgs(feedItem); TableCell titleCell = new TableCell(); TableCell pubDateCell = new TableCell(); HyperLink lnkItem = new HyperLink(); lnkItem.Target = this.Target; titleCell.Controls.Add(lnkItem); feedItem.Cells.Add(titleCell); if (ShowPubDate) feedItem.Cells.Add(pubDateCell); OnItemCreated(e); // raise the ItemCreated event rows.Add(feedItem); if (useDataSource) { feedItem.DataItem = item; if (item.Link == String.Empty) titleCell.Text = item.Title; else { lnkItem.NavigateUrl = item.Link; lnkItem.Text = item.Title; titleCell.Controls.Add(lnkItem); } if (ShowPubDate) pubDateCell.Text = item.PubDate.ToString(this.DateFormatString); OnItemDataBound(e); // raise the ItemDataBound event } return feedItem; }
Con la conclusión de estos tres pasos: crear una propiedad DataSource, invalidar el método DataBind() y crear la jerarquía de controles a través de CreateChildControls(), tiene un control web de entrada de datos en funcionamiento.
Estilos en controles databound
Los controles DataGrid y DataList Web permiten a los desarrolladores de páginas adaptar fácilmente la apariencia de la salida mediante el uso de estilos . Estos incluyen estilos de nivel superior que se aplican a todo el control Web, así como estilos más específicos, como ItemStyle, AlternatingItemStyle, HeaderStyle, FooterStyle, etc. Para RssFeed, hay tres propiedades de estilo de destino: HeaderStyle, ItemStyley AlternatingItemStyle. Dado que RssFeed se representa como una tabla HTML, con su encabezado y elementos como filas en la tabla, estos tres estilos son de tipo TableItemStyle.
private TableItemStyle headerStyle = null; private TableItemStyle itemStyle = null; private TableItemStyle alternatingItemStyle = null; public virtual TableItemStyle HeaderStyle { get { if (headerStyle == null) headerStyle = new TableItemStyle(); return headerStyle; } } public virtual TableItemStyle ItemStyle { get { if (itemStyle == null) itemStyle = new TableItemStyle(); return itemStyle; } } public virtual TableItemStyle AlternatingItemStyle { get { if (alternatingItemStyle == null) alternatingItemStyle = new TableItemStyle(); return alternatingItemStyle; } }
Nota Tenga en cuenta que los estilos son propiedades complejas y, por lo tanto, requieren cuidado para asegurarse de que están almacenados correctamente en el control ViewState de RssFeed. (Este código se ha omitido en el ejemplo anterior para mayor brevedad y porque solo consta de parte del código necesario para mantener correctamente el estado de estilo sobre postbacks).
De forma intuitiva, es posible que piense que estos estilos deben aplicarse al encabezado y los elementos del control rssFeed
Para evitar que los estilos se almacenen en viewStates de los controles secundarios, debe aplicar los estilos después de se ha guardado ViewState. Esto significa que debe aplicar los estilos en la fase de representación. Por lo tanto, debe invalidar el método Render() y, desde allí, aplicar los estilos. Para simplificar el método
protected override void Render(HtmlTextWriter writer) { // Parepare the control hiearchy for rendering PrepareControlHierarchyForRendering(); // We call RenderContents instead of Render() // so that the encasing tag (<span>) // is not included; rather, just a <table> is emitted... RenderContents(writer); }
En el método PrepareControlHierarchyForRendering(), debe hacer referencia mediante programación al control Table en la jerarquía de controles, tomar el encabezado y aplicar su estilo y, a continuación, recorrer en iteración las filas restantes, aplicar el estilo adecuado (sea el ItemStyle o AlternatingItemStyle).
protected virtual void PrepareControlHierarchyForRendering() { // Make sure we have a control to work with if (Controls.Count != 1) return; // Apply the table style Table outerTable = (Table) Controls[0]; outerTable.CopyBaseAttributes(this); outerTable.ApplyStyle(ControlStyle); // apply the header formatting outerTable.Rows[0].ApplyStyle(this.HeaderStyle); // Apply styling for all items in table, if styles are specified... if (this.itemStyle == null && this.alternatingItemStyle == null) return; // First, get alternatingItemStyle setup... TableItemStyle mergedAltItemStyle = null; if (this.alternatingItemStyle != null) { mergedAltItemStyle = new TableItemStyle(); mergedAltItemStyle.CopyFrom(this.itemStyle); mergedAltItemStyle.CopyFrom(this.alternatingItemStyle); } else mergedAltItemStyle = itemStyle; bool isAltItem = false; for (int i = 1; i < outerTable.Rows.Count; i++) { if (isAltItem) outerTable.Rows[i].MergeStyle(mergedAltItemStyle); else outerTable.Rows[i].MergeStyle(ItemStyle); isAltItem = !isAltItem; } }
En el resto de este artículo se examina cómo agregar compatibilidad con plantillas, incluido cómo usar la propagación de eventos para responder a eventos que se producen dentro de una plantilla.
Agregar plantillas al control DataBound
En Building Templated Custom ASP.NET Server Controls, he examinado los pasos necesarios para agregar compatibilidad con plantillas a un control que no sea de entrada de datos. Recuerde que esto implica los tres pasos siguientes:
- Creación de una variable de miembro privado de tipo ITemplate.
- Crear una propiedad pública de tipo ITemplate. Es a través de esta propiedad que el desarrollador de páginas especificará el marcado HTML, los controles web y la sintaxis de enlace de datos para la plantilla.
- Aplicar la plantilla mediante el método
InstatiateIn() de .
Estos pasos son los mismos para agregar una plantilla a un control de entrada de datos.
Muchos controles de entrada de datos contienen más de una plantilla. El DataList, por ejemplo, contiene un HeaderTemplate, FooterTemplate, ItemTemplate, AlternatingItemTemplate, etc. Para cada plantilla debe proporcionarse un control, se debe crear una variable de miembro privado independiente y se debe proporcionar una propiedad ITemplate pública independiente. Para RssFeed, vamos a usar solo dos plantillas: HeaderTemplate, que puede personalizar el marcado del encabezado; y ItemTemplate, que especifica el marcado personalizado para cada elemento (RssFeedItem) del control RssFeed.
Comience definiendo las variables de miembro privado y las propiedades ITemplate públicas para HeaderTemplate y ItemTemplate:
private ITemplate _itemTemplate; private ITemplate _headerTemplate; public ITemplate ItemTemplate { get { return _itemTemplate; } set { _itemTemplate = value; } } public ITemplate HeaderTemplate { get { return _headerTemplate; } set { _headerTemplate = value; } }
A continuación, debe volver a los métodos
Para determinar si se ha especificado una plantilla, simplemente compruebe si la variable de miembro privado aplicable es null o no. Si es null, no se ha proporcionado una plantilla. El siguiente fragmento de código procede del método CreateControlHierarchy() y crea el encabezado RssFeed desde headerTemplate si se proporciona HeaderTemplate.
protected virtual void CreateControlHierarchy(bool useDataSource) { ... if (rssData != null) { // create a Table Table outerTable = new Table(); Controls.Add(outerTable); // Add a header, if needed TableRow headerRow = new TableRow(); TableCell headerCell = new TableCell(); // see if we should use the template or the default if (_headerTemplate != null) { _headerTemplate.InstantiateIn(headerCell); } else { // add a default header ... } // Add the cell and row to the row/table headerRow.Cells.Add(headerCell); outerTable.Rows.Add(headerRow); ... } }
Tenga en cuenta que antes de crear el encabezado, compruebe si _headerTemplate es null. Si no es así, significa que el usuario ha especificado un HeaderTemplate, por lo que crea una instancia de la plantilla en el encabezado Cell. Si no se ha proporcionado headerTemplate, se agrega el encabezado predeterminado. (En la sección "Creating a Non-Templated DataBound Control", hemos examinado el código para crear este encabezado predeterminado).
En una vena similar, el método CreateRssFeedItem() comprueba si _itemTemplate es null o no y, en función de esa comparación, crea una instancia de la plantilla en la instancia RssFeedItem creada o compila mediante programación la interfaz RssFeedIte m predeterminada.
protected virtual RssFeedItem CreateRssFeedItem(TableRowCollection rows, RssFeedItemType itemType, RssItem item, bool useDataSource) { RssFeedItem feedItem = new RssFeedItem(itemType); RssFeedItemEventArgs e = new RssFeedItemEventArgs(feedItem); // see if there is an ItemTemplate if (_itemTemplate != null) { TableCell dummyCell = new TableCell(); // instantiate in the ItemTemplate _itemTemplate.InstantiateIn(dummyCell); feedItem.Cells.Add(dummyCell); OnItemCreated(e); // raise the ItemCreated event rows.Add(feedItem); if (useDataSource) { feedItem.DataItem = item; feedItem.DataBind(); OnItemDataBound(e); // raise the ItemDataBound event } } else { // manually create the item ... } return feedItem; }
Tenga en cuenta que con la plantilla, si va a crear el control desde eldataSource de
En este punto ha mejorado el control para que permita al desarrollador de páginas personalizar el HTML representado mediante plantillas. Pero, ¿qué ocurre si el desarrollador de páginas quiere agregar, por ejemplo, un Botón control web dentro de la plantilla y, a continuación, responder mediante programación a su evento click? Como es probable que sepa, el DataGrid, DataListy Repeater todos tienen un evento ItemCommand que se desencadena si se desencadena un evento Command desde los intestinos de sus plantillas. Echemos un vistazo a cómo ampliar RssFeed para admitir también el evento ItemCommand.
Detección y propagación de eventos
Hay numerosas acciones que pueden hacer que un control web genere un evento. Si un surfista web, por ejemplo, hace clic en un botón de control web que causa una devolución de correo, tras la postback que el evento Command button se activará. ¿Qué debe ocurrir si uno de los controles secundarios de un control compuesto genera un evento? El modelo de control de servidor ASP.NET usa de propagación de eventos para percolar el evento a través de la jerarquía de control hasta que algún control determine que la propagación debe detenerse.
La propagación de eventos se puede realizar a través de dos métodos: OnBubbleEvent() y RaiseBubbleEvent(). RaiseBubbleEvent(), como su nombre implica, propaga un evento al elemento primario del control. El método OnBubbleEvent() para un control compuesto se ejecutará si uno si sus controles secundarios propagan un evento. Para aclarar las cosas, veamos un ejemplo sencillo. Supongamos que tiene un control compuesto p, que tiene un control secundario c. Ahora, imagine que c desencadena un evento que se propaga hasta su elemento primario a través de una llamada a RaiseBubbleEvent(). Cuando se propaga el evento, se ejecutará pel método OnBubbleEvent().
El método
Esta técnica se usa en DataGrid, DataListy repeater para controlar el evento Command de Buttons, LinkButtonsy ImageButtons dentro de los controles. Dado que el evento Command del botón llama a RaiseBubbleEvent(), esto percola el evento hasta el elemento primario del botón. Las clases que componen los elementos de DataGrid, DataListy Repeater—el DataGridItem, DataListItemy RepeaterItem— invalidan el OnBubbleEvent() para detectar eventos de propagación. Si se ha propagado un evento Comando
Con esta comprensión de cómo controla los eventos de burbujas de datos Web, echemos un vistazo a los pasos necesarios para agregar un evento
protected override bool OnBubbleEvent(object source, EventArgs args) { // only bother bubbling appropriate events if (args is CommandEventArgs) { RssFeedItemCommandEventArgs e = new RssFeedItemCommandEventArgs(this, source, (CommandEventArgs) args); base.RaiseBubbleEvent(this, e); return true; } else return false; }
Como puede ver,
Todo lo que queda es que el rssFeed control OnBubbleEvent() se invalide. Cuando se propaga una instancia de RssFeedItemCommandEventArgs, es hora de generar el evento ItemCommand y finalizar la propagación; de lo contrario, deje que la propagación continúe sin obstáculos.
protected override bool OnBubbleEvent(object source, EventArgs args) { // only bother bubbling appropriate events if (args is RssFeedItemCommandEventArgs) { OnItemCommand((RssFeedItemCommandEventArgs) args); return true; } else return false; }
Ahora que el control RssFeed genera un evento ItemCommand, puede agregar botones al ItemTemplate y responder mediante programación a su clic. A continuación se muestra un ejemplo sencillo que ilustra esto. Imagine que tiene configurado ItemTemplate para mostrar un botón de Visitar para cada elemento. Cuando el usuario hace clic en este botón, el objetivo es que se abran a la dirección URL del elemento RSS de
<skm:RssFeed ...> <ItemTemplate> <strong><%# Container.DataItem.Title %></strong> <br> <asp:Button runat="server" Text="Visit" CommandName='<%# Container.DataItem.Link %>'> </asp:Button> </ItemTemplate> </skm:Rss>
A continuación, querrá conectar el evento RssFeedItemCommand evento a un controlador de eventos. El código de este controlador de eventos sería dolorosomente sencillo: solo una Response.Redirect() simple al valor del CommandName del botón en el que se hace clic.
private void blog_ItemCommand(object sender, skmRss.RssFeedItemCommandEventArgs e) { Response.Redirect(e.CommandName); }
Conclusión
En este artículo hemos visto cómo, en primer lugar, crear un control de entrada de datos y, a continuación, cómo agregar compatibilidad con plantillas a este control de entrada de datos. En concreto, disectamos RssFeed, un control de servidor ASP.NET personalizado diseñado para mostrar información de una fuente de distribución RSS. Al crear un control con plantilla de entrada de datos, resulta útil crear primero el control como un control de entrada de datos y, a continuación, agregar compatibilidad con plantillas. Como se describe en la sección "Creación de un control de entrada de datos sin plantilla", la creación de un control de entrada de datos implica tres pasos:
- Cree una propiedad
DataSource. - Invalide el método
DataBind(). - Invalide el método CreateChildControls() y proporcione un método para crear la jerarquía de controles.
Agregar compatibilidad con plantillas a un control de entrada de datos no es especialmente difícil. Comience agregando una variable de miembro privado y la propiedad pública correspondiente de tipo ITemplate para cada plantilla que el control debe admitir. A continuación, en el método CreateChildControls(), cree una instancia de la plantilla en el elemento de entrada de datos (RssFeedItem, para este control).
Los controles de entrada de datos son un medio útil para ayudar a ASP.NET desarrolladores de páginas a mostrar datos estructurados en un formato que se puede presentar en web. Agregar compatibilidad con plantillas al control de entrada de datos concede a los desarrolladores de páginas un alto grado de libertad al crear el marcado HTML resultante del control.
Acerca del autor
Scott Mitchell, autor de cinco libros y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft durante los últimos cinco años. Scott trabaja como consultor independiente, entrenador y escritor. Puede acceder a él en mitchell@4guysfromrolla.com o a través de su blog, que se puede encontrar en http://ScottOnWriting.NET.