ASP.NET

Entity Framework (VI): Webservices


Hasta ahora hemos visto el funcionamiento de LINQ y Entity Framework. La siguiente serie de artículos estarán orientados hacia los servicios web, por lo que haremos una pequeña introducción aplicando los conocimientos que hemos obtenido hasta el momento.

Lo primero que deberemos aclarar es el propio concepto de servicio web. Ya vimos en artículos anteriores de qué se tratan, cómo se crean y cómo se consumen estas pequeñas aplicaciones cuyo objetivo es el intercambio de información entre distintas plataformas y lenguajes de modo estándar.

Para crear un nuevo servicio web, crearemos un nuevo proyecto web de tipo ASP.NET Empty Web Application y le asociaremos un nuevo nombre. Los servicios web reciben ese nombre porque operan sobre el protocolo HTTP, así que este será nuestro punto de partida.

(más…)

LINQ to SQL (I): Mapeo Objeto-Relacional


A estas alturas ya debemos de tener una idea bastante precisa del funcionamiento básico de LINQ. Hemos visto cómo lanzar consultas a listados de objetos (LINQ to Objects) e incluso algún ejemplo sobre cómo iterar sobre ficheros XML (LINQ to XML). Todas estas operaciones tienen un factor común: se realizaban sobre listados que implementaban la interfaz IEnumerable.

Además de poder lanzar consultas sobre listados de objetos, LINQ nos permite una opción aún más potente: lanzar consultas sobre bases de datos SQL Server mediante LINQ to SQL, que no es más que un mapper objeto-relacional, es decir, encargado de transformar sentencias LINQ en expresiones T-SQL y viceversa.

La potencia de LINQ to SQL radica en que, además de permitir realizar operaciones de consulta sobre una base de datos, también es posible realizar inserciones, actualizaciones y eliminaciones de forma nativa. A continuación veremos cómo realizar esto.

