viernes, 15 de abril de 2011

No sólo arrastrar y soltar controles de Telerik

Llevo mucho tiempo trabajando con los controles de Telerik de ASP.NET AJAX. En mi opinión son magníficos (que no me oiga mi compañero Dani que me dirá que luego son un llorica), y debo decir que aceleran el desarrollo de aplicaciones en gran medida. En cualquier caso, los mayores problemas que siempre encuentro cuando trabajo con ellos son los siguientes:

·         Exceso de recursos embebidos de tipo .js.

·         Exceso de recursos embebidos de tipo .css.

·         Difícil personalización de estilos css.

Intentando resumen lo anterior en una sola frase diría algo como “cada vez que agrego un control de Telerik, sé que estoy penalizando en parte el rendimiento de mi página porque conlleva X peticiones extras para ficheros .js y .css, y además como tenga que modificar su apariencia estoy bastante jodido y sólo podré hacerlo a base de ensayo-error y con la ayuda de !important”.

Por ejemplo, para una página sencilla donde simplemente queremos obtener un listado (que pagine y puede ser ordenado) de la tabla Northwind.Categories, tenemos que incluir el siguiente código (el control ScriptManager y SqlDataSource se ponen pero lógicamente no forman parte de este ejemplo).

        <asp:ScriptManager ID="ScriptManager1" runat="server">

        </asp:ScriptManager>

        <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"

            SelectCommand="SELECT [CategoryID], [CategoryName], [Description] FROM [Categories]">

        </asp:SqlDataSource>

        <telerik:RadGrid ID="RadGrid1" runat="server" AllowPaging="True" AllowSorting="True"

            DataSourceID="SqlDataSource1" GridLines="None" PageSize="5">

            <MasterTableView AutoGenerateColumns="False" DataKeyNames="CategoryID" DataSourceID="SqlDataSource1">

                <RowIndicatorColumn>

                    <HeaderStyle Width="20px"></HeaderStyle>

                </RowIndicatorColumn>

                <ExpandCollapseColumn>

                    <HeaderStyle Width="20px"></HeaderStyle>

                </ExpandCollapseColumn>

                <Columns>

                    <telerik:GridBoundColumn DataField="CategoryID" DataType="System.Int32" HeaderText="CategoryID"

                        ReadOnly="True" SortExpression="CategoryID" UniqueName="CategoryID">

                    </telerik:GridBoundColumn>

                    <telerik:GridBoundColumn DataField="CategoryName" HeaderText="CategoryName" SortExpression="CategoryName"

                        UniqueName="CategoryName">

                    </telerik:GridBoundColumn>

                    <telerik:GridBoundColumn DataField="Description" HeaderText="Description" SortExpression="Description"

                        UniqueName="Description">

                    </telerik:GridBoundColumn>

                </Columns>

            </MasterTableView>

        </telerik:RadGrid>

 

Veamos el tamaño y el número de peticiones que son necesarias para que la página funcione correctamente.

clip_image002

El resumen es:

·         16 peticiones

·         249 KB

Está claro que al ejecutar el sitio web en modo depuración, algunos recursos embebidos del tipo están sirviendo su versión de depuración (en vez de su versión release que está optimizada). De hecho, ejecutando la página en modo lanzamiento, obtenemos las siguientes estadísticas:

·         16 peticiones

·         282 KB

Como vemos, tampoco es muy definitiva la diferencia entre la versión debug y release del sitio web, pero en cualquier caso nuestro modo activo será release para ajustarnos más a la realidad de una aplicación web publicada en producción.

La primera conclusión es que a cualquiera le parecen excesivas 16 peticiones y un tamaño de casi 300 KB para servir una página .aspx que sólo incluye un listado de 5 registros. Queda claro que siguientes peticiones a la misma página ya servirían todos los recursos embebidos desde la caché local (por ejemplo, paginar a la segunda página recupera todo las peticiones desde la caché local, excepto el PostBack del formulario), pero aún así, vamos a ser quisquillosos y vamos a intentar mejorar esa primera petición.

Como primera medida para intentar optimizar la página, vamos a combinar la solicitud de ficheros .js a través de la etiqueta CompositeScript del control ScriptManager. El cómo llego aquí y cómo entender mejor esto se puede hacer visitando este blog y el excelente artículo que hay al respecto. En este blog nos invitan a usar el control ScriptReferenceProfiler, que puede ser descargado desde aquí. Veamos qué información nos muestra.

clip_image004

