sábado, 31 de diciembre de 2011

Usando CSS Sprites en una columna de un GridView

La técnica de sprites es bastante antigua... bueno, yo los usé programando para la Commodore 64, así que si, tenemos unos cuántos años!

En este caso vamos a ver como utilizar la técnica de sprites con hojas de estilos (CSS) para identificar un  un estado determinado en un registro de datos. Para eso listaremos los datos en  un GridView en una página web y usaremos una columna con imágenes para mostrar los diferentes valores de estado del registro.

Este ejemplo apunta a mostrar cómo usar estilos para posicionar una imagen en un elemento dinámicamente, y si bien las imágenes solo muestran un estado, podrían utilizarse botones o links en su lugar para lograr acciones determinadas. En este ejemplo se verá también cómo aplicar wilcards (comodines) en las clases de una hoja de estilos.


La técnica es bastante sencilla, pero muy útil. En este ejemplo se verá cómo establecer una imagen para cada valor de la columna estado. Para este ejemplo tendremos 4 posibles estados diferentes, del 0 al 3, pero puede ser cualquier otro tipo de dato, siempre que sean valores definidos, para que sean identificables.

Lo primero que tenemos que tener es un gráfico con las imágenes que  representan a cada uno de los estados. Según el valor del estado, se mostrará la parte del gráfico correspondiente al valor de estado del dato.

El gráfico de este ejemplo tiene 5 imágenes: 4 corresponden a cada valor posible del campo Estado, y una la utilizaré solamente a modo de  ejemplo para que la imagen cambie cuando se pose el mouse en ella. En el ejemplo que dejo para bajar, en el ToolTip de la imagen se muestra el valor del estado, mientras la imagen cambia a un signo de interrogación.

Imágenes que usaremos en este ejemplo
Este gráfico tiene 60 x 40 pixels, y a cada imagen se le asignó un espacio de 20 x 20 pixels. 


La cosa es bastante simple: mediante una clase CSS para cada estado se establece un gráfico de fondo al objeto que contiene el dato en la celda correspondiente de la grilla. En nuestro caso será un span de HTML (más abajo se explica por qué). Luego se modifica la posición de la imagen a mostrar, desplazándola n pixels en los ejes x e y para representar cada valor de estado.
El gráfico puede tener cualquier forma. Lo que importa es el posicionamiento que se le de mediante los estilos.

Para mostrar el relojito verde en el objeto que contiene el valor del estado el desplazamiento será de 20 px hacia la izquierda y  20 px  hacia arriba.


El ejemplo:
Usaremos un GridView en una página web, con dos columnas. La columna que importa es la de Estado, (cuyo campo se llama STATUS). Los pasos son:

Agregar un GridView a la página, estableciendo AutoGenerateColumns="False" y la columna STATUS como TemplateField. El código de la grilla quedaría así:

            <asp:GridView ID="GridView1" AutoGenerateColumns="False" runat="server">
                <Columns>
                    <asp:TemplateField HeaderText="Estado">
                        <EditItemTemplate>
                            <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("STATUS") %>'></asp:TextBox>
                        </EditItemTemplate>
                        <ItemTemplate>
                            <asp:Label ID="Label1" runat="server" Text='<%# Bind("STATUS") %>'></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <asp:BoundField DataField="TAREA" HeaderText="Descripción" />
                </Columns>
            </asp:GridView>

Vamos a crear un DataTable como fuente de datos para poblar la grilla. Podríamos usar  cualquier fuente de datos, pero para este ejemplo, en el CodeBehind de la página, tendremos este código:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!this.Page.IsPostBack)
        {
            // Asignar la fuente de datos al GridView y mostrarlos.
            GridView1.DataSource = getDatos();
            GridView1.DataBind();
        }
    }
 
    private DataTable getDatos()
    {
        // Construir la tabla con datos.
        // En este caso para Estado (STATUS) utilizaremos 
        // 4 valores enteros, entre 0 y 3.
        DataTable dt = new DataTable();
        dt.Columns.Add(new DataColumn("STATUS", typeof(int))); // Estados, de 0 a 3 en este caso.
        dt.Columns.Add(new DataColumn("TAREA"));
 
        // Llenar tabla con los datos.
        DataRow dr = dt.NewRow();
        dt.Rows.Add(1, "Obtener lista de requisitos.");
        dt.Rows.Add(2, "Llamar al cliente.");
        dt.Rows.Add(0, "Presentar diseño.");
        dt.Rows.Add(3, "Modificar planos.");
        dt.Rows.Add(1, "Agregar funcionalidad requerida.");
        dt.Rows.Add(3, "Generar localización nuevo proyecto.");
 
        // Retornar tabla
        return dt;
    }


