Usar TemplateFields en el control DetailsView (VB)

por Scott Mitchell

Descargar PDF

Las mismas funcionalidades de TemplateFields disponibles con GridView también están disponibles con el control DetailsView. En este tutorial vamos a mostrar un producto a la vez mediante un control DetailsView que contiene TemplateFields.

Introducción

TemplateField ofrece un mayor grado de flexibilidad en la representación de datos que los controles de campo de datos BoundField, CheckBoxField, HyperLinkField y otros. En el tutorial anterior, examinamos el uso de TemplateField en una GridView para:

  • Mostrar varios valores de campo de datos en una columna. Específicamente, los campos FirstName y LastName se combinaron en una columna GridView.
  • Usar un control web alternativo para expresar un valor de campo de datos. Vimos cómo mostrar el valor HiredDate mediante un control Calendar.
  • Mostrar información de estado basada en los datos subyacentes. Aunque la tabla Employees no contiene una columna que devuelve el número de días que un empleado ha estado en el trabajo, hemos podido mostrar dicha información en el ejemplo de GridView del tutorial anterior con el uso de un método de formato y TemplateField.

Las mismas funcionalidades de TemplateFields disponibles con GridView también están disponibles con el control DetailsView. En este tutorial, se mostrará un producto a la vez mediante un control DetailsView que contiene dos TemplateFields. El primer TemplateField combinará los campos de datos UnitPrice, UnitsInStock y UnitsOnOrder en una fila de DetailsView. El segundo TemplateField mostrará el valor del campo Discontinued, pero usará un método de formato para mostrar "YES" si Discontinued es True, y "NO" si no lo es.

Two TemplateFields are Used to Customize the Display

Figura 1: Se usan dos TemplateFields para personalizar la presentación (haga clic para ver la imagen de tamaño completo)

Comencemos.

Paso 1: Enlace de los datos al control DetailsView

Como se explicó en el tutorial anterior, al trabajar con TemplateFields, a menudo es más fácil empezar creando el control DetailsView, que contiene solo BoundFields y, luego, agregar nuevos TemplateFields o convertir los BoundFields existentes en TemplateFields según sea necesario. Por lo tanto, empiece este tutorial agregando un control DetailsView a la página a través del Diseñador y enlazándolo a un ObjectDataSource que devuelva la lista de productos. Estos pasos crearán un control DetailsView con BoundFields para cada uno de los campos de valor no booleano del producto y un CheckBoxField para el campo de valor booleano (descontinuado).

Abra la página DetailsViewTemplateField.aspx y arrastre un control DetailsView desde el cuadro de herramientas al Diseñador. En la etiqueta inteligente de DetailsView, elija agregar un nuevo control ObjectDataSource que invoque el métodoGetProducts() de la clase ProductsBLL.

Add a New ObjectDataSource Control that Invokes the GetProducts() Method

Figura 2: Agregue un nuevo control ObjectDataSource que invoque el método GetProducts() (haga clic para ver la imagen de tamaño completo)

Para este informe, quite los BoundFields ProductID, SupplierID, CategoryID y ReorderLevel. A continuación, reordene de modo que los BoundFields CategoryName y SupplierName aparezcan inmediatamente después del BoundField ProductName. No dude en ajustar las propiedades de HeaderText y las de formato para los BoundFields según considere necesario. Al igual que con GridView, estas modificaciones de nivel de BoundField se pueden realizar a través del cuadro de diálogo Campos (accesible haciendo clic en el vínculo Editar campos de la etiqueta inteligente de DetailsView) o a través de la sintaxis declarativa. Por último, desactive los valores de propiedad Height y Width de DetailsView para permitir que el control DetailsView se expanda en función de los datos mostrados y active la casilla Habilitar paginación en la etiqueta inteligente.

Después de realizar estos cambios, el marcado declarativo del control DetailsView debe ser similar al siguiente:

<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False"
    DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True"
    EnableViewState="False">
    <Fields>
        <asp:BoundField DataField="ProductName" HeaderText="Product"
          SortExpression="ProductName" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category"
          ReadOnly="True" SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName" HeaderText="Supplier"
          ReadOnly="True" SortExpression="SupplierName" />
        <asp:BoundField DataField="QuantityPerUnit"
          HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" />
        <asp:BoundField DataField="UnitPrice" HeaderText="Price"
          SortExpression="UnitPrice" />
        <asp:BoundField DataField="UnitsInStock"
          HeaderText="Units In Stock" SortExpression="UnitsInStock" />
        <asp:BoundField DataField="UnitsOnOrder"
          HeaderText="Units On Order" SortExpression="UnitsOnOrder" />
        <asp:CheckBoxField DataField="Discontinued"
          HeaderText="Discontinued" SortExpression="Discontinued" />
    </Fields>