La principal desventaja de usar LINQ to SQL es que nuestra base de datos ha de ser, obligatoriamente, SQL Server (no obstante, existen proveedores LINQ para otras bases de datos, como LINQ to Oracle o LINQ to MySQL (vía NuGet). En caso de que todo esto falle y que necesitemos un proveedor personalizado, siempre podremos desarrollar por cuenta propia nuestro propio proveedor. En cualquier caso, siempre podremos utilizar un mapper objeto-relacional más genérico, como por ejemplo Entity Framework, del que hablaremos en posteriores artículos.

En este primer acercamiento a LINQ to SQL hablaremos de cómo establecer la relación entre nuestro programa (orientado a objetos) y nuestra base de datos (relacional). Para ello disponemos de varias posibilidades, que mostraremos a continuación:

(más…)

El Controlador en ASP.NET MVC 4 (III): Action Filters


Un Action Filter o filtro de acción es un atributo que puede asociarse a una acción de un controlador (o al controlador en su totalidad), modificando la forma en la que la acción se ejecuta. El Framework ASP.NET MVC proporciona unos cuantos, entre ellos:

Nombre Descripción
Authorize Restringe una acción para los roles o usuarios especificados.
HandleError Permite especificar una vista que se mostrará en caso de excepción no controlada.
OutputCache Almacena en caché la salida de un controlador
ValidateAntiForgeryToken Ayuda a evitar peticiones de falsificación de petición (pishing)
ValidateInput Desactiva las validaciones de petición. No es aconsejable hacer uso de este filtro salvo en ocasiones muy concretas, ya que pone en peligro la seguridad de la aplicación.

Además de los existentes, es posible crear filtros personalizados para fines definidos por el negocio (como por ejemplo, un sistema de autenticación personalizado).

Tal y como vimos brevemente en el artículo anterior con [HttpGet] y [HttpPost], las acciones pueden “condimentarse” con atributos que afectan a su funcionalidad. Los filtros se añaden a modo de atributo a las acciones, haciendo que su comportamiento varíe. Veamos un par de ejemplos.

(más…)

Introducción a ASP.NET MVC 4


Aunque no hayas estudiado patrones de diseño, es altamente probable de que, si te dedicas a la programación, hayas oído hablar en alguna ocasión del Modelo-Vista-Controlador. Se trata de uno de los patrones de diseño más ubicuos y populares, que persigue la filosofía de crear un patrón de tres capas que diferencian las labores de la aplicación en:

  • MODELO: Gestiona la información que el usuario precisa, es decir qué información quiere el usuario.
  • VISTA: Muestra la información al usuario y permite que éste interactúe con ella, proporcionando datos, modificándolos, solicitando su modificación o simplemente, consultándolos. En resumen, cómo se muestra la información en pantalla.
  • CONTROLADOR: Actúa de intermediario entre los elementos anteriores y el usuario, atendiendo las peticiones HTTP (en el caso de ASP.NET), generando la información que el usuario requiere (modelo) y mostrándola por pantalla (vista).

Microsoft proporciona una implementación estándar de este patrón permitiendo crear un proyecto basado en web que genera automáticamente estos tres elementos, separando de una forma más clara la funcionalidad interna de la aplicación de la gestión de las peticiones y su visualización en pantalla.

(más…)

Permitir conexiones locales a IIS y SQL Server en Windows 7


A la hora de desarrollar una aplicación .NET / ASP.NET con persistencia de datos, será necesario establecer una conexión con un servidor de base de datos. Generalmente, .NET suele hacer uso del binomio ASP.NET/SQL Server, para lo cual suelen darse, dejando a un lado los entornos de pruebas, integración y producción, dos escenarios típicos:

  • Utilizar una base de datos local en la que “cacharrear” y otra base de datos de desarrollo común a todos los desarrolladores de la aplicación.
  • Utilizar directamente una base de datos de desarrollo sin hacer uso de una base de datos local.

El segundo escenario típicamente suele consistir en un servidor SQL Server en red local, que bien puede ser dedicado (preferiblemente) o directamente (aunque sea mala práctica) el equipo de un compañero concreto.

Sin embargo, el acceso al equipo de un equipo en red local, políticas de red aparte, no es automático, sino que requiere permitir que tanto el servidor web (IIS 7 en nuestro caso) como el servidor de base de datos (ejemplificaremos con SQL Server 2012) permita el acceso a conexiones remotas y no sólo locales. Veremos a continuación cómo lograr este objetivo. (más…)

Utilizar la Base de Datos como repositorio de imágenes (I)


En cierto proyecto necesitaba mostrar una serie de imágenes que el usuario debía insertar desde su equipo local. En un principio pensé enviar al servidor las imágenes, guardar la ruta relativa en Base de Datos y, cuando fuese necesario acceder a las imágenes, utilizar dicha ruta para acceder a la imagen. Pero no era tan sencillo. Por desgracia, no teníamos permiso de escritura en el disco duro, por lo que tenía que juguetear con las imágenes sin que éstas existieran físicamente. ¿Cómo? Almacenando y recuperando las imágenes de base de datos.

Guardando una imagen en SQL Server

Para empezar, veremos cómo almacenar en base de datos una imagen. Crearemos, desde el SQL Server Management Studio, una nueva tabla que tendrá tres campos:

  • Un Id único entero y autoincrementable (IdImagen)
  • Un nombre para la imagen nvarchar(50) (NombreImagen)
  • Una secuencia de bytes, variable de tipo image (Fichero).

09111201 Creada la tabla, crearemos una página que, a partir de la ruta de la imagen, la inserte en base de datos. Podemos utilizar un Input File o un control similar para indicarle la ruta, pero aquí indicaremos únicamente el código necesario para subir la imagen dada su ruta física. Lo que haremos a continuación será lo siguiente:

  • Crear una conexión a Base de Datos a partir de una cadena de conexión
string ConnectionString = @"Data Source=DANIGARCIASQLSERVER2005;Initial Catalog=TestDB;Persist Security Info=True;User ID=dani;Password=c0ntr4s3n14";
  • Crear una cadena de texto con la sentencia INSERT.
string CommandString = "INSERT INTO Imagen(NombreImagen, Fichero) VALUES (@NombreImagen, @Fichero)";
  • A continuación necesitaremos convertir nuestra imagen en un objeto que podamos manejar, por ejemplo un array de bytes. Para ello, a partir de la ruta física de la imagen crearemos un objeto de tipo System.Drawing.Bitmap que guardaremos en un MemoryStream que, a su vez, convertiremos en un array de bytes (byte[]).
  byte[] bImagen = null; System.IO.MemoryStream ms = new System.IO.MemoryStream();  System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(@"C:TEMPinicio.jpg"); if (bmp != null) { bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); bImagen = ms.ToArray(); ms.Close(); }  
  • Instanciar un objeto de tipo SqlCommand a partir de la  conexión y de la sentencia INSERT.
  SqlConnection conexion = new SqlConnection(ConnectionString);  SqlCommand command = new SqlCommand(CommandString, conexion);  
  • Añadiremos ahora los SqlParameters necesarios al objeto SqlCommand con los datos a insertar en la base de datos. Estos serán, en nuestro caso, el nombre de la imagen y el array de bytes con la imagen.
  SqlParameter nombre = new SqlParameter("@NombreImagen", "inicio.jpg"); SqlParameter imagen = new SqlParameter("@Fichero", bImagen);  
  • Por último, abrimos la conexión, ejecutamos la sentencia mediante ExecuteNonQuery() y cerramos la conexión. Hecho esto, tendremos nuestra imagen almacenada en base de datos.
  conexion.Open();  command.Parameters.Add(nombre); command.Parameters.Add(imagen); command.ExecuteNonQuery();  conexion.Close();  

