Solución al problema de los temas de ASP.NET basado en un Virtual Path Provider

febrero 14, 2011
By

Todo aquel que haya trabajado anteriormente con los temas de ASP.NET habrá disfrutado de la comodidad de definir y cambiar de una tema a otro, ya sea cambiado la configuración en el web.config o programáticamente. Sin embargo también habrá sufrido las limitaciones de los temas cuando se requiere cierta personalización, por ejemplo solo incluir algunos estilos en una página y no todos los que se encuentran en la carpeta del tema correspondiente o controlar el orden en que se insertan los CSS. Por otra parte no existe manera de controlar el atributo media o insertar estilos condicionales según la versión del navegador, tan útiles hasta tanto no sean estándares todos los navegadores, si es que algún día llega a ocurrir :-)

Egil Hansen brinda una solución alternativa (en inglés) How to take control of style sheets in ASP.NET Themes with the StylePlaceholder and Style control. Por su parte, la solución propuesta en este artículo es transparente y no requiere la modificación del código en las páginas que incluyen los estilos. Para ello definiremos un Virtual Path Provider ThemesVirtualPathProvider que se encargará de servir los ficheros del tema correspondiente. En las páginas la inclusión de los ficheros de estilos .css harán referencia al tema Default. Luego, en tiempo de ejecución se servirá el fichero del tema real definido en nuestra aplicación. En el ejemplo se ha usado un appSettings Theme para definir el nombre del tema que deberá cargarse, aunque se puede implementar la lógica qu mejor convenga según el escenario real, por ejemplo determinar el tema a partir de la URL o el dominio del pedido.

A continuación se muestra el código correspondiente al ThemesVirtualPathProvider.

[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.High)]
public class ThemesVirtualPathProvider : VirtualPathProvider
{
    public static void AppInitialize()
    {
        HostingEnvironment.RegisterVirtualPathProvider(new ThemesVirtualPathProvider());
    }

    public ThemesVirtualPathProvider() : base() { }

    private bool IsThemeFile(string virtualPath)
    {
        String checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
        return checkPath.StartsWith("~/App_Themes", StringComparison.InvariantCultureIgnoreCase);
    }

    public override VirtualFile GetFile(string virtualPath)
    {
        if (IsThemeFile(virtualPath))
        {
            // Si se trata de un fichero dentro de la carpeta App_Themes
            // devolver el ThemeVirtualFile correspondiente al tema definido en el web.config.
            string currentTheme = ConfigurationManager.AppSettings["Theme"];
            if (!string.IsNullOrEmpty(currentTheme))
            {
                return new ThemeVirtualFile(virtualPath, currentTheme);
            }
        }
        return base.GetFile(virtualPath);
    }
}

El método GetFile es quien hace el trabajo. Si el pedido corresponde a un fichero dentro de la carpeta App_Themes, determina el tema que se ha configurado, y devuelve una instancia de la clase ThemeVirtualFile que devuelve el contenido del fichero correcto, y el del fichero definido estáticamente el el código de la página. A continuación se muestra el código de la clase ThemeVirtualFile.

[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public class ThemeVirtualFile : VirtualFile
{
    private byte[] content;

    public ThemeVirtualFile(string virtualPath, string themeName) : base(virtualPath)
    {
        virtualPath = virtualPath.Replace("Default", themeName);
        string checkPath = HttpContext.Current.Server.MapPath(VirtualPathUtility.ToAbsolute(virtualPath));
        if (!File.Exists(checkPath))
        {
            throw new FileNotFoundException("File not found", checkPath);
        }
        content = File.ReadAllBytes(checkPath);
    }

    public override Stream Open()
    {
        MemoryStream stream = new MemoryStream(content);
        stream.Seek(0, SeekOrigin.Begin);
        return stream;
    }
}

Para que la solución propuesta funcione hay que indicar en IIS que los ficheros .css y las imágenes sean tratados por ASP.NET en lugar de ser servidos directamente por IIS. Dependiendo de la versión de IIS el procedimiento para hacer esto varía. El ejemplo para descargar está preparado para funcionar con IIS7.5 en modo ASP.NET 4.0 Integrated. En el web.config deberán registrarse con el Handler System.Web.StaticFileHandler todos aquellos ficheros que deban ser tratados por el ThemesVirtualPathProvider, es decir los ficheros .css y las imágenes que componen los temas.

Esta solución puede servir de punto de partida a todo aquel que necesite cargar los estilos o temas dinámicamente, y ciertamente resuelve los problemas que presenta la solución nativa de temas propuesta por ASP.NET.

Descargar código de ejemplo.

One Response to Solución al problema de los temas de ASP.NET basado en un Virtual Path Provider

  1. espinete on agosto 19, 2011 at 2:37 pm

    Interesante, graciass

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Acerca del autor...

Lester Sánchez

Web: http://www.linkedin.com/in/lestersanchez
Lester Sánchez
Profesor de la Facultad de Matemática y Computación de la Universidad de La Habana. Entusiasta de las tecnologías .NET y en especial de SharePoint. Webmaster de weboomania.comLeer completo