</asp:DetailsView>

Tómese un momento para visualizar la página en un explorador. En este punto, debería ver un único producto enumerado (Chai) con filas que muestran el nombre del producto, la categoría, el proveedor, el precio, las unidades en existencias, las unidades en pedido y su estado descontinuado.

The Product's Details Are Shown Using a Series of BoundFields

Figura 3: Los detalles del producto se muestran mediante una serie de BoundFields (haga clic para ver la imagen de tamaño completo)

Paso 2: Combinar el precio, las unidades en existencias y las unidades en pedido en una fila

DetailsView tiene una fila para los campos UnitPrice, UnitsInStock y UnitsOnOrder. Podemos combinar estos campos de datos en una sola fila con un TemplateField agregando un nuevo TemplateField o convirtiendo uno de los BoundFields UnitPrice, UnitsInStock y UnitsOnOrder existentes en un TemplateField. Aunque personalmente prefiero convertir los BoundFields existentes, vamos a agregar un nuevo TemplateField para practicar.

Para empezar, haga clic en el vínculo Editar campos de la etiqueta inteligente de DetailsView para abrir el cuadro de diálogo Campos. A continuación, agregue un nuevo TemplateField y establezca la propiedad HeaderText en "Price and Inventory" y mueva el nuevo TemplateField para que se ubique encima del BoundField UnitPrice.

Add a New TemplateField to the DetailsView Control

Figura 4: Agregue un nuevo TemplateField al control DetailsView (haga clic para ver la imagen de tamaño completo)

Dado que este nuevo TemplateField contendrá los valores que se muestran actualmente en los BoundFields UnitPrice, UnitsInStock y UnitsOnOrder, vamos a quitarlos.

La última tarea de este paso es definir el marcado de ItemTemplate para el TemplateField "Price and Inventory", lo que se puede realizar a través de la interfaz de edición de plantillas de DetailsView en el Diseñador o manualmente a través de la sintaxis declarativa del control. Al igual que con GridView, se puede acceder a la interfaz de edición de plantillas de DetailsView haciendo clic en el vínculo Editar plantillas de la etiqueta inteligente. Desde aquí puede seleccionar la plantilla para editar en la lista desplegable y, luego, agregar cualquier control web desde el cuadro de herramientas.

Para este tutorial, empiece agregando un control de etiqueta al ItemTemplate del TemplateField "Price and Inventory". A continuación, haga clic en el vínculo Editar DataBindings desde la etiqueta inteligente del control web de etiqueta y enlace la propiedad Text al campo UnitPrice.

Bind the Label's Text Property to the UnitPrice Data Field

Figura 5: Enlace la propiedad Text de la etiqueta al campo de datos UnitPrice (haga clic para ver la imagen de tamaño completo)

Aplicar formato de moneda al precio

Con esta adición, el TemplateField del control web de etiqueta de "Price and Inventory" mostrará solo el precio del producto seleccionado. En la figura 6, se muestra una captura de pantalla del avance hasta ahora, visto a través de un explorador.

The Price and Inventory TemplateField Shows the Price

Figura 6: El TemplateField de "Price and Inventory" muestra el precio (haga clic para ver la imagen de tamaño completo)

Tenga en cuenta que el precio del producto no tiene el formato de moneda. Con BoundField, es posible aplicar formato estableciendo la propiedad HtmlEncode en False y la propiedad DataFormatString en {0:formatSpecifier}. En el caso de TemplateField, sin embargo, las instrucciones de formato deben especificarse en la sintaxis de enlace de datos o mediante el uso de un método de formato definido en algún lugar en el código de la aplicación (por ejemplo, en la clase de código subyacente de la página ASP.NET).

Para especificar el formato de la sintaxis de enlace de datos utilizada en el control web de etiqueta, vuelva al cuadro de diálogo DataBindings haciendo clic en el vínculo Editar DataBindings en la etiqueta inteligente de la etiqueta. Puede escribir las instrucciones de formato directamente en la lista desplegable Formato o seleccionar una de las cadenas de formato definidas. Al igual que con la propiedad DataFormatString de BoundField, el formato se especifica mediante {0:formatSpecifier}.

