Come assumere il controllo dei fogli di stile nei temi ASP.NET con i controlli StylePlaceHolder e Style

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

Domanda

Aggiornamento: questo si è trasformato in un post sul blog, con collegamenti e codice aggiornati, sul mio blog: https://egilhansen.com/2008 / 12/01 / how-to-take-control-of-fogli di stile-in-asp-net-temi-con-il-styleplaceholder-e-stile-controllo /


Il problema è piuttosto semplice. Quando usi Temi ASP.NET non hai molto da dire su come i tuoi fogli di stile sono renderizzati alla pagina.

Il motore di rendering aggiunge tutti i fogli di stile presenti nella cartella dei temi in ordine alfabetico, usando < link href = & # 8221; ... & # 8221; notazione.

Sappiamo tutti che l'ordine dei fogli di stile è importante, per fortuna le carenze di asp.nets possono essere aggirate prefissando i fogli di stile con 01, 02, ..., 99 e forzando così l'ordine desiderato (vedi Rusty Swayne post di blog sulla tecnica per ulteriori informazioni).

Questo è particolarmente importante se si utilizza un foglio di stile di ripristino, che consiglio vivamente; semplifica molto lo stile di un sito in modo coerente tra i browser (dai un'occhiata a Ripristina ricaricato da Eric Meyer ).

Ti manca anche la possibilità di specificare un tipo di supporto (ad es. schermo, stampa, proiezione, braille, discorso). E se preferisci includere fogli di stile usando il metodo @import, rimani fuori dal freddo.

Un'altra opzione mancante è Commento condizionale, che è particolarmente utile se si utilizza un & # 8220; ie-fix.css & # 8221; foglio di stile.

Prima di spiegare in che modo StylePlaceholder e Style control risolvono i problemi di cui sopra, credito a causa del credito, la mia soluzione è ispirata da Per Zimmerman & # 8217; s post sul blog su il soggetto.

Il controllo StylePlaceHolder si trova nella sezione dell'intestazione della pagina principale o della pagina. Può ospitare uno o più controlli di stile e rimuoverà gli stili aggiunti dal motore di rendering per impostazione predefinita e ne aggiungerà uno proprio (rimuoverà solo gli stili aggiunti dal tema attivo corrente).

Il controllo Style può ospitare sia stili incorporati tra i tag di apertura e chiusura # 8217; e un riferimento a un file di foglio di stile esterno tramite la sua proprietà CssUrl. Con altre proprietà puoi controllare il rendering del foglio di stile sulla pagina.

Lasciami mostrare un esempio. Prendi in considerazione un semplice progetto di sito Web con una pagina master e un tema con tre fogli di stile & # 8211; 01reset.css, 02style.css, 99iefix.cs. Nota: li ho nominati usando la tecnica di prefisso descritta in precedenza, in quanto consente una migliore esperienza in fase di progettazione. Inoltre, il prefisso tag dei controlli personalizzati è & # 8220; ass: & # 8221 ;.

Nella pagina principale & # 8217; s sezione dell'intestazione, aggiungere:

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

Nella directory del tema, aggiungi un file skin (ad esempio Styles.skin) e aggiungi il seguente contenuto:

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

Questo è fondamentalmente. Esistono altre proprietà sul controllo Stile che possono essere utilizzate per controllare il rendering, ma questa è l'impostazione di base. Con quello sul posto, puoi facilmente aggiungere un altro tema e sostituire tutti gli stili, poiché devi solo includere un file skin diverso.

Ora al codice che rende tutto ciò possibile. Devo ammettere che l'esperienza in fase di progettazione ha alcune stranezze. Probabilmente è dovuto al fatto che non sono molto abile nello scrivere controlli personalizzati (in effetti, questi due sono i miei primi tentativi), quindi mi piacerebbe molto inserire quanto segue. In un attuale progetto basato su WCAB / WCSF che sto sviluppando, vedo errori come questo nella vista di progettazione di Visual Studios, ae non ho idea del perché. Il sito viene compilato e tutto funziona online.

Esempio di errore in fase di progettazione in Visual Studio http: //www.egil .dk / wp-content / styleplaceholder-error.jpg

Di seguito è riportato il codice per il controllo 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);
        }

    }
}

E il codice per il controllo 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
    }
}

Allora, cosa ne pensate ragazzi? È una buona soluzione al problema del tema asp.net? E il codice? Mi piacerebbe davvero un po 'di input, soprattutto per quanto riguarda l'esperienza del design time.

Ho caricato una versione zippata della soluzione Visual Studio che contiene il progetto, nel caso qualcuno fosse interessato.

Cordiali saluti, Egil.

È stato utile?

Soluzione

Ho trovato la risposta alla mia domanda.

Il motivo degli errori di rendering che sto ottenendo in modalità progettazione è un bug apparente in Visual Studio SP1, che Microsoft non ha ancora risolto .

Quindi il codice sopra funziona come previsto, anche in modalità progettazione, purché includa solo i controlli personalizzati in un assembly precompilato e non attraverso un altro progetto nella stessa soluzione.

Vedi il link sopra per una spiegazione più dettagliata di come e perché.

Altri suggerimenti

Funziona in modo molto fluido.

Per quelli come me che non ricordano mai la sintassi di <% tag ecco cosa devi aggiungere all'inizio della definizione della pagina principale e il file skin per registrare lo spazio dei nomi.

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

Non sono sicuro di volere così tanto "culo" in tutto il mio codice, ma per il resto mi piace.

Oh, e se questo è davvero il tuo primo controllo personalizzato, ottimo lavoro. So che è stato ispirato dal codice di qualcun altro ma almeno sembra avere tutti gli attributi e le interfacce giuste.

Ri: usando un file CSS multimediale specifico, puoi usare l'istruzione CSS @media, funziona bene.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top