2008-11-30 9 views
10

Update: Dies in eine Blog-Post gedreht, mit aktualisierten Links und Code, über auf meinem Blog: https://egilhansen.com/2008/12/01/how-to-take-control-of-style-sheets-in-asp-net-themes-with-the-styleplaceholder-and-style-control/Wie mit dem Styleplaceholder und Style-Kontrolle in ASP.NET Themen Kontrolle von Stylesheets nehmen


Das Problem ist ziemlich einfach. Bei der Verwendung von ASP.NET-Designs haben Sie nicht viel zu sagen, wie Ihre Stylesheets auf der Seite gerendert werden.

Die Render-Engine fügt alle Stylesheets, die Sie in Ihrem themes-Ordner haben, in alphabetischer Reihenfolge hinzu, indem Sie die < link href = "..." -Notation verwenden.

Wir alle wissen, dass die Reihenfolge der Stylesheets wichtig ist, glücklicherweise können asp.nets - Unzulänglichkeiten umgangen werden, indem man die Stylesheets mit 01, 02, ..., 99 voranstellt und so die gewünschte Reihenfolge erzwingt (siehe Rusty Swayne blog post auf die Technik für weitere Informationen).

Dies ist besonders wichtig, wenn Sie ein Reset-Stylesheet verwenden, das ich sehr empfehlen kann; es macht es viel einfacher, eine Website in konsistenter Form über Browser hinweg zu gestalten (siehe Reset Reloaded from Eric Meyer).

Ihnen fehlt auch die Möglichkeit, einen Medientyp anzugeben (z. B. Bildschirm, Druck, Projektion, Blindenschrift, Sprache). Und wenn Sie Stylesheets mit der @ import-Methode bevorzugen, bleiben Sie auch draußen.

Eine weitere fehlende Option ist Bedingter Kommentar, was besonders nützlich ist, wenn Sie ein Stylesheet "ie-fix.css" verwenden.

Bevor ich erkläre, wie die StylePlaceholder und Style-Kontrolle die oben genannten Probleme lösen, Kredit, wo Kredit fällig ist, ist meine Lösung von Per Zimmerman’s blog post zum Thema inspiriert.

Das StylePlaceHolder-Steuerelement wird im Kopfbereich Ihrer Masterseite oder Seite platziert. Es kann ein oder mehrere Style-Steuerelemente enthalten und entfernt Stile, die von der Render-Engine standardmäßig hinzugefügt wurden, und fügt eigene hinzu (es werden nur Stile aus dem aktuellen aktiven Design entfernt).

Das Style-Steuerelement kann sowohl Inline-Stile zwischen den öffnenden und schließenden Tags als auch einen Verweis auf eine externe Stylesheet-Datei über die CssUrl-Eigenschaft hosten. Mit anderen Eigenschaften steuern Sie, wie das Stylesheet auf der Seite dargestellt wird.

Lassen Sie mich ein Beispiel zeigen. Betrachten Sie ein einfaches Website-Projekt mit einer Masterseite und einem Theme mit drei Stylesheets - 01reset.css, 02style.css, 99iefix.cs. Hinweis: Ich habe sie mit der oben beschriebenen Präfix-Technik benannt, da sie für eine bessere Design-Zeiterfahrung sorgt. Außerdem lautet das Tag-Präfix der benutzerdefinierten Steuerelemente "ass:".

im Kopfbereich der Master-Seite hinzufügen:

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

In Ihrem Themenverzeichnis, fügen Sie eine Skin-Datei (zB Styles.skin) und den folgenden Inhalt hinzu:

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

Das heißt im Grunde genommen. Es gibt eine Reihe von Eigenschaften im Style-Steuerelement, die zur Steuerung des Renderings verwendet werden können. Dies ist jedoch die grundlegende Konfiguration. Damit können Sie problemlos ein anderes Design hinzufügen und alle Stile ersetzen, da Sie nur eine andere Skin-Datei hinzufügen müssen.

Nun zu dem Code, der alles möglich macht. Ich muss zugeben, dass die Designerfahrung einige Macken hat.Es ist wahrscheinlich aufgrund der Tatsache, dass ich nicht sehr geschickt benutzerdefinierte Steuerelemente zu schreiben (in der Tat, diese beiden sind meine ersten Versuche), so würde ich sehr gerne Eingang in die folgenden. In einem aktuellen WCAB/WCSF-basierten Projekt, das ich entwickle, sehe ich Fehler wie diese in der Designansicht von Visual Studios, und ich habe keine Ahnung warum. Die Seite kompiliert und alles funktioniert online.

Example of design time error in Visual Studio http://www.egil.dk/wp-content/styleplaceholder-error.jpg

Im Folgenden ist der Code für die Steuerung 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); 
     } 

    } 
} 

Und der Code für die Style-Steuerung:

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

Also, was denkt ihr? Ist dies eine gute Lösung für das Asp.net-Problem? Und was ist mit dem Code? Ich hätte gerne einen Input dazu, vor allem in Bezug auf die Designzeit.

Ich habe eine zipped version of the Visual Studio solution hochgeladen, die das Projekt enthält, falls jemand interessiert ist.

Mit freundlichen Grüßen, Egil.

Antwort

2

Die Antwort auf meine eigene Frage gefunden.

Der Grund für die Renderfehler, die ich im Entwurfsmodus erhalte, ist ein offensichtlicher Fehler in Visual Studio SP1, which Microsoft has yet to fix.

So funktioniert der obige Code wie erwartet, auch im Entwurfsmodus, solange Sie nur die benutzerdefinierten Steuerelemente in einer vorkompilierten Assembly und nicht durch ein anderes Projekt in derselben Lösung enthalten.

Siehe den obigen Link für eine detailliertere Erklärung, wie und warum.

0

Funktioniert sehr reibungslos.

Für diejenigen wie mich, die nie erinnern Syntax von <% Tags hier ist, was Sie an der Spitze der Masterseitendefinition und der Skin-Datei hinzufügen müssen, um den Namespace zu registrieren.

Ich bin mir nicht sicher, dass ich so viel 'Arsch' über meinen Code, aber sonst mag ich es.

Oh, und wenn das wirklich Ihre erste benutzerdefinierte Steuerung ist, super Job. Ich weiß, es wurde von jemand anderem Code inspiriert, aber es mindestens erscheint, um alle richtigen Attribute und Schnittstellen zu haben.

+0

Danke für die freundlichen Worte Simon. Dies ist in der Tat meine erste benutzerdefinierte Kontrolle, es wurde jedoch einige Male umgestaltet, so erste benutzerdefinierte Steuerung, nicht der erste Versuch :) –

+0

@egil lustige Sache ist ich am nächsten Tag zu MVC gewechselt - teilweise weil ich müde wurde Sie müssen nach cleveren Lösungen wie Ihren nach einfachen Problemen suchen. glücklicherweise entwickle ich eine neue Seite, also durfte ich den Luxus mit MVC spielen und genieße es wirklich –

0

Re: mit spezifischen Medien CSS-Datei, können Sie die @ media CSS-Anweisung, funktioniert gut.