Al ejecutar, luego de cargarse los datos, veremos el valor de STATUS en la columna Estado.


Lo que queremos aquí son cuatro cosas:
  1. No ver el valor de Estado. 
  2. Ver en su lugar una imagen por cada valor de estado (valor del campo STATUS).
  3. Que la imagen cambie cuando se posa el mouse sobre ella y 
  4. Mostrar en el ToolTip de la imagen el valor de STATUS.

Vamos entonces a concentrarnos en el ItemTemplate correspondiente a la columna de Estado. Ahi veremos que la propiedad Text del Label hace un Bind al campo STATUS. 
     
<ItemTemplate>
     <asp:Label ID="Label1" runat="server" Text='<%# Bind("STATUS") %>'></asp:Label>
</ItemTemplate>

Para no ver el valor de STATUS, simplemente dejamos Text="" (vacío).

El truco principal está en utilizar el DataFormatString que se aplica al enlazar el campo. Es decir, cuando se hace Bind("STATUS"), se puede agregar un formato al texto resultante. Por ejemplo, Bind("STATUS", "{0:0.0}") mostrará el valor numérico con dos decimales.

Utilizando esto, la idea es asignarle a la propiedad CssClass una clase CSS dinámica para cada valor de STAUS. Lo que se quiere lograr es, por ejemplo:

CssClass="tareaStyle_0" para el STATUS = 0
CssClass="tareaStyle_1" para el STATUS = 1
CssClass="tareaStyle_2" para el STATUS = 2
CssClass="tareaStyle_3" para el STATUS = 3

Luego aplicaremos un estilo a cada uno de ellos en particular.
Aplicando entonces el formato al enlazar el dato, quedaría así:

CssClass='<%# Bind("STATUS", "tareaStyle_{0}") %>'
donde {0} será reemplazado con el valor del Estado STATUS.

El código de la grilla quedaría finalmente como se muestra a continuación, utilizando la misma técnica para el ToolTip:

<ItemTemplate>
    <asp:Label ID="Label1" runat="server" CssClass='<%# Bind("STATUS", tareaStyle_{0}") %>' Text="" ToolTip='<%# Bind("STATUS", "El estado de esta tarea es {0}") %>'></asp:Label>
</ItemTemplate>

A los fines de simplificar el ejemplo, eliminé el EditItemTemplate del GridView, ya que no mostraremos la fila en modo edición.

<asp:GridView ID="GridView1" AutoGenerateColumns="False" runat="server">
    <Columns>
        <asp:TemplateField HeaderText="Estado">
            <ItemTemplate>
                <asp:Label ID="Label1" runat="server" CssClass='<%# Bind("STATUS", "tareaStyle_{0}") %>' Text="" ToolTip='<%# Bind("STATUS", "El estado de esta tarea es {0}") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="TAREA" HeaderText="Descripción" />
    </Columns>
</asp:GridView>

La hoja de estilos. 
Ahora solo falta aplicar los estilos correspondientes a cada unos de los diferentes valores de estado.
Creamos una nueva hoja de estilos. Debemos contar también con el archivo de imagen que contiene los iconos que representarán los valores de STATUS, que en este caso se llamará icoStatus.png.

La forma de crear las clases CSS es primero crear una en común para todos estilos de los estados. Como tenemos 4 estados diferentes que se llamarán tareaStyle_0, tareaStyle_1, tareaStyle_2 y tareaStyle_3 lo más lógico sería crear una enumerando todos, de esta manera:

.tareaStyle_0, .tareaStyle_1, .tareaStyle_2, .tareaStyle_3
{
    /* ... estilos ...*/
}

Pero para no tener que estar agregando una clase para cada diferente valor, en lugar de eso utilizaremos wilcards en la clase para hacer que se corresponda solo con la parte del nombre de la clase .tareaStyle_

Al convertir la columna a TemplateField, el objeto que queda dentro del ItemTemplate de la columna es control web Label, por lo que éste será traducido por el servidor a HTML y enviado al navegador como un <span>. Así definiremos los siguientes estilos para el atributo class del span:


span[class*='tareaStyle_'] 
{
width:20px;
height:20px;
display:block;
margin:0 auto 0 auto;
background-image:url('icoStatus.png');
background-repeat:no-repeat;
cursor:pointer;
} 

Es importante tener en cuenta lo definido como ancho y alto para el span (20px por 20px), ya que funcionará como la ventana donde se mostrará la imagen correspondiente del gráfico de fondo, cuando lo posicionemos correctamente según el valor de STATUS. Luego se define la imagen de fondo, sin repeticiones, y finalmente que el cursor del mouse aparezca como una manito, como lo hace en los links.
Si no definimos una posición para la imagen de fondo, entonces veremos solo el primer icono de arriba a la izquierda.