Para el campo UnitPrice, use el formato de moneda especificado seleccionando el valor de lista desplegable adecuado o escribiendo {0:C} a mano.

Format the Price as a Currency

Figura 7: Aplique el formato de moneda al precio (haga clic para ver la imagen de tamaño completo)

Mediante declaración, la especificación de formato se indica como un segundo parámetro en los métodos Bind o Eval. La configuración realizada a través del Diseñador da como resultado la siguiente expresión de enlace de datos en el marcado declarativo:

<asp:Label ID="Label1" runat="server" Text='<%# Eval("UnitPrice", "{0:C}") %>'/>

Agregar los campos de datos restantes al TemplateField

En este punto, hemos mostrado y formateado el campo de datos UnitPrice en el TemplateField "Price and Inventory", pero todavía necesitamos mostrar los campos UnitsInStock y UnitsOnOrder. Vamos a mostrarlos en una línea debajo del precio y entre paréntesis. Desde la interfaz de edición de plantillas en el Diseñador, este marcado se puede agregar colocando el cursor dentro de la plantilla y escribiendo simplemente el texto que se va a mostrar. Como alternativa, este marcado se puede escribir directamente en la sintaxis declarativa.

Agregue el marcado estático, los controles web de etiqueta y la sintaxis de enlace de datos para que el TemplateField de "Price and Inventory" muestre la información de precios e inventario de la siguiente manera:

UnitPrice
(In Stock / On Order:UnitsInStock / UnitsOnOrder)

Después de realizar esta tarea, el marcado declarativo de DetailsView debe ser similar al siguiente:

<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False"
    DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True"
    EnableViewState="False">
    <Fields>
        <asp:BoundField DataField="ProductName"
          HeaderText="Product" SortExpression="ProductName" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category"
          ReadOnly="True" SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName"
          HeaderText="Supplier" ReadOnly="True"
          SortExpression="SupplierName" />
        <asp:BoundField DataField="QuantityPerUnit"
          HeaderText="Qty/Unit" SortExpression="QuantityPerUnit" />
        <asp:TemplateField HeaderText="Price and Inventory">
            <ItemTemplate>
                <asp:Label ID="Label1" runat="server"
                  Text='<%# Eval("UnitPrice", "{0:C}") %>'></asp:Label>
                <br />
                <strong>
                (In Stock / On Order: </strong>
                <asp:Label ID="Label2" runat="server"
                  Text='<%# Eval("UnitsInStock") %>'></asp:Label>
                <strong>/</strong>
                <asp:Label ID="Label3" runat="server"
                  Text='<%# Eval("UnitsOnOrder") %>'>
                </asp:Label><strong>)</strong>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:CheckBoxField DataField="Discontinued"
           HeaderText="Discontinued" SortExpression="Discontinued" />
    </Fields>
</asp:DetailsView>

Con estos cambios hemos consolidado la información de precios e inventario en una sola fila de DetailsView.

The Price and Inventory Information is Displayed in a Single Row

Figura 8: La información de precios e inventario se muestra en una sola fila (haga clic para ver la imagen de tamaño completo)

Paso 3: Personalización de la información del campo descontinuada

La columna Discontinued de la tabla Products es un valor de bit que indica si el producto se ha descontinuado. Al enlazar DetailsView (o GridView) a un control de origen de datos, los campos de valor booleano, como Discontinued, se implementan como CheckBoxFields, mientras que los campos de valor no booleano, como ProductID, ProductName, etc., se implementan como BoundFields. CheckBoxField se representa como una casilla deshabilitada que está activada si el valor del campo de datos es True. De lo contrario, está desactivada.

En lugar de mostrar el CheckBoxField, es posible que debamos mostrar texto que indica si el producto está o no descontinuado. Para ello, podríamos quitar el CheckBoxField de DetailsView y luego agregar un BoundField cuya propiedad DataField se haya establecido en Discontinued. Tómese un momento para hacerlo. Después de este cambio, DetailsView muestra el texto "True" para los productos descontinuados y "False" para los productos que todavía están activos.

The Strings True and False Are Used to Display the Discontinued State

Figura 9: Las cadenas True y False se usan para mostrar el estado descontinuado (haga clic para ver la imagen de tamaño completo)

Imagine que no quisiéramos que se usen las cadenas "True" o "False" sino "YES" y "NO" en su lugar. Esta personalización se puede realizar con la ayuda de un TemplateField y un método de formato. Un método de formato puede tomar cualquier número de parámetros de entrada, pero debe devolver el código HTML (como una cadena) para insertarlo en la plantilla.

