René Pacios

/* Overflow My Brain & More */

Como crear y agregar controles personalizados nuestro sitio ASP.NET de forma poco intrusiva

Una de las características que más repercusión ha tenido en ASP.NET Web Forms, y que personalmente creo que ha sido clave en el éxito, es la posibilidad de crear controles personalizados o extender los ya existentes. Esta característica está presente desde la aparición de .NET, donde en principio, únicamente existía un tipo de proyecto web dentro de la plataforma,  poco a poco ha ido evolucionando y dejando pasos a nuevos paradigmas como ASP.NET MVC, ASP.NET Web API, etc. Sin embargo Microsoft sigue apostando por esta tecnología, ASP.NET WebForms nos permite un rápido desarrollo de aplicaciones (RAD Application) basadas en Arrastrar y Soltar, crear componentes visuales reutilizables, paradigma de programación similar al de una aplicación de escritorio, hacen que resulte una opción a tener en cuenta  para algunos escenarios como por ejemplo una aplicación utilizada en una intranet local, pequeñas aplicaciones puntuales dirigidas por datos.

¿Con todo esto quiero decir que no tengamos en cuenta otras opciones cuando se trate de aplicación para intranet? No de ningún modo, la idea que trato de plasmar es que si dispones de una aplicación con Web Forms puedes sacar mucho partido de ella, y no porque otro paradigma como ASP.NET MVC, sea más acertado para el escenario X debes tirarla a la basura y rehacerla de todo.

Bueno, y después de este disclaimer de porque un domingo del 2014 me pongo a postear sobre Web Forms, es para hablaros de una característica no muy conocida, y que nos facilita un poco más la labor de extender controles. Vemos un poco de código:

public class SmartImage : System.Web.UI.WebControls.Image
{
    [EditorAttribute(typeof(System.Web.UI.Design.ImageUrlEditor), typeof(UITypeEditor))]
    public string FallBackUrl
    {
        get
        {
            var s = (string)ViewState["FallBackUrl"];
            if (s == null) return string.Empty;
            return s;
        }
        set
        {
            ViewState["FallBackUrl"] = value;
         }
    }

protected override void OnLoad(EventArgs e)
{

    if (!string.IsNullOrEmpty(FallBackUrl))
    {
        string defaultImageUrl = ResolveUrl(FallBackUrl);
        this.Attributes["onerror"] = String.Format("this.src = '{0}'", defaultImageUrl);

    }
    base.OnLoad(e);
}

}

 

Para este ejemplo sencillo, hemos creado un control SmartImage que extiende el control image de ASP.NET en la que hemos añadido una un nuevo parámetros FallBackUrl. La idea del control es que si se produce un error al mostrar una imagen (imaginemos un error 404 por una ruta mal indicada) se muestre una imagen por defecto, en vez de la típica aspa roja de IE. 

Para utilizar este nuevo control, podemos agregarlo directamente desde la dll generada al cuadro de herramientas de visual estudio, o si por el contrario disponemos de una solución que contenga el proyecto con el control, junto con el proyecto web Visual Studio detecta estos artefactos y creará la pestaña del control por nosotros de modo que sólo nos quedaría arrastrar y soltar.image

El paso siguiente, después de agregar el control a nuestra página, será establecer los diferentes parámetros, e decir, la ruta de la imagen a mostrar junto con la ruta a la imagen por defecto. Evidentemente en este caso particular referenciando contenido estáticos el control carece de sentido, sin embargo puede resultar de gran utilidad al mostrar contenido que proviene de una base de datos en donde se almacenan rutas a imágenes que pueden haber sido movidas.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1.Default" %>

<%@ Register assembly="WebControls" namespace="WebControls" tagprefix="cc1" %>
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <cc1:SmartImage ID="SmartImage1" runat="server" ImageUrl="~/Images/son-goku.jpeg" FallBackUrl="~/Images/default-thumb.gif" />
        <cc1:SmartImage ID="SmartImage2" runat="server" ImageUrl="~/Images/son-gERRRORoku.jpeg" FallBackUrl="~/Images/default-thumb.gif" />

    </div>
    </form>
</body>
</html>

Como podéis ver, resulta muy sencillo agregar un control personalizado. En este ejemplo hemos agregado un error a propósito, en la url del control SmartImage2 de forma que si ejecutamos la página veremos lo siguiente:

image

Que ha pasado, como veis en la primera imagen se ha mostrado sin problemas, sin embargo la segunda no ha podido cargarse correctamente de modo que ha mostrado la imagen indicada en el parámetro FallBackUrl. Toda esta magia de los controles de ASP.NET se consigue porque hemos añadido una función en lado de cliente en el evento OnError de la imagen, si inspeccionamos el código fuente de la página vemos que el control image se ha renderizado de una forma un poco diferente a lo normal:

    <div>
        <img id="SmartImage1" onerror="this.src = &#39;/Images/default-thumb.gif&#39;" src="Images/son-goku.jpeg" />
        <img id="SmartImage2" onerror="this.src = &#39;/Images/default-thumb.gif&#39;" src="Images/son-gERRRORoku.jpeg" />      
    </div>

Vale esto está genial, pero resulta que tengo una aplicación X con N cientas de páginas, y resulta tedios agregar la referencia al control en todas, ellas cambiar los controles image cuando correspondan, etc. ¿no hay otra manera más sencilla de agregar un control personalizado a mi página?