Ahora solo queda posicionarlo para cada uno de los estados. En estos no podremos usar ningún comodín, debiendo crear la clase correspondiente a cada estado, pero que solamente contendrá el desplazamiento del gráfico para obtener la posición de la imagen deseada en el span de 20 x 20 pixels.


/* estilos para cada estado */
 
.tareaStyle_0 {
    background-position: 0 0;
}
 
.tareaStyle_1 {
    background-position: -20px 0;
}
 
.tareaStyle_2 {
    background-position: 0 -20px;
}
 
.tareaStyle_3 {
    background-position: -20px -20px;
}


Como se ve, la posición corresponde al lugar en que se encuentra cada imagen.



Por último solo nos queda el estilo para cambiar la imagen al pasar el mouse por encima. En este caso utilizaremos otra vez los wilcards en la clase de estilos y la imagen correspondiente al signo de interrogación:

span[class*='tareaStyle_']:hover {
    background-position: -40px 0;   
}


Conclusión:
La ventaja de usar esta técnica es que se descarga una sola imagen desde el servidor, y luego el navegador calcula qué mostrar en base a lo definido en los estilos, haciendo en general mucho más rápida la descarga  de la página.

En la web se pueden encontrar varias herramientas para generar este tipo de gráficos combinados. CSS Sprites  es una página, donde se suben las imágenes y devuelve un solo gráfico con todas las combinadas. La sugerencia es que suban todas las imágenes de un mismo tamaño para que sea más fácil ubicarlas luego, pero depende de cada proyecto.

Recursos:

  • CSS Sprites: Herramienta para generar los CSS sprites
  • El código de ejemplo de esta nota. Hacer clic en "Descargar" para bajar todos los archivos juntos. Luego incluirlos en un proyecto web para hacerlo funcionar.

lunes, 19 de diciembre de 2011

Mi Gadget. Mi Geek. Mi amigo Gabriel


Gabriel D. Sulé es un experto arquitecto y programador sobre tecnologías Microsoft .Net, especialista y coach de SharePoint, y creo que uno de los pocos buenos conocedores de BizTalk de la argentina también.

Más allá de ser una excelente persona, Gabriel ha recopilado mucha información útil sobre tecnologías Microsoft y herramientas casi fundamentales para nosotros los desarrolladores. Basta buscar en su blog alguna palabra clave relacionada con esto para encontrarse con un compendio de cosas interesantes. Por ejemplo, simplemente puse "CSS" y encontré cosas muy útiles.

Así que el dato útil que quiero dejar es la dirección de su blog: http://gabrielsule.blogspot.com

Tuve la suerte de compartir interesantes proyectos con él en alguna oportunidad, en los que me divertí mucho. Ojalá tengan la oportunidad de conocer a Gabriel personalmente. Vale la pena!




viernes, 18 de noviembre de 2011

Un control para subir archivos (bastante) versátil.


Notas de actualización:
Si bien este artículo se vio ya superado por nuevas tecnologías, queda como una buena referencia de aprendizaje. Si estás buscando un control ya hecho, con buenas funcionalidades, recomiendo que busques las referencias al final del artículo.

La Web 2.0 permitió a los usuarios de internet interactuar bases de datos y archivos y procesos del servidor, lo que dió lugar a los sitios dinámicos que se alimentan con información de los usuarios.  Un control interesante en ASP:NET que permite esto es el control FileUpload para subir archivos desde la máquina del usuario al servidor sin necesidad de usar FTP. Con este tipo de controles, compartir fotos en la red y otro tipo de archivos se transformó en un juego de niños.

Sin embargo, el control que viene con Visual Studio permite subir un archivo por cada control que se incluya, lo que limita la cantidad de archivos a la cantidad de controles incluidos en la página. Una opción es preguntarle al usuario cuántos archivos quiere subir para que podamos crear dinámicamente la cantidad de controles que necesitemos, pero a veces este procedimiento es un tanto molesto.

Inspirándome en este artículo decidí construir este control web (Web User Control) que aquí publico, el que  permite agregar dinámicamente la cantidad de archivos a subir a medida que los vamos eligiendo. El secreto se basa en ir agregando los controles desde JavaScript, para evitar perder los archivos que se fueron cargados con algún eventual Postback producido por algún  otro evento de la página.

Este control dista bastante de esos como los que usa Google o Facebook, donde se pueden seleccionar todos archivos de un saque, pero es bastante simple y práctico. Aquí está disponible el código fuente, por lo que cada uno pude hacerle las mejoras que requiera para su proyecto. Por otro lado, tampoco es asincrónico. Para eso recomiendo hacer algo con el control Ajax AsyncFileUpload.

