ASP.NET Head Tag Content Include and page scoped CSS Class - Style tags

4 minute read,

One of the most important things I strive for is maintainable code.  While working on RentToolbox, I’ve noticed I have many instances of specific CSS styling information in global CSS files that are statically linked in a master page.  As a small example, the code I’m referring to looks similar to the following:

90 <html>
91     <head>
92        <link href="style.css" type="text/css" />
93     </head>
94     <body>
95       <div class="SomeCustomCssClass" />
96     </body>
97 </html>

SomeCustomCssClass” is a specific CSS class only applied to certain pages on the website.  Why have all these special case CSS classes in style.css if they’re only used on a limited number of pages?

Solution 1 - Multiple CSS Files

The obvious solution to this is to remove the specific CSS classes from style.css and create individual CSS files that contain the specific CSS classes as illustrated below:

90 <html>
91     <head>
92        <link href="style.css" type="text/css" />
93        <link href="custom.css" type="text/css" />
94     </head>
95     <body>
96       <div class="SomeGlobalCssClass" />
97       <div class="SomeCustomCssClass" />
98     </body>
99 </html>

This would work; however, the disadvantage is that the number of CSS files has increased.  So what, what’s the big deal?  Well, suppose you have a large website with a few thousand pages you’ll soon be running into the same problem with bloated CSS files again.  Some CSS classes would be used, and some will not.  The number of custom.css files will only continue to increase.

Can we do better?

Solution 2 - Inline CSS styling

We could use inline css styling via the style tag and do the following:

90 <html>
91     <head>
92        <link href="style.css" type="text/css" />
93     </head>
94     <body>
95       <div class="SomeGlobalCssClass" />
96       <div style="color: Red; and whole bunch of styling" />
97     </body>
98 </html>

Unfortunately, if our “SomeCustomCssClass” did a lot of heavy lifting, then our html/ASPX file wouldn’t be very readable and be littered with long and ugly style tags.

Can we do better?

Solution 3 - Explicit CSS classes

We could use place explicit CSS styling in the head tag as shown below:

 90 <html>
 91     <head>
 92        <link href="style.css" type="text/css" />
 93        <style type="text/css">
 94 
 95           .SomeCustomCssClass{
 96                color: red; and a lot of styles.
 97           }
 98 
 99        </style>
100     </head>
101     <body>
102       <div class="SomeGlobalCssClass" />
103       <div style="SomeCustomCssClass" />
104     </body>
105 </html>

This works great, but you hit a really big wall when you try and use this approach with MasterPages and ContentPlaceHolders. The problem using this approach with master pages stems from the fact that you cant access the <head> tag inside a content place holder without  writing C# code.  What if we defined the style tag in the content place holder like this:

 90 <%@ MasterPage %>
 91 <html>
 92     <head>
 93        <link href="style.css" type="text/css" />
 94     </head>
 95     <body>
 96       <div class="SomeGlobalCssClass" />
 97 
 98         <asp:ContentPlaceHolder>
 99             <asp:Content>
100                <style type="text/css">
101 
102                   .SomeCustomCssClass{
103                       color: red; and a lot of styles.
104                   }
105 
106                </style>
107                <div style="SomeCustomCssClass" />
108             </asp:Content>
109         </asp:ContentPlaceHolder>
110     </body>
111 </html>

This would be great if it were allowed.  Unfortunately, standards say, we cannot have <style> tags defined in <body> tags.  <style> must be defined within in the content of a <head> tag only.   Ugh, so, what can we do?

Solution 4 - Use HeadContentInclude control

I wrote a small control that transfers the inner contents of the control into the head tag allowing the best of both worlds.  The ASP.NET markup turns into:

 90 <%@ MasterPage %>
 91 <html>
 92     <head>
 93        <link href="style.css" type="text/css" />
 94     </head>
 95     <body>
 96       <div class="SomeGlobalCssClass" />
 97 
 98         <asp:ContentPlaceHolder>
 99             <asp:Content>
100                <ctrl:HeadContentInclude ID="headContent1">
101                    <style type="text/css">
102 
103                        .SomeCustomCssClass{
104                           color: red; and a lot of styles.
105                        }
106 
107                    </style>
108                </ctrl:HeadContentInclude>
109 
110                <div style="SomeCustomCssClass" />
111 
112             </asp:Content>
113         </asp:ContentPlaceHolder>
114     </body>
115 </html>

Now, any code defined in the HeadContentInclude control, will be rendered in the <head> tag while still allowing us to define custom CSS classes inline with the content. :sunglasses:  Coolness.  You could potentially use this to customize the and keywords per page too.  The code for the HeadContentInclude is rather very simple:

27     [ToolboxData( "<{0}:HeadContentInclude runat=server></{0}:HeadContentInclude>" )]
28     public class HeadContentInclude : Control
29     {
30         protected override void OnInit( EventArgs e )
31         {
32             base.OnInit( e );
33             this.Page.PreRender += new EventHandler( Page_PreRender );
34         }
35 
36         void Page_PreRender( object sender, EventArgs e )
37         {
38             //get the current page before removing it
39             Page currentPage = this.Page;
40 
41             //the remove method below will cause this.Page = null
42             this.Parent.Controls.Remove( this );
43 
44             //append control into the header
45             currentPage.Header.Controls.Add( this );
46         }
47 
48         public override bool EnableViewState
49         {
50             get{ return false; }
51             set{ }
52         }
53         public override bool Visible
54         {
55             get{ return true; }
56             set{ }
57         }
58     }

Hope that helped.  Happy maintainable coding! :sunglasses:

Updated:

Leave a comment

Your email address will not be published. Required fields are marked *

Loading...