Comment prendre le contrôle des feuilles de style dans les thèmes ASP.NET avec le contrôle StylePlaceHolder et Style

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

Question

Mise à jour: cet article est devenu un article de blog, avec des liens et du code mis à jour, sur mon blog: https://egilhansen.com/2008 / 12/01 / comment-prendre-le-contrôle-de-feuilles-de-style-en-asp-net-themes-avec-le-styleplaceholder-and-style-control /

Le problème est assez simple. Lorsque vous utilisez des thèmes ASP.NET, vous n'avez pas beaucoup à dire sur le rendu de vos feuilles de style sur la page.

Le moteur de rendu ajoute toutes les feuilles de style que vous avez dans votre dossier de thèmes dans l'ordre alphabétique, à l'aide de < link href = & # 8221; ... & # 8221; notation.

Nous savons tous que l'ordre des feuilles de style est important. Heureusement, vous pouvez éviter les problèmes d'asp.nets en préfixant les feuilles de style par 01, 02, ..., 99 et en forçant ainsi l'ordre souhaité (voir Rusty Swayne publication de blog sur la technique pour obtenir plus d'informations).

Ceci est particulièrement important si vous utilisez une feuille de style de réinitialisation, ce que je recommande vivement; il est beaucoup plus facile de styler un site de manière cohérente sur tous les navigateurs (consultez la page Réinitialiser Rechargé depuis Eric Meyer ).

Vous manquez également la possibilité de spécifier un type de support (écran, impression, projection, braille, parole, par exemple). Et si vous préférez inclure des feuilles de style à l’aide de la méthode @import, vous êtes également laissé de côté.

Une autre option manquante est le commentaire conditionnel, particulièrement utile si vous utilisez un & # 8220; ie-fix.css & # 8221; feuille de style.

Avant d’expliquer comment StylePlaceholder et Style control résolvent les problèmes ci-dessus, crédit lorsque le crédit est dû, ma solution est inspirée de Per Zimmerman & # 8217; billet de blog sur le sujet.

Le contrôle StylePlaceHolder est placé dans la section d'en-tête de votre page maître ou de votre page. Il peut héberger un ou plusieurs contrôles de style. Il supprimera les styles ajoutés par défaut par le moteur de rendu et ajoutera le sien (il ne supprimera que les styles ajoutés au thème actif en cours).

Le contrôle Style peut à la fois héberger des styles en-ligne entre ses balises d'ouverture et de fermeture & # 8217; et une référence à un fichier de feuille de style externe par le biais de sa propriété CssUrl. Avec d'autres propriétés, vous contrôlez le rendu de la feuille de style dans la page.

Laissez-moi vous montrer un exemple. Considérons un projet de site Web simple avec une page maître et un thème avec trois feuilles de style & # 8211; 01reset.css, 02style.css, 99iefix.cs. Remarque: je les ai nommés à l'aide de la technique de préfixage décrite précédemment, car elle permet une meilleure expérience de conception. En outre, le préfixe de balise des contrôles personnalisés est & # 8220; ass: & # 8221;.

Dans la section d'en-tête de la page maître & # 8217; ajoutez:

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

Dans votre répertoire de thèmes, ajoutez un fichier de skin (par exemple, Styles.skin) et ajoutez le contenu suivant:

<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>

C'est fondamentalement ça. Il existe d'autres propriétés sur le contrôle Style pouvant être utilisées pour contrôler le rendu, mais il s'agit de la configuration de base. Cela fait, vous pouvez facilement ajouter un autre thème et remplacer tous les styles, car il vous suffit d’inclure un fichier de skin différent.

Passons maintenant au code qui rend tout cela possible. Je dois admettre que l'expérience de conception a quelques bizarreries. C'est probablement dû au fait que je ne maîtrise pas très bien l'écriture de commandes personnalisées (en fait, ces deux tentatives sont mes premières tentatives), aussi j'aimerais beaucoup avoir votre mot à dire sur les points suivants. Dans un projet actuel basé sur WCAB / WCSF que je développe, je vois des erreurs comme celle-ci dans Visual Studio Design, unnd je ne sais pas pourquoi. Le site est compilé et tout fonctionne en ligne.

Exemple d'erreur de temps de conception dans Visual Studio http: //www.egil .dk / wp-content / styleplaceholder-error.jpg

Voici le code du contrôle 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);
        }

    }
}

Et le code pour le contrôle de style:

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
    }
}

Alors, vous en pensez quoi? Est-ce une bonne solution au problème de thème asp.net? Et qu'en est-il du code? J'aimerais vraiment avoir quelques commentaires à ce sujet, en particulier en ce qui concerne l'expérience de la conception.

J'ai téléchargé une version compressée de la solution Visual Studio qui contient le projet, au cas où quelqu'un serait intéressé.

Cordialement, Egil.

Était-ce utile?

La solution

J'ai trouvé la réponse à ma propre question.

La raison des erreurs de rendu générées en mode conception est un bogue apparent dans Visual Studio SP1, que Microsoft n'a pas encore corrigé .

Le code ci-dessus fonctionne donc comme prévu, également en mode conception, à condition d'inclure les contrôles personnalisés dans un assemblage préalablement compilé, et non via un autre projet de la même solution.

Voir le lien ci-dessus pour une explication plus détaillée de comment et pourquoi.

Autres conseils

fonctionne très bien.

Pour ceux qui, comme moi, ne se souviennent jamais de la syntaxe de <% tags, voici ce que vous devez ajouter en haut de la définition de la page maître et du fichier d'habillage pour enregistrer l'espace de nom.

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

Je ne suis pas sûr de vouloir autant de «fesses» dans tout mon code, mais sinon je l'aime bien.

Oh, et s’il s’agit vraiment de votre premier excellent travail de contrôle personnalisé. Je sais que le code de quelqu'un d'autre l'a inspiré, mais au moins, il semble que tous les attributs et interfaces appropriés ont été définis.

Objet: utiliser un fichier CSS multimédia spécifique, vous pouvez utiliser l'instruction CSS @media, fonctionne très bien.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top