Volviendo a lo nuestro, así se ve el control en Internet Explorer 8 y Opera 11 , con la hoja de estilos CSS adjunta. Con Firefox se ve bastante parecido a esto también.

Chrome y Safari muestran el control FileUpload de otra forma, por lo que habrá que modificar los estilos correspondientes si se quiere cambiar el aspecto.


Este control tiene algunas propiedades que lo hacen relativamente versátil y facilitan su uso.


Propiedad

Descripción

Valor default

Titulo

Título a mostrar en el control. Para no mostrar nada, establecerlo en string.empty.

Si no se establece esta propiedad, se muestra "Archivos a subir".

Comment

Una nota depués del título.

string.empty

DestinationFolder

Carpeta donde se guardarán los archivos en el servidor. Si no existe, puede ser creada. Se establece el path relativo y no el físico. Para que la carpeta sea creada en el servidor, el usuario de internet debe tener premisos de escritura. Esta propiedad es obligatoria.

-

UploadButtonIsVisible

Muestra el botón de "Subir archivos" dentro del control. Sirve para subir los archivos de forma independiente al resto de las acciones de la página. Si se quiere realizar otras acciones antes de subir los archivos se puede dejar este botón no visible y llamar manualmente al evento UploadFiles(CreateDir).

false (no visible)

FileExtensionsEnabled

Limita los archivos a subir a las extensiones aquí definidas. Las extensiones se deben establecer separadas por "|" (pipe). Ej: ".txt|.jpg|.gif". Si no se establece esta propiedad, cualquier extensión es admitida.

null

HasFile

Solo lectura. Indica si el control tiene al menos el primer archivo seleccionado.

false

MaxFilesLimit

Cantidad máxima de archivos que el usuario puede agregar. Si se establece en cero, funciona igual que el control estándar FileUpload (solo deja subir un archivo). Si esta propiedad no se establece no limita la cantidad de archivos a agregar.

null

MaxUploadSize

Límite en bytes de la tranferencia de todos los archivos a subir. Ver debajo más detalles sobre limitaciones.

Hasta 4 MB

El control tiene solo un método:


Método

Descripción

UploadFiles(bool CreateDir)

Realiza la subida de los todos los archivos seleccionados en el control. Si el parámetro CreateDir se establece en true, indica que el directodio destino definido en
DestinationFolder debe crearse si no existe.

Limitaciones:

  • Por cuestiones de seguridad, de manera predeterminada con el control FileUpload se pueden subir de hasta 4 megabytes de una sola vez. Pasado ese límite se producirá un error "denial of service". Este control no captura ese error.
    Sin embargo, tal como se explica en la especificación de la clase FileUlpload, se puede aumentar ese límite modificando el web.config de la aplicación. Por ejemplo para establecer el límite en 100MB, que deben ser subidos en menos de 15 minutos, se debe agregar <httpRuntime  maxRequestLength="102400" executionTimeout="900"/> dentro de <system.web>  del archivo web.config de nuestra aplicación web.
  • El control está escrito en C#, por lo que los programadores de Visual Basic .Net deberán pasarlo. Se agradece el aporte de quien lo haga. Una página que puede ayudar a ésto es esta, que permite convertir C# en VBNet y viceversa. Funciona bastante bien.
  • Solo se puede utilizar un control por página. Para utilizar varios en una misma página es necesario realizar modificaciones al código.

Uso simple:
El siguiente video explica como implementar el control de manera  simple y rápida con Visual Studio 2010.



Download de archivos:
Desde aquí se puede bajar el código fuente del control con sus estilos para usarlo directamente.


Aquí hay un ejemplo de configuración y uso en un ejemplo para Visual Studio 2010.

  • MultiFileUpload_VS2010example.rar (actualizado nov.2012 con función para mostrar los archivos subidos. Hacer Archivo > Descargar para bajar el proyecto completo ).


Conclusión:
Más allá de la utilidad de este control, en el código fuente se puede ver cómo manejar propiedades dentro de un Web User Control utilizando el StateBag ViewState, registrar código JavaScript en una página o en su Masterpage con RegisterStartupScript y hasta cómo usar básicamente expresiones regulares.

Espero sea de utilidad.

Actualización:

Para soluciones más concretas quizás quieras ver este control, que está hecho pensando en las  posibilidades del javaScript sobre los navegadores más nuevos.

Actualización 2:
Con la evance y difusión de jQuery como standard, sin dudas el control que yo utilizaría es jQuery File Upload.


martes, 15 de noviembre de 2011

De esto se trata.

"En el mundo hay 10 tipos de personas: las que entienden binario... y las que no".
Así que este no es más que otro blog de tecnología, sobre programación con C#, algo de JavaScript y alguna que otra cosilla que aparezca.