Pues la respuesta es sí, existe una característica en ASP.NET no muy conocida, que permite asignar un nuevo tipo (clase) a otro tipo (a otra clase) en tiempo de compilación con el elemento tagMApping disponible a partir de la versión 2.0 de .NET. Con esta característica, podríamos especificar que siempre que se utilice la Etiqueta Label, se utilice la clase  personalizada que cambie completamente el texto mostrado. Como seguramente ya se habrá dado cuenta, la idea consiste en asociar el tag image, a nuestro nuevo control SmartImage. Para hacer esto debemos indicar el siguiente mapeo en nuestro archivo de configuración del siguiente modo:

<?xml version="1.0"?>
<configuration>
    <system.web>
      <pages>
        <tagMapping>
          <add tagType="System.Web.UI.WebControls.Image" mappedTagType="WebControls.SmartImage, WebControls"/>
        </tagMapping>
      </pages>
      <compilation debug="true" targetFramework="4.0" />
    </system.web>
</configuration>

A partir de ahora ya podemos incorporar la propiedad FallBackUrl a todos los controles image de nuestro proyecto, como puede verse en el siguiente ejemplo:

  <div>
        <cc1:SmartImage ID="SmartImage1" runat="server" ImageUrl="~/Images/son-goku.jpeg" FallBackUrl="~/Images/default-thumb.gif" />
        <cc1:SmartImage ID="SmartImage2" runat="server" ImageUrl="~/Images/son-gERRRORoku.jpeg" FallBackUrl="~/Images/default-thumb.gif" />

        <asp:Image ID="Image1" runat="server" ImageUrl="~/Images/OTROERROR DE IAMGEN" FallBackUrl="~/Images/default-thumb.gif"  />

    </div>

Nota: Debemos tener en cuenta que TagMapping funciona en tiempo de compilación, de modo que no tendremos intellisense ni veremos la propiedad FallBackUrl en nuestro cuadro de propiedades como sucedía con el control FallBack

El siguiente paso ya depende un poco de la implementación que le queramos dar, como os habréis dado cuenta, no hemos adelantado mucho, sigo teniendo mi aplicación con X páginas y N imágenes en cada una. Bien para ello vamos a establecer una ruta por defecto para el parámetro FallBackUrl, para el ejemplo vamos a hardcoderarlo directamente en el control, pero lo ideal sería poder obtener la configuración del archivo de configuración u otro mecanismo más versátil. Nuestra clase SmartImage quedaría del siguiente modo:

public class SmartImage : System.Web.UI.WebControls.Image
{
    [Editor(typeof(System.Web.UI.Design.ImageUrlEditor), typeof(UITypeEditor))]
    public string FallBackUrl
    {
        get
        {

            var s = (string)ViewState["FallBackUrl"];
            // if (s == null) return string.Empty;
            //para mostrar el ejemplo de una configuración por defecto
            if (s == null) return "~/Images/default-thumb.gif";

            return s;
        }
        set
        {
            ViewState["FallBackUrl"] = value;

        }
    }

    protected override void OnLoad(EventArgs e)
    {

        if (!string.IsNullOrEmpty(FallBackUrl))
        {
            string defaultImageUrl = ResolveUrl(FallBackUrl);
            this.Attributes["onerror"] = String.Format("this.src ='{0}'", defaultImageUrl);

        }
        base.OnLoad(e);
    }
}

 

Veamos que ocurre si ahora agregamos un control image a nuestro proyecto sin establecer el parámetro FallBackUrl. Para ello creamos una nueva página como la siguiente:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ControlesImageNormales.aspx.cs" Inherits="WebApplication1.ControlesImageNormales" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Image ID="Image1" runat="server" ImageUrl="~/Images/son-goku.jpeg" />
            <asp:Image ID="Image2" runat="server" ImageUrl="~/Images/OTROERROR DE IAMGEN" />
        </div>
    </form>
</body>
</html>

Como vemos no existe referencia al control smartImage por ningún lugar. Sin embargo si ejecutamos la página y luego utilizamos la opción mostrar código fuente veremos algo similar a esto:

image

 <div>
            <img id="Image1" onerror="this.src =&#39;/Images/default-thumb.gif&#39;" src="Images/son-goku.jpeg" />
            <img id="Image2" onerror="this.src =&#39;/Images/default-thumb.gif&#39;" src="Images/OTROERROR%20DE%20IAMGEN" />
        </div>

Sin cambiar nada en nuestra página hemos conseguido aplicar el comportamiento de smartImage a todos los controles de tipo imagen de nuestra aplicación web, de una forma nada intrusiva. Esta característica nos abre un gran abanico de posibilidades, pudiendo incluso de una forma sencilla modificar al forma en que se renderizan algunos controles para hacerlos las usables o incluso más Seo Friendly cuando los ControlAdapters no son suficiente.

Espero que os resulte de utilidad. Os he dejado una copia del proyecto en mi cuenta de github: https://github.com/rene15009/ASP-ExtendControls 

Nos leemos!!!

Acerca de René

René Pacios es un apasionado de la tecnología, autodidacta, emprendedor, le encanta el desarrollo web, para moviles, aplicaciones, todo aquello que automatice tareas y haga que las máquinas trabajen para él. Es un gran fan de las tecnologías Microsoft, y le encanta estar a la última siempre que el tiempo se lo permite. Siempre quiso ser cantante, pero creo que en esta vida se va a quedar sólo en canta-mañanas

               

Comentarios (1) -

Agregar comentario

Loading