Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 82159
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 10, 20262026-05-10T21:34:24+00:00 2026-05-10T21:34:24+00:00

Update: This turned into a blog post, with updated links and code, over at

  • 0

Update: This turned into a blog post, with updated links and code, over at my blog: https://egilhansen.com/2008/12/01/how-to-take-control-of-style-sheets-in-asp-net-themes-with-the-styleplaceholder-and-style-control/


The problem is pretty simple. When using ASP.NET Themes you do not have much say in how your style sheets are rendered to the page.

The render engine adds all the style sheets you have in your themes folder in alphabetic order, using the <link href=”…” notation.

We all know the order of the style sheets are important, luckily asp.nets shortcomings can be circumvented by prefixing the style sheets with 01, 02, … , 99, and thus forcing the order you want (see Rusty Swayne blog post on the technique for more information).

This is especially important if you use a reset style sheet, which I highly recommend; it makes it much easier to style a site in a consistent form across browsers (take a look at Reset Reloaded from Eric Meyer).

You also miss out of the possibility to specify a media type (e.g. screen, print, projection, braille, speech). And if you prefer to include style sheets using the @import method, you are also left out in the cold.

Another missing option is Conditional Comment, which is especially useful if you use an “ie-fix.css” style sheet.

Before I explain how the StylePlaceholder and Style control resolve the above issues, credit where credit is due, my solution is inspired by Per Zimmerman’s blog post on the subject.

The StylePlaceHolder control is placed in the header section of your master page or page. It can host one or more Style controls, and will remove styles added by the render engine by default, and add its own (it will only remove styles added from the current active theme).

The Style control can both host inline styles in-between it’s opening and closing tags and a reference to a external style sheet file through its CssUrl property. With other properties you control how the style sheet it renders to the page.

Let me show an example. Consider a simple web site project with a master page and a theme with three style sheets – 01reset.css, 02style.css, 99iefix.cs. Note: I have named them using prefixing technique described earlier, as it makes for a better design time experience. Also, the tag prefix of the custom controls is “ass:”.

In the master page’s header section, add:

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

In your theme directory, add a skin file (e.g. Styles.skin) and add the following content:

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

That is basically it. There are a more properties on the Style control that can be used to control the rendering, but this is the basic setup. With that in place, you can easily add another theme and replace all the styles, since you only need to include a different skin file.

Now to the code that makes it all happen. I must admit that the design time experience have some quirks. It is probably due to the fact that I am not very proficient in writing custom controls (in fact, these two are my first attempts), so I would very much like input on the following. In a current WCAB/WCSF based project I am developing, I am seeing errors like this in Visual Studios design view, and I have no idea why. The site compiles and everything works online.

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

The following is the code for the StylePlaceHolder control:

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

And the code for the Style control:

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

So what do you guys think? Is this a good solution to the asp.net theme problem? And what about the code? I would really like some input on it, especially in regards to the design time experience.

I uploaded a zipped version of the Visual Studio solution that contains the project, in case anyone is interested.

Best regards, Egil.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. 2026-05-10T21:34:24+00:00Added an answer on May 10, 2026 at 9:34 pm

    Found the answer to my own question.

    The reason for the rendering errors I am getting in design mode, is an apparent bug in Visual Studio SP1, which Microsoft has yet to fix.

    So the above code works as expected, also in design mode, as long as you just include the custom controls in a pre compiled assembly, and not through another project in the same solution.

    See the link above for a more detailed explanation of how and why.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Ask A Question

Stats

  • Questions 118k
  • Answers 118k
  • Best Answers 0
  • User 1
  • Popular
  • Answers
  • Editorial Team

    How to approach applying for a job at a company ...

    • 7 Answers
  • Editorial Team

    How to handle personal stress caused by utterly incompetent and ...

    • 5 Answers
  • Editorial Team

    What is a programmer’s life like?

    • 5 Answers
  • Editorial Team
    Editorial Team added an answer You should call document.getElementById after the document is loaded. Try… May 11, 2026 at 11:37 pm
  • Editorial Team
    Editorial Team added an answer As others have said, don't restrict the set of characters… May 11, 2026 at 11:37 pm
  • Editorial Team
    Editorial Team added an answer Yes, it's called a set. Most languages implement them in… May 11, 2026 at 11:37 pm

Related Questions

How does the comma operator work in C++? For instance, if I do: a
My app has a DataGridView object and a List of type MousePos. MousePos is
I work at a small LAMP Development Studio where the idea has been to
I have a web project that I developed where one of the main functions

Trending Tags

analytics british company computer developers django employee employer english facebook french google interview javascript language life php programmer programs salary

Top Members

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.