Nos informa que hay 8 peticiones a ficheros .js que podrían ser combinadas, así que el código de nuestro control ScriptManager pasa a ser el siguiente:

        <asp:ScriptManager ID="ScriptManager1" runat="server">

            <CompositeScript>

                <Scripts>

                    <asp:ScriptReference Name="MicrosoftAjax.js" />

                    <asp:ScriptReference Name="MicrosoftAjaxWebForms.js" />

                    <asp:ScriptReference Name="Telerik.Web.UI.Common.Core.js" Assembly="Telerik.Web.UI" />

                    <asp:ScriptReference Name="Telerik.Web.UI.Grid.RadGridScripts.js" Assembly="Telerik.Web.UI" />

                    <asp:ScriptReference Name="Telerik.Web.UI.Common.jQuery.js" Assembly="Telerik.Web.UI" />

                    <asp:ScriptReference Name="Telerik.Web.UI.Common.jQueryPlugins.js" Assembly="Telerik.Web.UI" />

                    <asp:ScriptReference Name="Telerik.Web.UI.Common.Navigation.NavigationScripts.js"

                        Assembly="Telerik.Web.UI" />

                    <asp:ScriptReference Name="Telerik.Web.UI.ComboBox.RadComboBoxScripts.js" Assembly="Telerik.Web.UI" />

                </Scripts>

            </CompositeScript>

        </asp:ScriptManager>

 

Y el resultado es que hemos bajado de 16 peticiones a 9 (con el ahorro de tráfico de red en round-trips que ello conlleva).

clip_image006

Aún así, 9 peticiones siguen siendo muchas peticiones. De estas 9 peticiones, tenemos lo siguiente:

·         La propia página, intocable.

·         Un super-fichero .js que es la combinación de todos los ficheros especificados, intocable.

·         2 Sprites con imágenes para la rejilla.

·         5 ficheros .css con estilos para la rejilla.

La primera duda que quiero solucionar es cómo saber qué ficheros .js son necesarios cada vez que se incluye un control del tipo RadGrid (imagina que si hubiéramos incluido en la página más controles de Telerik, no sabríamos decir – ni tampoco el ScriptReferenceProfiler – que ficheros .js ha solicitado cada control de la página). Para obtener la información de que ficheros .js carga cada control de Telerik, podemos visitar el siguiente enlace Disabling Embedded Resources.

A propósito de la lectura del enlace anterior de Telerik, podemos leer que la propiedad EnableEmbeddedScripts permite habilitar o deshabilitar (por defecto está habilitada) la carga de todos los ficheros .js que ha solicitado el control. La pregunta ahora es ¿Son necesarios estos ficheros .js? Pues bien, me puedo equivocar, pero creo que la respuesta es que son necesarios sólo si utilizamos características del control que necesitan del código existente en esos ficheros para funcionar. Por ejemplo, si establecemos la propiedad a False para el control RadGrid, podemos observar que la rejilla sigue paginando, ordenando, etc. Sin embargo, seleccionar una fila desde el cliente no funciona puesto que el control no dispone del código necesario ya que no se han incluido los ficheros .js necesarios para llevar a cabo esta acción. Además, tampoco funciona desplegar el combo para seleccionar el nº de filas por página, etc.

 

De este modo, es muy importante no arrastrar y soltar simplemente un control en nuestra página .aspx y esperar que haga todo el trabajo por nosotros. Debemos investigar que características queremos activar de ese control, y así y con una configuración personalizada, podemos optimizar el tamaño y velocidad de carga de nuestra página.

 

Lógicamente, cuando hablamos de una suite de controles como la de Telerik, esto puede ser una tarea ardua y llevarnos un tiempo precioso, pero los resultados hablan por sí mismos.

 

El siguiente enlace puede ser de mucha utilidad. Optimizing output, page load time and the overall performance of RadControls for ASP.NET AJAX.

 

 

La personalización de un control como el RadGrid no se queda aquí y hay decenas de propiedades que podemos tocar para personalizar la salida del control. Este post no pretende ser un post sobre el control RadGrid, así que no veremos estas propiedades pero espero que el ejemplo anterior haya levantado tu curiosidad sobre cómo parametrizar un control.

Por otro lado, aún tenemos pendiente 7 peticiones que fueran realizadas para cargar el estilo del RadGrid. A este respecto lo primero que hay que decir es que, normalmente, no te servirá ninguna de las skins predeterminadas que te ofrece Telerik. Lo cierto es que se agradece el intento. Pero después de un tiempo con ellas te das cuenta de que limitan tu creatividad y los diseños web que las acompañan (recordemos que normalmente una aplicación web no es solo una rejilla perdida en la inmesidad de una página).