Agregue un método de formato a la clase de código subyacente denominada DisplayDiscontinuedAsYESorNO de la página DetailsViewTemplateField.aspx, que acepte un objeto Northwind.ProductsRow como parámetro de entrada y devuelva una cadena. Como se explicó en el tutorial anterior, este método se debe marcar como Protected o Public para que sea accesible desde la plantilla.

Protected Function DisplayDiscontinuedAsYESorNO(discontinued As Boolean) As String
    If discontinued Then
        Return "YES"
    Else
        Return "NO"
    End If
End Function

Este método comprueba el parámetro de entrada (discontinued) y devuelve "YES" si es True o, de lo contrario, devuelve "NO".

Nota:

En el método de formato examinado en el tutorial anterior, recuerde que hemos pasado un campo de datos que podría contener valores NULL y, por lo tanto, fue necesario comprobar si el valor de la propiedad HiredDate del empleado tenía un valor de base de datos NULL antes de acceder a la propiedad HiredDate de EmployeesRow. Esta comprobación no es necesaria aquí, ya que la columna Discontinued nunca puede tener asignados valores de base de datos NULL. Además, es por este motivo que el método puede aceptar un parámetro de entrada booleano en lugar de tener que aceptar una instancia ProductsRow o un parámetro de tipo Object.

Con este método de formato completo, todo lo que queda es llamarlo desde el ItemTemplate del TemplateField. Para crear el TemplateField, quite el BoundField Discontinued y agregue un nuevo TemplateField o convierta el BoundField Discontinued en un TemplateField. A continuación, desde la vista de marcado declarativo, edite el TemplateField para que contenga solo un ItemTemplate que invoque el método DisplayDiscontinuedAsYESorNO y pase el valor de la propiedad Discontinued de la instancia ProductRow actual. Se puede acceder a esto a través del método Eval. En concreto, el marcado de TemplateField debe ser similar al siguiente:

<asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
    <ItemTemplate>
        <%#DisplayDiscontinuedAsYESorNO(Convert.ToBoolean(Eval("Discontinued")))%> 
    </ItemTemplate>
</asp:TemplateField>

Esto hará que el método DisplayDiscontinuedAsYESorNO se invoque al representar DetailsView, pasando en el valor Discontinued de la instancia ProductRow. Dado que el método Eval devuelve un valor de tipo Object, pero el método DisplayDiscontinuedAsYESorNO espera un parámetro de entrada de tipo Boolean, convertimos el valor devuelto del método Eval en Boolean. Luego, el método DisplayDiscontinuedAsYESorNO devolverá "YES" o "NO" en función del valor que reciba. El valor devuelto es lo que se muestra en esta fila de DetailsView (vea la figura 10).

YES or NO Values are Now Shown in the Discontinued Row

Figura 10: Los valores YES o NO ahora se muestran en la fila Descontinuado (haga clic para ver la imagen de tamaño completo)

Resumen

El TemplateField en el control DetailsView permite un mayor grado de flexibilidad para mostrar datos que el disponible con los otros controles de campo y es ideal para situaciones en las que:

  • Es necesario mostrar varios campos de datos en una columna GridView
  • Los datos se expresan mejor mediante un control web en lugar de texto sin formato
  • La salida depende de los datos subyacentes, como mostrar metadatos o volver a formatear los datos

Aunque los TemplateFields permiten un mayor grado de flexibilidad en la representación de los datos subyacentes de DetailsView, la salida de DetailsView aún se ve un poco cuadrada, ya que cada campo se representa como una fila en una <table> HTML.

El control FormView ofrece un mayor grado de flexibilidad para configurar la salida representada. El FormView no contiene campos, sino simplemente una serie de plantillas (ItemTemplate, EditItemTemplate, HeaderTemplate, etc.). Vamos a ver cómo usar el FormView para lograr aún más control del diseño representado en nuestro siguiente tutorial.

¡Feliz programación!

Acerca del autor

Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, instructor y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Puede ponerse en contacto con él en mitchell@4GuysFromRolla.com. o a través de su blog, http://ScottOnWriting.NET.

Agradecimientos especiales a

Esta serie de tutoriales contó con la revisión de muchos revisores que fueron de gran ayuda. El revisor principal de este tutorial fue Dan Jagers. ¿Le interesa revisar mis próximos artículos en MSDN? Si es así, escríbame a mitchell@4GuysFromRolla.com.