Cambiar el Theme dinámicamente... con javascript

Es muy posible que en alguno de vuestros proyectos hayáis tenido la necesidad de cambiar el Theme de forma dinámica.

Lo típico es poner una lista desplegable con el listado de Themes y que al seleccionar uno u otro hiciérais un PostBack para saber qué Theme había elegido el usuario y cambiárselo. En codeproject hay un interesante artículo al respecto.

Sin embargo, como siempre, el problema de los PostBack es la -ya desfasada- recarga de página.

Resulta ser que la funcionalidad de los App_Themes de ASP.NET es una de las cosas más sencillas que hay (de hecho tengo en mente una mejora de lo que hace ASP.NET, pero esa ya es otra historia), de modo que el resultado final no deja de ser los típicos elementos "link" con su atributo "href" correspondiente, ubicado dentro del elemento "head" de nuestra página (podéis echarle un vistazo al código fuente de esta misma página).

Sabiendo esto, y con un poquito de javascript, no resulta demasiado difícil conseguir el objetivo de este artículo: cambiar el Theme de forma dinámica sin recargar la página.

El código de nuestra página es el típico de:

.aspx
        <asp:DropDownList ID="DropDownList1" runat="server" onchange="ddl_SelectedIndexChange(this)" />

.aspx.cs
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            InitilizeControl();
        }
    }

    private void InitilizeControl()
    {
       // Si no hay un Theme, desactivamos el control
        if (string.IsNullOrEmpty(Page.Theme))
        {
            Visible = false;
            return;
        }

        DropDownList1.Items.Clear();
       // Rellenamos la lista desplegable con todos los Themes que tengamos en nuestro sitio Web
        foreach (string theme in GetThemes())
        {
            DropDownList1.Items.Add(theme);
        }

       // Dejamos elegido el Theme que estamos usando.
        if (DropDownList1.Items.FindByValue(Page.Theme) != null)
            DropDownList1.Items.FindByValue(Page.Theme).Selected = true;
    }

    // Recoge un listado de los Themes de nuestro sitio web
    public static StringCollection GetThemes()
    {
        DirectoryInfo dInfo = new DirectoryInfo(HttpContext.Current.Server.MapPath("~/App_Themes"));
        DirectoryInfo[] dArrInfo = dInfo.GetDirectories();
        StringCollection list = new StringCollection();
        foreach (DirectoryInfo sDirectory in dArrInfo)
        {
            list.Add(sDirectory.Name);
        }
        return list;
    }


Hasta aquí todo muy sencillo, y probablemente ya hayáis hecho algo similar. De hecho, para mejorar la eficiencia, se puede obviar el GetThemes y meter nosotros mismos los Theme a mano.

La "magia" la realiza el javascript que viene a continuación:

.js
        // Recoge el valor seleccionado y llama a la función principal
         function ddl_SelectedIndexChange(ddl)
        {
            theme = ddl.options[ddl.selectedIndex].value;
            changeTheme(theme);
        }
               
        function changeTheme(theme)
        {
           // Recoge todos los elementos "link"
            var links = document.getElementsByTagName('link');
           
           // Los recorremos uno por uno
            for (i = 0; i< links.length; i++)
            {
                link = links[i];
                themeConst = 'App_Themes/';
                hrefTheme = link.getAttribute('href');

                oldThemeAux = hrefTheme.substring(hrefTheme.indexOf(themeConst) + themeConst.length);               
               
                // Tras simples funciones javascript, obtenemos el Theme que teníamos activo hasta el momento
                oldTheme = oldThemeAux.substring(0, oldThemeAux.indexOf('/'));
               
                // Conseguimos el nuevo Theme
                newTheme = hrefTheme.replace('/'+oldTheme+'/', '/'+theme+'/');

                // Y finalmente asignamos el nuevo Theme.
                link.setAttribute('href', newTheme);
            }
        }


Realmente sencillo, ¿no?

Sí, sencillo, pero limitado. Con esto sólo conseguimos cambiar el Theme una vez. Si el usuario recarga la página, no se guarda la selección hecha. Pero eso otra cosa