Cómo tomar el control de las hojas de estilo en ASP.NET Themes con StylePlaceHolder y Style control

StackOverflow https://stackoverflow.com/questions/328763

Pregunta

Actualización: Esto se convirtió en una publicación de blog, con enlaces y código actualizados, en mi blog: https://egilhansen.com/2008 / 12/01 / cómo-tomar-control-de-hojas-de-estilo-en-asp-net-themes-with-the-styleplaceholder-and-style-control /


El problema es bastante simple. Cuando usa Temas ASP.NET, no tiene mucho que decir sobre cómo se representan sus hojas de estilo en la página.

El motor de renderizado agrega todas las hojas de estilo que tiene en su carpeta de temas en orden alfabético, usando < link href = & # 8221; ... & # 8221; notación.

Todos sabemos que el orden de las hojas de estilo es importante, afortunadamente, las deficiencias de asp.nets se pueden eludir prefijando las hojas de estilo con 01, 02, ..., 99 y forzando el orden que desea (ver Rusty Swayne publicación de blog en la técnica para obtener más información).

Esto es especialmente importante si utiliza una hoja de estilo de reinicio, que recomiendo encarecidamente; hace que sea mucho más fácil diseñar un sitio de forma coherente en todos los navegadores (consulte Restablecer recargado de Eric Meyer ).

También se pierde la posibilidad de especificar un tipo de medio (por ejemplo, pantalla, impresión, proyección, braille, voz). Y si prefiere incluir hojas de estilo utilizando el método @import, también se quedará en el frío.

Otra opción que falta es Comentario condicional, que es especialmente útil si usa un & # 8220; ie-fix.css & # 8221; hoja de estilo.

Antes de explicar cómo el StylePlaceholder y Style control resuelven los problemas anteriores, crédito donde se debe, mi solución está inspirada en Por la publicación del blog de Zimmerman & # 8217 en el tema.

El control StylePlaceHolder se coloca en la sección de encabezado de su página o página maestra. Puede alojar uno o más controles de Estilo, y eliminará los estilos agregados por el motor de renderizado de manera predeterminada, y agregará los suyos (solo eliminará los estilos agregados del tema activo actual).

El control Style puede alojar estilos en línea entre las etiquetas de apertura y cierre & # 8217; y una referencia a un archivo de hoja de estilos externo a través de su propiedad CssUrl. Con otras propiedades, controla cómo la hoja de estilo representa en la página.

Déjame mostrarte un ejemplo. Considere un proyecto de sitio web simple con una página maestra y un tema con tres hojas de estilo & # 8211; 01reset.css, 02style.css, 99iefix.cs. Nota: Los he nombrado usando la técnica de prefijos descrita anteriormente, ya que mejora la experiencia de tiempo de diseño. Además, el prefijo de etiqueta de los controles personalizados es & # 8220; ass: & # 8221 ;.

En la sección de encabezado de la página maestra & # 8217; s, agregue:

<ass:StylePlaceHolder ID="StylePlaceHolder1" runat="server" SkinID="ThemeStyles" />

En su directorio de temas, agregue un archivo de máscara (por ejemplo, Styles.skin) y agregue el siguiente contenido:

<ass:StylePlaceHolder1runat="server" SkinId="ThemeStyles">
    <ass:Style CssUrl="~/App_Themes/Default/01reset.css" />
    <ass:Style CssUrl="~/App_Themes/Default/02style.css" />
    <ass:Style CssUrl="~/App_Themes/Default/99iefix.css" ConditionCommentExpression="[if IE]" />
</ass:StylePlaceHolder1>

Eso es básicamente eso. Hay más propiedades en el control de Estilo que se pueden usar para controlar la representación, pero esta es la configuración básica. Con eso en su lugar, puede agregar fácilmente otro tema y reemplazar todos los estilos, ya que solo necesita incluir un archivo de máscara diferente.

Ahora al código que hace que todo suceda. Debo admitir que la experiencia en el tiempo de diseño tiene algunas peculiaridades. Probablemente se deba al hecho de que no soy muy competente en la escritura de controles personalizados (de hecho, estos dos son mis primeros intentos), por lo que me gustaría recibir información sobre lo siguiente. En un proyecto actual basado en WCAB / WCSF que estoy desarrollando, veo errores como este en la vista de diseño de Visual Studios, unY no tengo idea de por qué. El sitio se compila y todo funciona en línea.