Hecho esto insertaremos una imagen en una tabla de la base de datos. 09111202 Más adelante aprenderemos cómo recuperarla y referenciarla directamente a través de una URL con extensión aspx, es decir, invocar una ruta de tipo getImage.aspx?Id=xxx.

Variables de servidor en ASP.NET


Nuestro servidor ASP.NET aloja una gran cantidad de información útil que, en determinado momento, puede salvarnos de situaciones complejas. Este conjunto de variables puede consultarse mediante el objeto Request.ServerVariables[<NombreVariable>]. Así, si realizamos la siguiente consulta:

string navegador = Request.ServerVariable[&quot;HTTP_USER_AGENT&quot;];

La variable navegador alojará, como intuitivamente podremos imaginar, el navegador que está utilizando el cliente que se conecta a nuestro servidor, dato utilizado, por ejemplo, para estadísticas. En nuestro caso, el dato devuelto será el siguiente:

Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 1.0.3705; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)

Hay cantidad de variables de servidor cuya consulta puede hacernos la vida más fácil. Como ejemplo, indicaremos algunas de ellas:

VARIABLE SIGNIFICADO EJEMPLO
SERVER_PROTOCOL Protocolo de conexión HTTP/1.1
REMOTE_ADDR Dirección IP Remota 80.24.53.11
LOCAL_ADDR Direccion IP Local 127.0.0.1
PATH_INFO Ruta de la aplicación /MiAplicacion/Pagina.aspx
URL URL de la página /MiAplicacion/Pagina.aspx
AUTH_TYPE Tipo de autenticación Forms
SERVER_PORT Puerto HTTP 80
HTTP_USER_AGENT Navegador del usuario Mozilla/4.0 (compatible; MSIE 8.0;…
HTTP_HOST Nombre del host MyMachine
SERVER_NAME Nombre del servidor MyMachine
SERVER_SOFTWARE Servidor web Microsoft-IIS/6.0
APPL_PHYSICAL_PATH Ruta física de la aplicación C:\Inetpub\wwwroot\MiAplicacion\
HTTPS ¿Usa SSL? off
AUTH_USER Usuario autenticado administrador
REMOTE_USER Usuario remoto administrador
HTTP_CONNECTION Tipo de conexión HTTP Keep alive
HTTP_ACCEPT_LANGUAGE Idioma es

Existen más variables de servidor, y basta con recorrer con un bucle su contenido para echarle un vistazo a todas:


Hashtable hs = new Hashtable();

foreach (string s in Request.ServerVariables.AllKeys)
{
hs.Add(s, Request.ServerVariables[s]);
}

Enviar un e-mail desde .NET utilizando una cuenta SMTP de GMail


En mi proyecto de fin de carrera implementé un sistema automatizado de reservas que avisaba por e-mail del estado de las mismas. Para mi desgracia, olvidé mencionarlo durante la defensa, por lo que a nivel institucional fue una pérdida de tiempo, pero no así a nivel didáctico (que es, a fin de cuentas, lo importante).

Para implementarlo, utilicé los espacios de nombres System.Net y System.Net.Mail y una cuenta de Gmail a la que previamente le había activado el acceso POP/SMTP. Mandar un mail desde .NET es relativamente sencillo.

Si pensamos en la composición de un mail en términos de objetos, veremos que en realidad, enviar un mail es muy, pero que muy sencillo. Imaginemos que tenemos una cuenta en Gmail, por ejemplo ‘estoesunapruebademail25@gmail.com‘, cuya contraseña es ‘inteligible43‘. Queremos mandar un correo. ¿Qué información necesitamos?

  • Un mensaje, que implementará la clase MailMessage.
  • Un cliente de correo (SmtpClient) que se encargue de la autenticación y envío del mensaje.

A su vez, el mensaje necesitará:

  • Uno o varios destinatarios.
  • Un remitente.
  • Un asunto (subject).
  • El texto del mensaje.

Mientras que el cliente del correo necesitará saber:

  • Servidor SMTP al que conectarse y puerto.
  • Nombre de usuario y contraseña.

(más…)

Almacenamiento y recuperación de parámetros en Sesión y ViewState


Muchas veces necesitamos utilizar parámetros entre distintos métodos de un mismo formulario web, o incluso entre formularios distintos. Una forma dinámica de realizar este intercambio de datos es la utilización del ViewState y del espacio de sesión.

Antes de embarcarnos a almacenar datos y datos en estos dos espacios de memoria, hay que dejar claras dos cosas:

  • El ViewState se intercambia continuamente entre cliente y servidor, por lo que es aconsejable no sobrecargarlo. Al aumentar el ViewState, aumentará el tamaño de la página, y por consiguiente, el tráfico de datos y la velocidad de carga de la misma.
  • La sesión se almacena en servidor, por lo que si nuestro sitio web posee una carga de datos moderada – alta, el rendimiento del mismo se verá penalizado. Nuevamente, es aconsejable utilizar con cautela este espacio de memoria.

Teniendo claros estos dos conceptos, utilizaremos el ViewState para almacenar datos locales (de una misma página) y la sesión para pasar parámetros entre distintas páginas sin hacer uso de la QueryString. Para los profanos en la materia, explicaremos que la QueryString es la colección de parámetros que pueden, opcionalmente, acompañar a la dirección de la página. Éstos parámetros ofrecen información directamente recopilable por nuestro formulario web, pero cuenta con la desventaja de que es visible para el usuario.

(más…)

Servicios Web (II): Consumiendo un servicio web


Veíamos anteriormente cómo crear un servicio web simple. A continuación veremos cómo consumirlo.

Si crear un WebService no tiene demasiada dificultad, consumirlo es, si cabe, mucho más sencillo. Crearemos una aplicación web normal y corriente, y sobre ésta, haremos click derecho y seleccionaremos la opción [Agregar Referencia Web…]

0906agregarreferenciaweb
Hecho esto, se desplegará un menú que nos permitirá la detección de servicios web. Si introducimos la dirección en la que el servicio está publicado y pulsamos en “Ir” se nos mostrará los métodos que el servicio expone. En la caja de texto derecha añadiremos el nombre que tendrá nuestra referencia (por ejemplo, ServicioSimple) y pulsaremos sobre [Agregar referencia].

0906servicio

Hecho esto, se agregará a nuestro proyecto una referencia a nuestro servicio web, que se mostrará en nuestro proyecto de la siguiente manera:

0906referencia

Finalmente, añadiremos controles a nuestra página para comprobar que nuestra aplicación se comunica perfectamente con el servicio web. Para ello crearemos un GridView, dos Label, una caja de Texto y tres botones.

En el primer botón codificaremos la funcionalidad para cargar el GridView con la primera tabla del DataSet devuelto por el método GetData() de nuestro servicio web. Para ello crearemos una referencia a nuestro servicio, crearemos una referencia a un DataSet e invocaremos el método GetData() del WebService. Finalmente rellenaremos el Grid con el resultado obtenido.

protected void ButtonGrid_Click(object sender, EventArgs e)
{
// Referenciamos el servicio
ServicioSimple.Service referenciaServicio = new ServicioSimple.Service();

// Creamos una referencia a un DataSet
DataSet ds;

// Invocamos el WebMethod GetData()
ds = referenciaServicio.GetData();

// Rellenamos el DataGrid
GridViewDatos.DataSource = ds.Tables[0];
GridViewDatos.DataBind();
}

En el segundo botón haremos que nuestro label muestre el resultado de la invocación al método GetDate(), que recordemos que devolvía la fecha actual.

protected void ButtonFecha_Click(object sender, EventArgs e)
{
// Referenciamos el servicio
ServicioSimple.Service referenciaServicio = new ServicioSimple.Service();

LabelFecha.Text = referenciaServicio.GetDate().ToString();
}

Por último, el tercer botón mostrará el resultado de invocar al método GetDateDiff(int). Para ello le pasaremos al WebService un parámetro que indicará el número de horas que tiene que añadir. Para ello utilizaremos la caja de texto.

protected void ButtonFechaDiff_Click(object sender, EventArgs e)
{
// Referenciamos el servicio
ServicioSimple.Service referenciaServicio = new ServicioSimple.Service();

if (!string.IsNullOrEmpty(TextBoxDiferencia.Text))
{
LabelFechaDiferencia.Text = referenciaServicio.GetDateDiff(Convert.ToInt32(TextBoxDiferencia.Text)).ToString();
}
}

Hecho. Ya hemos creado un servicio web y hemos aprendido a consumirlo.

0906resultado

Servicios Web (I): Creando un servicio web simple


Un servicio web no es más que un conjunto de protocolos y estándares cuyo objetivo es el intercambio de información entre distintas aplicaciones. Los servicios web tienen la peculiaridad de ser independientes de plataforma, es decir, cumplen estándares propios y no dependen ni del hardware sobre el que se ejecuta ni del sistema operativo o servidor web que lo aloja.

Un servicio web intercambia información. Al invocar un servicio web estamos realizando una petición a otra máquina, a la que (opcionalmente) le enviamos información, la procesa y nos devuelve una respuesta, generalmente atómica y serializable, es decir: lo común es que un servicio web nos devuelva un dato o estructura de datos con la información que necesitemos (atómica) en forma de cadena de texto (serializable). Esto es así por el lenguaje común que “hablan” los servicios web: XML.

Si quisiéramos crear un servicio web, en una solución en blanco pulsaríamos sobre Agregar > Nuevo sitio Web…

0906servicioweb01
Hecho esto, seleccionaremos la plantilla “Servicio Web ASP.NET”, y alojaremos el servicio web en nuestro IIS indicándole la ruta que deseemos.

0906servicioweb02
A continuación, crearemos los métodos que queramos exponer, añadiendo el atributo [WebService] a la clase y el atributo [WebMethod] a los métodos que deban ser expuestos.

Crearemos tres métodos: uno que devuelva la hora actual, otro que devuelva nuestra localización geográfica y otro que añada las horas pasadas como parámetro a la hora actual.

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;

[WebService(Namespace = &quot;http://localhost/Services/Date&quot;)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
public Service () {

//Eliminar la marca de comentario de la línea siguiente si utiliza los componentes diseñados
//InitializeComponent();
}

[WebMethod]
public DateTime GetDate() {
return DateTime.Now;
}

[WebMethod]
public string GetLocation()
{
return &quot;UTC/GMT +2, Madrid, Spain.&quot;;
}

[WebMethod]
public DateTime GetDateDiff(int horasDiferencia)
{
return DateTime.Now.AddHours(horasDiferencia);
}

}

Hecho esto, iremos a la dirección de nuestro servicio web, lo cual nos mostrará lo siguiente:
0906servicioweb03
Para probar el servicio web, haremos click sobre cualquiera de los métodos expuestos. Si el webservice no requiere ningún parámetro, se nos mostrará directamente el resultado de la invocación:

0906servicioweb05
En caso contrario, se nos pedirá que introduzcamos el parámetro requerido, lo cual nos llevará a la obtención del dato.

0906servicioweb04
Si analizamos el contenido de la respuesta, comprobaremos que, efectivamente, se trata de un fichero XML.

<?xml version="1.0" encoding="utf-8"?>
<dateTime xmlns="http://localhost/Services/Date">2009-06-09T17:27:00.0874935+02:00</dateTime&gt;

Posteriormente comprobaremos cómo realizar una invocación a un servicio web desde nuestra aplicación.

Abstracción de Datos (V): Aplicando el patrón Abstract Factory con C# y ADO.NET


Por norma general, un proyecto estará “atado” a una fuente de datos en particular (SQL Server, MySQL, Oracle…). Sin embargo, hay veces que esto no será así: la fuente de datos podría cambiar en cualquier momento, por lo que deberíamos ser capaces de, en la medida de lo posible, abstraer nuestra aplicación de nuestra base de datos.

Previamente comentábamos la flexibilidad otorgada por el patrón de diseño Abstract Factory. Hoy veremos una aplicación práctica, orientada precisamente a obtener práctica independencia de la fuente de datos mediante la utilización de clases comunes a todas las fuentes de datos.

Por norma general utilizamos clases específicas para lograr interactuar con una fuente de datos en concreto (por ejemplo, usaremos un objeto de la clase SqlConnection cuando trabajemos con SQL Server). Éstas clases específicas están optimizadas para trabajar con unos esquemas determinados, aportando un importante incremento del rendimiento de la aplicación. Pero como todo en esta vida, no es gratis. ¿Cuál es el precio? La portabilidad.

(más…)

Añadiendo un TemplateField a un GridView de forma dinámica


La mayor parte de los sitios web que se sirven de listados lo hacen utilizando un esquema como el que sigue:

090501Grid
Como vemos, lo normal suele ser utilizar campos enlazados a la consulta (BoundFields), así como campos-plantilla (TemplateFields) con los que se suele interactuar con el listado (ver detalles, edición, etc.).

Si sabemos de antemano qué datos vamos a obtener, es sencillo “maquetar” nuestro GridView de forma sencilla. El problema radica en el momento en el que el número de columnas que nuestro GridView mostrará sea variable. En este caso, el atributo AutoGenerateColumns deberá ser puesto a True, con lo que perderemos el control del orden de los TemplateFields.

Veamos el siguiente caso: queremos un campo plantilla que muestre un LinkButton que nos permita editar el registro, y otro que nos permita visualizar los detalles del mismo. Queremos uno de estos campos en la primera columna (el de edición) y otro en la última (visualización). Si utilizamos TemplateFields con el atributo AutoGenerateColumns=”True”, lo que lograremos será que ambos botones se sitúen juntos en las dos primeras columnas de nuestro GridView. ¿Cómo hacer que uno de ellos se coloque al final? Añadiéndolo de forma dinámica.

Para comenzar, añadiremos un GridView con un TemplateField conteniendo un LinkButton, colocando el atributo AutogenerateColumns=”True”.

090502Grid
El templatefield contendría un LinkButton:

090503Grid

El resultado sería este:

090504Grid

Seguidamente, acudiremos a nuestro código, listos para editar el evento RowDataBound. En él haremos lo siguiente:

  • Declararemos una celda y un LinkButton.
  • Daremos formato al LinkButton (nombre, clase, etc.)
  • Asignaremos al LinkButton un CommandArgument igual al Identificador (DataKey) de esa fila del GridView
  • Añadiremos el LinkButton a la celda, y la celda a la fila

Para ello utilizaremos el siguiente código:


Protected Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView1.RowDataBound
Try

    ' Si se trata de una fila de datos...
    If e.Row.RowType = DataControlRowType.DataRow Then
        Dim celda As New TableCell()
        Dim boton As New LinkButton()

        boton.Text = "Detalles"
        If e.Row.Cells.Count > 1 Then
            boton.CommandArgument = GridView1.DataKeys(e.Row.RowIndex).Value.ToString()
        End If

        ' Añadimos el LinkButton a la celda, y la celda a la fila
        celda.Controls.Add(boton)
        e.Row.Controls.Add(celda)
    End If
Catch ex As Exception
    Throw ex
End Try
End Sub

Con esto lograremos el buscado esquema [Botón] [CAMPOS] [Botón].

Control Repeater


El control Repeater permite, como su propio nombre indica, repetir una plantilla compuesta por diversos controles un número de veces determinado, de acuerdo a una estructura de datos que pueda proveer los datos necesarios para poder rellenar esos controles. A continuación veremos un ejemplo en ASP.NET y VB.NET

Creación del control

Lo primero que debemos hacer es acceder al cuadro de herramientas y seleccionar el Repeater dentro de los controles de datos.

Control Repeater

Control Repeater

Una vez hayamos creado el control en nuestro aspx, deberemos introducir dentro del mismo aquellos controles que queramos repetir. Nuestro Repeater tendrá, en principio, el siguiente aspecto:

<asp :Repeater ID="Repeater1" runat="server">
</asp>

En el interior del mismo podremos introducir lo que queramos: controles HTML, tablas, controles ASPX… teniendo en cuenta que todo lo que introduzcamos se repetirá un número de veces igual al número de elementos del DataSource que le asignemos al Repeater.

Es aconsejable crear la sección a repetir antes de añadirla al Repeater. Si por ejemplo queremos insertar una tabla con un GridView y un Label, cuyo código sería el siguiente:

<table cellpadding="2" cellspacing="1" bgcolor="#f6f7f9">
  <tr>
    <td>
      <asp :GridView ID="DatosGridView" runat="server" AutoGenerateColumns="False"
              BackColor="White" BorderColor="#F6F7F9" BorderStyle="Solid"
              BorderWidth="1px" CellPadding="1"
              DataKeyNames="Id">
        <columns>
          <asp :BoundField DataField="Id" HeaderText="Id" SortExpression="Id" />
          <asp :BoundField DataField="Descripcion" HeaderText=" Descripcion " SortExpression=" Descripcion" />
        </columns>
      </asp>
      <asp :Label ID="NoResultadosLabel" runat="server" Text="Label"></asp>
    </td>
  </tr>
</table>

Lo que obtendríamos en nuestra página sería lo siguiente:

Ejemplo de Repeater

Ejemplo de Repeater

Una vez creado el componente a repetir, cortamos su código asociado y lo introducimos dentro del Repeater, del siguiente modo:

<asp :Repeater ID="Repeater1" runat="server">
  <itemtemplate>
    <table cellpadding="2" cellspacing="1" bgcolor="#f6f7f9">
      <tr>
        <td>
          <asp :GridView ID="DatosGridView" runat="server" AutoGenerateColumns="False"
                  BackColor="White" BorderColor="#F6F7F9" BorderStyle="Solid"
                  BorderWidth="1px" CellPadding="1"
                  DataKeyNames="Id">
            <columns>
              <asp :BoundField DataField="Id" HeaderText="Id" SortExpression="Id" />
              <asp :BoundField DataField="Descripcion" HeaderText="Descripcion" SortExpression=" Descripcion" />
            </columns>
</asp>
          <asp :Label ID="EtiquetaLabel" runat="server" Text="Label"></asp>
        </td>
      </tr>
    </table>
  </itemtemplate>
</asp>

Ya tenemos una tabla con un GridView y un Label dentro del Repeater. Ahora sólo tendremos que asociarle datos para que funcione.


Enlazado de datos

El Repeater tiene un evento llamado ItemDataBound, el cual se lanza al llamar al método DataBind() del Repeater, del siguiente modo:

Dim lista As New ArrayList
lista.Add(1)
lista.Add(2)
lista.Add(3)
lista.Add(4)

Repeater1.DataSource = lista
Repeater1.DataBind()

La llamada al DataBind() lanzará un evento ItemDataBound por cada uno de los elementos de su DataSource. El DataSource puede ser un ArrayList, un DataTable, un DataSet, etc. En nuestro caso, el evento se lanzará cuatro veces, uno por cada valor del ArrayList.

Además, el contenido del elemento del DataSource será accesible desde el evento ItemDataBound a través de la propiedad e.Item.

Protected Sub RepeaterRecibo_ItemDataBound(ByVal sender As Object, _
             ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) _
             Handles RepeaterRecibo.ItemDataBound

Dentro de este evento será donde realizaremos la asignación de los datos del interior del Repeater, es decir, la asignación de la propiedad Text del Label y el DataBind del GridView. Todo ello dependerá (si así lo deseamos, y es donde radica la potencia de este control) del elemento que en ese momento estemos manejando.

Si quisiéramos hacer dicho enlace, utilizaríamos el método FindControl() para instanciar el objeto en función de la iteración:


Dim EtiquetaLabel As Label
Dim DatosGridView As GridView
Dim dt As DataTable
Dim id As Integer

Select Case e.Item.ItemType
  Case ListItemType.Item, ListItemType.AlternatingItem
    id = CInt(CType(Repeater1.DataSource, ArrayList).Item(e.Item.ItemIndex))

    ' Se rellena el DataTable que obtenemos a partir del elemento del ArrayList
    dt = ConsultarPorId(id)

    ' Se realiza el enlace de los datos, tanto del GridView como del Label.
    EtiquetaLabel = CType(e.Item.FindControl("EtiquetaLabel"), Label)

    DatosGridView = CType(e.Item.FindControl("DatosGridView"), GridView)
    EtiquetaLabel.Text = dt.Rows(0).Item("Nombre").ToString
    DatosGridView.DataSource = dt
    DatosGridView.DataBind()

  End Select

Esto hará que se cargue un GridView y un Label para cada ID (en este caso se trata de IDs, podemos almacenar cualquier tipo de dato en el DataSource), repitiendo el código DataSource.Item.Count veces.

Añadir un tooltip a los elementos de un DropDownList


En ocasiones necesitamos utilizar controles de tipo SELECT cuyo ancho es demasiado pequeño para que su contenido sea completamente visible.

Una solución parcial a este problema es la de agregar un tooltip a cada elemento que resalte el contenido del mismo al pasar el ratón por encima.

Una forma sencilla (pero que desgraciadamente no funciona en todos los navegadores) consiste en agregar el elemento title a cada uno de los elementos option del select.

<select name=”Combo”>
    <option title=”Primer elemento”>Primer elemento</option>
    <option title=”Segundo elemento”>Segundo elemento</option>
    <option title=”Tercer elemento”>Tercer elemento</option>
    <option title=”Cuarto elemento”>Cuarto elemento</option>
    <option title=”Quinto elemento”>Quinto elemento</option>
    <option title=”Sexto elemento”>Sexto elemento</option>
    <option title=”Séptimo elemento”>Séptimo elemento</option>
    <option title=”Octavo elemento”>Octavo elemento</option>
</select>

Por desgracia, realizar esta opción en HTML puro y duro no resulta demasiado efectivo si queremos cambiar el contenido dinámicamente. Para realizar esta acción es .NET, partiríamos de un control de tipo DropDownList, al que llamaremos, por ejemplo, DropDownListElementos.

<asp:DropDownList ID=”DropDownListElementos” runat=”server”></asp:DropDownList>

Rellenaremos el contenido del control asignándole un origen de datos, por ejemplo, un DataTable llamado dt.

DropDownListElementos.DataSource = dt;
DropDownListElementos.DataValueField = dt.Columns[0].ColumnName;
DropDownListElementos.DataTextField = dt.Columns[1].ColumnName;
DropDownListElementos.DataBind();

Ahora que nuestro DropDownList contiene elementos, añadiremos de forma dinámica el atributo title a cada uno de sus elementos.

if (DropDownListElementos != null)
    foreach (ListItem li in DropDownListElementos.Items)
        li.Attributes.Add(“title”, li.Text);

Hecho esto, se añadirá, por ejemplo, en el método Load de la página que deseemos incluir este atributo, justo después de haber rellenado los elementos del DropDownList.

Title de un elemento de un SELECT

Title de un elemento de un SELECT

Añadir un tooltip a los elementos de un DropDownList


En ocasiones necesitamos utilizar controles de tipo SELECT cuyo ancho es demasiado pequeño para que su contenido sea completamente visible.

Una solución parcial a este problema es la de agregar un tooltip a cada elemento que resalte el contenido del mismo al pasar el ratón por encima.

Una forma sencilla (pero que desgraciadamente no funciona en todos los navegadores) consiste en agregar el elemento title a cada uno de los elementos option del select.

<select name=”Combo”>
    <option title=”Primer elemento”>Primer elemento</option>
    <option title=”Segundo elemento”>Segundo elemento</option>
    <option title=”Tercer elemento”>Tercer elemento</option>
    <option title=”Cuarto elemento”>Cuarto elemento</option>
    <option title=”Quinto elemento”>Quinto elemento</option>
    <option title=”Sexto elemento”>Sexto elemento</option>
    <option title=”Séptimo elemento”>Séptimo elemento</option>
    <option title=”Octavo elemento”>Octavo elemento</option>
</select>

Por desgracia, realizar esta opción en HTML puro y duro no resulta demasiado efectivo si queremos cambiar el contenido dinámicamente. Para realizar esta acción es .NET, partiríamos de un control de tipo DropDownList, al que llamaremos, por ejemplo, DropDownListElementos.

<asp:DropDownList ID=”DropDownListElementos” runat=”server”></asp:DropDownList>

Rellenaremos el contenido del control asignándole un origen de datos, por ejemplo, un DataTable llamado dt.

DropDownListElementos.DataSource = dt;
DropDownListElementos.DataValueField = dt.Columns[0].ColumnName;
DropDownListElementos.DataTextField = dt.Columns[1].ColumnName;
DropDownListElementos.DataBind();

Ahora que nuestro DropDownList contiene elementos, añadiremos de forma dinámica el atributo title a cada uno de sus elementos.

if (DropDownListElementos != null)
    foreach (ListItem li in DropDownListElementos.Items)
        li.Attributes.Add(“title”, li.Text);

Hecho esto, se añadirá, por ejemplo, en el método Load de la página que deseemos incluir este atributo, justo después de haber rellenado los elementos del DropDownList.

Title de un elemento de un SELECT

Title de un elemento de un SELECT