Para agilizar la carga de estilos de Telerik, tenemos el control RadStyleSheetManager, que lo que hace es combinar todos los recursos embebidos relativos a estilos de la página (css, imágenes, etc.) para realizar un número menor de peticiones. La verdad es que este control es muy bueno y hace su función, pero nosotros lo que queremos es dar nuestro propio estilo a los controles de Telerik. En cualquier caso, este control es buenísimo para combinar nuestras propias hojas de estilo (siempre que estén expuestas como recursos embebidos de un ensamblado) en una sola petición. En cualquier caso, si incluimos el control RadStyleSheetManager, ahora nuestro flujo de peticiones vuelve a mejorar (5 peticiones de css pasan a ser sólo 1 petición combinada). El resto de peticiones siguen siendo la propia página, el script combinado y los sprites.

clip_image008

Las opciones más relevantes a la hora de personalizar la apariencia de los controles de Telerik son (además de seleccionar una de las skin predeterminadas que trae la suite y que la sirva automáticamente):

·         Registrar una skin predeterminada a mano.

·         Crear un skin personalizada.

·         No utilizar ninguna skin.

Para una lectura más detallada sobre este tema puedes visitar el enlace How skins work.

Registrar una skin predeterminada a mano es una opción pero no lo veo.  Vamos a centrarnos en crear una skin personalizada o en, directamente, no utilizar ninguna skin.

Para no utilizar ninguna skin, simplemente tenemos que establecer la propiedad Skin a “”. Esto se traduce en lo siguiente:

clip_image010

Como podemos observar, ya “casi” no tiene estilo pero aún así hay imágenes que aparecen. De este modo (me hubiera gustado que no fuera así) hay que ir a las propiedades que controlan que aparezcan esas imágenes y cambiar su valor. Por ejemplo:

<PagerStyle FirstPageImageUrl="" PrevPageImageUrl="" NextPageImageUrl="" LastPageImageUrl="" />

 

De todas formas ¿Cuantas imágenes de este tipo nos vamos a encontrar? Uff, la verdad es que no parece una solución definitiva lo de “no utilizar ninguna skin”. Lo cierto es que hay controles que no me han dado nada de guerra de este modo, por ejemplo RadMenu o RadToolTip, pero el RadGrid parece que está un poco más difícil.

Crear una skin personalizada es otra opción válida. Puedes ver cómo realizar el proceso en Creating a custom skin. Las distintas opciones pasan por crearla desde cero (un gran curro, créeme), tomar una skin base y modificarla (gran curro pero menos que desde cero) y por último utilizar una herramienta on-line que proporciona Telerik y que simplifica en gran parte el proceso. La herramienta es Telerik Visual Style Builder. Además, ahora es posible cargar estas skins personalizas como recursos embebidos en un ensamblado. How to load skins from external assemblies.

En cualquiera de los casos, sería fantástico tener una referencia de las clases css que utiliza cada control de Telerik (igual que antes encontramos que ficheros .js utilizaba cada control). Para encontrar esta información es necesario navegar en la ayuda de Telerik hasta el control deseado y allí podremos encontrar tanto el HTML generado por el control como el CSS utilizado para su estilo. Por ejemplo, para el RadGrid podemos ver su HTML aquí y su CSS aquí.

En cualquier caso, también he de decirte que la ayuda de Telerik no está todo lo actualizada que debiera, así que al final tendrás que tirar de Firebug para saber exactamente que HTML y que estilos se están aplicando.

Para un siguiente post (y porque un proyecto en el que estoy inmerso ahora mismo lo requiere) veremos cómo crear una skin personalizada y servirla como un recurso embebido en un ensamblado.

Un saludo!

6 comentarios:

  1. Sergio, excelente artículo.

    Blanco y en botella.

    ResponderEliminar
  2. Hola Sergio, estoy tratando de modificar el form del control RadSocialShare para enviar mail, la idea es que yo quiero personalizar este form para que los labels y mensajes aparezcan en español, y ocultar o quitar el control captcha, sabes como se podria hacer?
    Gracias,

    ResponderEliminar
  3. tengo un error con los Skin de la grilla en Chrome se pierde , en explorer funciona bkn .... ahora tengo que jugar con los css de esta grilla mmmmm

    tiene un post donde hablas el uso de http://stylebuilder.telerik.com

    ResponderEliminar