Ejemplo de error de tiempo de diseño en Visual Studio http: //www.egil .dk / wp-content / styleplaceholder-error.jpg

El siguiente es el código para el control StylePlaceHolder:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;

[assembly: TagPrefix("Assimilated.Extensions.Web.Controls", "ass")]
namespace Assimilated.WebControls.Stylesheet
{
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [DefaultProperty("SkinID")]
    [ToolboxData("<{0}:StylePlaceHolder runat=\"server\" SkinID=\"ThemeStyles\"></{0}:StylePlaceHolder>")]
    [ParseChildren(true, "Styles")]
    [Themeable(true)]
    [PersistChildren(false)]
    public class StylePlaceHolder : Control
    {
        private List<Style> _styles;

        [Browsable(true)]
        [Category("Behavior")]
        [DefaultValue("ThemeStyles")]
        public override string SkinID { get; set; }

        [Browsable(false)]
        public List<Style> Styles
        {
            get
            {
                if (_styles == null)
                    _styles = new List<Style>();
                return _styles;
            }
        }

        protected override void CreateChildControls()
        {
            if (_styles == null)
                return;

            // add child controls
            Styles.ForEach(Controls.Add);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            // get notified when page has finished its load stage
            Page.LoadComplete += Page_LoadComplete;
        }

        void Page_LoadComplete(object sender, EventArgs e)
        {
            // only remove if the page is actually using themes
            if (!string.IsNullOrEmpty(Page.StyleSheetTheme) || !string.IsNullOrEmpty(Page.Theme))
            {
                // Make sure only to remove style sheets from the added by
                // the runtime form the current theme.
                var themePath = string.Format("~/App_Themes/{0}",
                                              !string.IsNullOrEmpty(Page.StyleSheetTheme)
                                                  ? Page.StyleSheetTheme
                                                  : Page.Theme);

                // find all existing stylesheets in header
                var removeCandidate = Page.Header.Controls.OfType<HtmlLink>()
                    .Where(link => link.Href.StartsWith(themePath)).ToList();

                // remove the automatically added style sheets
                removeCandidate.ForEach(Page.Header.Controls.Remove);
            }
        }

        protected override void AddParsedSubObject(object obj)
        {
            // only add Style controls
            if (obj is Style)
                base.AddParsedSubObject(obj);
        }

    }
}

Y el código para el control de Estilo:

using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;

[assembly: TagPrefix("Assimilated.Extensions.Web.Controls", "ass")]
namespace Assimilated.WebControls.Stylesheet
{
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [DefaultProperty("CssUrl")]
    [ParseChildren(true, "InlineStyle")]
    [PersistChildren(false)]
    [ToolboxData("<{0}:Style runat=\"server\"></{0}:Style>")]
    [Themeable(true)]
    public class Style : Control
    {
        public Style()
        {
            // set default value... for some reason the DefaultValue attribute do
            // not set this as I would have expected.
            TargetMedia = "All";
        }

        #region Properties

        [Browsable(true)]
        [Category("Style sheet")]
        [DefaultValue("")]
        [Description("The url to the style sheet.")]
        [UrlProperty("*.css")]
        public string CssUrl
        {
            get; set;
        }

        [Browsable(true)]
        [Category("Style sheet")]
        [DefaultValue("All")]
        [Description("The target media(s) of the style sheet. See http://www.w3.org/TR/REC-CSS2/media.html for more information.")]
        public string TargetMedia
        {
            get; set;
        }

        [Browsable(true)]
        [Category("Style sheet")]
        [DefaultValue(EmbedType.Link)]
        [Description("Specify how to embed the style sheet on the page.")]
        public EmbedType Type
        {
            get; set;
        }

        [Browsable(false)]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public string InlineStyle
        {
            get; set;
        }

        [Browsable(true)]
        [Category("Conditional comment")]
        [DefaultValue("")]
        [Description("Specifies a conditional comment expression to wrap the style sheet in. See http://msdn.microsoft.com/en-us/library/ms537512.aspx")]
        public string ConditionalCommentExpression
        {
            get; set;
        }

        [Browsable(true)]
        [Category("Conditional comment")]
        [DefaultValue(CommentType.DownlevelHidden)]
        [Description("Whether to reveal the conditional comment expression to downlevel browsers. Default is to hide. See http://msdn.microsoft.com/en-us/library/ms537512.aspx")]
        public CommentType ConditionalCommentType
        {
            get; set;
        }

        [Browsable(true)]
        [Category("Behavior")]
        public override string SkinID { get; set; }

        #endregion

        protected override void Render(HtmlTextWriter writer)
        {            
            // add empty line to make output pretty
            writer.WriteLine();

            // prints out begin condition comment tag
            if (!string.IsNullOrEmpty(ConditionalCommentExpression))
                writer.WriteLine(ConditionalCommentType == CommentType.DownlevelRevealed ? "<!{0}>" : "<!--{0}>",
                                 ConditionalCommentExpression);

            if (!string.IsNullOrEmpty(CssUrl))
            {               
                // add shared attribute
                writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/css");

                // render either import or link tag
                if (Type == EmbedType.Link)
                {
                    // <link href=\"{0}\" type=\"text/css\" rel=\"stylesheet\" media=\"{1}\" />
                    writer.AddAttribute(HtmlTextWriterAttribute.Href, ResolveUrl(CssUrl));
                    writer.AddAttribute(HtmlTextWriterAttribute.Rel, "stylesheet");
                    writer.AddAttribute("media", TargetMedia);
                    writer.RenderBeginTag(HtmlTextWriterTag.Link);
                    writer.RenderEndTag();
                }
                else
                {
                    // <style type="text/css">@import "modern.css" screen;</style>
                    writer.RenderBeginTag(HtmlTextWriterTag.Style);
                    writer.Write("@import \"{0}\" {1};", ResolveUrl(CssUrl), TargetMedia);
                    writer.RenderEndTag();
                }
            }

            if(!string.IsNullOrEmpty(InlineStyle))
            {
                // <style type="text/css">... inline style ... </style>
                writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/css");
                writer.RenderBeginTag(HtmlTextWriterTag.Style);
                writer.Write(InlineStyle);
                writer.RenderEndTag();
            }

            // prints out end condition comment tag
            if (!string.IsNullOrEmpty(ConditionalCommentExpression))
            {
                // add empty line to make output pretty
                writer.WriteLine();
                writer.WriteLine(ConditionalCommentType == CommentType.DownlevelRevealed ? "<![endif]>" : "<![endif]-->");
            }
        }
    }

    public enum EmbedType
    {        
        Link = 0,
        Import = 1,
    }

    public enum CommentType
    {
        DownlevelHidden = 0,
        DownlevelRevealed = 1
    }
}

Entonces, ¿qué piensan ustedes? ¿Es esta una buena solución al problema del tema asp.net? ¿Y qué hay del código? Realmente me gustaría recibir algunos comentarios al respecto, especialmente en lo que respecta a la experiencia de tiempo de diseño.

Cargué una versión comprimida de la solución Visual Studio que contiene el proyecto, en caso de que alguien esté interesado.

Saludos, Egil.

¿Fue útil?

Solución

Encontré la respuesta a mi propia pregunta.

La razón de los errores de representación que estoy obteniendo en el modo de diseño es un error aparente en Visual Studio SP1, que Microsoft aún no ha solucionado .

Por lo tanto, el código anterior funciona como se esperaba, también en modo de diseño, siempre y cuando solo incluya los controles personalizados en un ensamblaje precompilado, y no a través de otro proyecto en la misma solución.

Consulte el enlace de arriba para obtener una explicación más detallada de cómo y por qué.

Otros consejos

Funciona muy bien.

Para aquellos como yo que nunca recuerdan la sintaxis de las etiquetas <%, esto es lo que debe agregar a la parte superior de la definición de la página maestra y al archivo de máscara para registrar el espacio de nombres.

<%@ Register TagPrefix="ass" Namespace="Assimilated.WebControls.Stylesheet" %>

No estoy seguro de querer tanto 'culo' en todo mi código, pero de lo contrario me gusta.

Ah, y si este es realmente tu primer gran trabajo de control personalizado. Sé que fue inspirado por el código de otra persona, pero al menos parece tener todos los atributos e interfaces correctos.

Re: usando un archivo CSS de medios específico, puedes usar la declaración CSS @media, funciona bien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top