ASP.NET and Styles & CSS Embedding
One thing that really bugs me about ASP.NET is the way it deals with header management especially when working with Themes. One of the big problems I see is that ASP.NET embeds the Themes style sheet at the bottom of the header list. If you happen to be using Master Pages which can have a header content section or your are adding additional CSS or style tags to your header the Themes style sheet will always end up on the bottom of the header list:
<%@Page Language="C#" MasterPageFile="https://weblog.west-wind.com/TimeTrakkerMaster.Master" AutoEventWireup="false" CodeBehind="OpenEntries.aspx.cs" Inherits="TimeTrakkerWeb.OpenEntries" Title="Open Entries - Time Trakker" %> <asp:Content ID="Head" ContentPlaceHolderID="head" runat="server"> <style type="text/css"> #itemtemplate { border-bottom: dashed 1px teal; padding: 9px; padding-left: 30px; } #itemtemplate:hover { background: url(images/lightorangegradient.png); cursor: pointer; } #itemtemplate b { color: Navy; } #itemtemplate a { text-decoration: none; } #itemtemplate a:visited { color: Navy; } #clockimg { width: 16px; height: 16px; margin-right: 15px; margin-bottom: 5px; background-image: url(images/punchout.gif); float: left; } </style> </asp:Content> <asp:Content ID="Content" ContentPlaceHolderID="Content" runat="server"> ... </asp:Content>
And I render that I get:
<html xmlns="http://www.w3.org/1999/xhtml" > <head><title> Open Entries - Time Trakker </title> <style type="text/css"> #pagecontainer { text-align:left; width: 850px; border: solid 1px silver; border-right: solid 2px silver;border-bottom: solid 2px silver; background: white; position:relative; } </style> <style type="text/css"> #itemtemplate { border-bottom: dashed 1px teal; padding: 9px; padding-left: 30px; } #itemtemplate:hover { background: url(images/lightorangegradient.png); cursor: pointer; } #itemtemplate b { color: Navy; } #itemtemplate a { text-decoration: none; } #itemtemplate a:visited { color: Navy; } #clockimg { width: 16px; height: 16px; margin-right: 15px; margin-bottom: 5px; background-image: url(images/punchout.gif); float: left; } </style> <link href="App_Themes/Standard/Standard.css" type="text/css" rel="stylesheet" />
</head>
Notice that the style sheet I defined in the master content page as well as in the master page (#pagecontent) end up above the Standard.css themes file.
The same is true if you attempt to add any controls to the Page.Header controls collection - whatever you do there ends up BEFORE the themes style sheet as far as I can tell or if you add styles manually to the header section.
This means you effectively can't override the styles defined in the themes file which is uhm problematic in many situations. This bit me today once again as I was overriding one of the styles in the themes css and found that I couldn't override the class.
There's a way to work around this but it's not XHTML compliant - you can embed styles and style sheet references into the page's content which at least guarantees that the styles are rendered after the theme's CSS.
Loading a CSS stylesheet from a Control
Another related issue I ran into today as I was building a small wrapper control around the jQuery Calendar control is how do you effectively load a CSS style sheet from a custom control, without actually rendering the style sheet multiple times?
The problem here is that unlike Script references which have a clear API in the Page.ClientScript/ScriptManager which help to make sure you don't load multiple references to the same script files, there's no corresponding API for CSS files. However, you can fake out ASP.NET by using the ClientScript/ScriptManager for this anyway:
string css = @"<link href=""" + this.ResolveUrl("scripts/jquery-calendar.css") + @""" type=""text/css"" rel=""stylesheet"" />";
ScriptManager.RegisterClientScriptBlock(this.Page, typeof(ControlResources), "_calcss", css, false);
You can also use Page.ClientScript.RegisterClientScriptBlock. Basically you can use this API to inject anything into the top of the page, just after the FORM tag as long as you pass the last parameter as false, which indicates that you're providing your own <script> tags - or that you are basically handling the full markup. The flag is there for compatibility with .NET 1.1 which requires that you add the script tags, but you can use this now for injecting code into the page. This isn't ideal for CSS in all cases though: if your CSS needs to modify the body tag then this might not work correctly and as mentioned this is not XHTML compliant.
Other Posts you might also like
- Map Physical Paths with an HttpContext.MapPath() Extension Method in ASP.NET
- Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
- Getting the Client IP Address in ASP.NET Core
- Resolving Paths To Server Relative Paths in .NET Code
- Getting the ASP.NET Core Server Hosting Urls at Startup and in Requests
The Voices of Reason
# re: ASP.NET and Styles & CSS Embedding
I dynamically inject CSS using the this.page.header.controls.add(); on the master page to do some browser type hacking. The control you add is an HtmlLink with the attributes of a CSS file.
I wonder if you could change that to .AddAt(); and put that on the page load of your child pages to correct your CSS?
-A
# re: ASP.NET and Styles & CSS Embedding
@Rainer - YES! Looks like StyleSheetThemet does the right thing with the style sheet's getting dumped right at the top of the header tag! Never occurred to me that this would be the case. Awesome.
Expirementing around with both Theme and StyleSheetScheme I can see that if you specify both though you get the CSS injected twice which is kinda lame. You'd figure that one would override the other.
Doesn't matter I suppose since I only ever use styles in themes anyway... Thanks!
# re: ASP.NET and Styles & CSS Embedding
I ended up creating a custom templated control which i could assign a skinid to and then added all my common css declarations that needed to be themable in a resource.skin file the each theme of my app.
eg
in page head of master page:
<TPControl:ThemeCss runat="server" ID="themecss" SkinID="Theme2Css"> <CssLiteralTemplate> </CssLiteralTemplate> </TPControl:ThemeCss>
Then in a controls.skin file in each of the themes in your app:
<TPControl:ThemeCss runat="server" SkinID="Theme2Css"> <CssLiteralTemplate> <style type="text/css"> @import url("~/Assets/Style/reset-fonts-grids.css"); @import url("~/App_Styles/tp/tp.css"); @import url("~/assets/style/commonform.css"); @import url("~/assets/style/commonStyle.css"); </style> <!--[if IE ]> <style type="text/css"> @import "~/App_Styles/tp/tpIEAll.css"; </style> <![endif]--> <!--[if lte IE 6]> <style type="text/css"> @import "~/App_Styles/tp/tpIE.css"; </style> <![endif]--> </CssLiteralTemplate> </TPControl:ThemeCss>
This way you can use @import to bring in your stylesheets, use conditional comments to target css overrides, er *ahem* bug fixes for various versions of IE and position the whole thing where you want it in the head. I added a lame but serviceable implementation to resolve any references of the '~' and then stored any assets referred to within the control to a App_Styles directory.
Ben
# re: ASP.NET and Styles & CSS Embedding
Just use Request.Browser to get a browser name and version, and generate the link off of that. (ex: IE6.css, IE7.css, and Firefox2.css). Place all the global css in one file, and tweak the css with overrides.
Combine that with the web developer toolbars for Firefox and IE 6/7, and you have a workable solution.
@Rick - Did you know the validation code at the bottom always fails on first attempt?
# re: ASP.NET and Styles & CSS Embedding
Can any of you confirm that this is occasional or on every comment?
I'm holding off for Win Server 2008 until upgrading the box (which is resource starved) and in the meantime the server is struggling a bit.
# re: ASP.NET and Styles & CSS Embedding, QUESTION???
how can i use StyleSheet Dynamically ??? means i parse some parameter to stylesheet
and creat it with dynamic parameter?
i need it , tanx.
# re: ASP.NET and Styles & CSS Embedding
HtmlLink css = new HtmlLink(); css.Href = this.ResolveUrl(this.StyleSheet); css.Attributes.Add("rel", "stylesheet"); css.Attributes.Add("type", "text/css"); this.Header.Controls.Add(css);
# re: ASP.NET and Styles & CSS Embedding
body #clockimg {border:solid 1px red}
#clockimg {border:solid 1px blue}
#clockimg will have the red border since it's more specified, the order doesn't matter
# re: ASP.NET and Styles & CSS Embedding
# re: ASP.NET and Styles & CSS Embedding
This seems to be a good occasion to tell you a couple of things concerning your weblog:
1. The minimum requirements for an (X)HTML page to be valid are:
- doctype
- html element
- head element
- body element
The head element must contain
- the character set meta element
- title element
You don't have the character set meta element.
2. You should/could get rid of the keywords meta tag. It got so abused by smart alecks striving to attain higher visibility that every self-respecting search engine on the planet simply ignores it.
Fine blog, however.
P.S.
I confirm the previous reader's claim your validation fails on the first attempt.
# Thanx for replay...
for example i have a stylesheet.css that it like this : ".head{height: #height;}"
that i wanna user can set "#height" at designTime , by Properties.
sorry for my bad English! :(
pleas say about it, really tanx
# re: ASP.NET and Styles & CSS Embedding
Gotta look into the validation issue. Oddly I just can't confirm this myself. I usually post as an admin and I bypass the validation, but even if I log out and comment here I don't see the failed validation. There is a timeout on the Captcha which is 5 minutes or so - could that be it?
# re: ASP.NET and Styles & CSS Embedding
> I'm not too concerned about 100% XHTML compliance
As soon as I had submitted my post, it occurred to me this might be your reaction. I forgot to tell you the most important stuff.
> some things are just too much and add little or no value
The character set is EXTREMELY important. If a visitor to your website has a localized version of the operating system and/or the browser, he may not see what you see on your machine. Trust me, I'm a doctor. Many French websites, for some reason, notoriously omit the character set. When I visit them they are unreadable on my machine.
> There is a timeout on the Captcha which is 5 minutes or so - could that be it?
I think not. This is why:
1. I typed my post.
2. Entered the code.
3. Clicked Post Comment.
- Didn't validate. Timed out? Possibly.
4. Re-entered the code.
- Didn't validate because I didn't fill in Your Name.
5. Filled in Your Name.
6. Re-entered the code.
7. Clicked Post Comment.
- Didn't validate. Timed out? Impossible.
8. Re-entered the code.
9. Clicked Post Comment.
- Went through.
# re: ASP.NET and Styles & CSS Embedding
Thanks for taking the time to check the captcha code. I see some of this if I wait long enough. I think the problem here though is that the server has been having problems - I suspect the AppDomain is bouncing up and down and everytime that happens the cache where the Captcha is stored goes with it. I'll put the AppDomain Id on the page on the bottom to see if that's indeed the problem.
Thx.
# re: ASP.NET and Styles & CSS Embedding
Your blog rocks! Keep up great work!
# re: ASP.NET and Styles & CSS Embedding
# re: ASP.NET and Styles & CSS Embedding
public class SkinedLink : WebControl { private string strHref; public string Href { get { return strHref; } set { strHref = value; } } protected override void Render(HtmlTextWriter output) { output.Write("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + this.ResolveUrl(strHref) + "\" />"); } }
This allows you to place your link tags at any point in your head element. You just set the Href property in your skin file. You can use multiple controls with different SkinID attributes and allows you to bring the cascading back to css in your asp.net themes.
# re: ASP.NET and Styles & CSS Embedding
But you're right, maybe it is better to just ditch themes. Building a custom control as you describe may be easier with an application global variable and some simple rules for picking style sheets (out of a directory or otherwise). Then again themes 'just work' with the exception of the placement overrides.
# re: ASP.NET and Styles & CSS Embedding
My biggest gripe with ASP.NET themes, and one of the reasons I devised this method, is that all of the css files are pulled into every page. This goes against some of the primary principles of css. If all of your css files are going to be pulled into every page, what is the purpose of having multiple files?
# re: ASP.NET and Styles & CSS Embedding
Is this going to affect anything?
# re: ASP.NET and Styles & CSS Embedding
<pages styleSheetTheme="default" theme="default">
to
<pages theme="default">
it fixes the error
# re: ASP.NET and Styles & CSS Embedding
# re: ASP.NET and Styles & CSS Embedding
HtmlLink css = new HtmlLink();
css.Href = this.ResolveUrl(this.StyleSheet);
css.Attributes.Add("rel", "stylesheet");
css.Attributes.Add("type", "text/css");
this.Header.Controls.Add(css);
in master page, VS 2008 RTM (3.5), I get squiggly lines, "The class or cssclass value is not defined", in all code pages.
Anyone know how to shut it off?
# re: ASP.NET and Styles & CSS Embedding
http://css-lessons.ucoz.com/css-parameters.htm
# re: ASP.NET and Styles & CSS Embedding
So after all this time and experience by those trying to use Themes what if any work-around actually supports the use of IE conditional comments but more importantly; how to change CSS stylesheets to cope with quirks of the other browsers?
# re: ASP.NET and Styles & CSS Embedding
In my case, all css, scripts and images have to be embedded resources.
I haven't got images such as those only referenced from within a style sheet to behave yet.
The whole lot is embedded as I work in in a "portal" environment where we add custom controls and can not add to the root file system.
My "mental" hold on all this is that if you use the Assembly:WebResource declaration, then use scriptmanager to get the resource you can then use:
ClientScript.GetWebResourceUrl(....)
to return a runtime location.
As per you other article in which you create a "ControlResources" class, it works fine for scripts but I'm stuffed If I can get to work with these '$%*(%.. embedded CSs images.
Do you have an example of that?
Regards
Rob
# re: ASP.NET and Styles & CSS Embedding
I applied all the rules to add a css in my custom control, but I can't find any effect of my css in parent page.
There is not any bug and I can see the css added in my custom control. but when I see the output viewsource I can see the CSS converted in WebResource.axd/.......encoded value.....
when I click in this link it shows 404 message that it cant find.
> Is there any change needed in web.config ?
I added a reference:
[assembly: WebResource("CustomizedModule.style.css", "text/css")]
namespace CustomizedModule
{
public class ServerCtrlCustomized : CompositeControl
{
....
On prerender I added this code:
protected override void OnPreRender(EventArgs e)
{
// Add our css file to our custom control
string cssUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(),"CustomizedModule.style.css");
HtmlLink cssLink = new HtmlLink();
cssLink.Href = cssUrl;
cssLink.Attributes.Add("rel", "stylesheet");
cssLink.Attributes.Add("type", "text/css");
this.Page.Header.Controls.Add(cssLink);
base.OnPreRender(e);
}
Please suggest me. I am stack with this problem from 2 days.
thanks.
# re: ASP.NET and Styles & CSS Embedding
Have you tried StylesheetTheme instead of Theme in web.config? StylesheetTheme is assigned early on in the process, Theme, however, comes last.
"Individual adjustments are sometimes necessary and you wouldn't want to be restricted by a theme once it has been set. The answer is that all templates permit overriding of the values defined in a theme, which means that in your page code you can assign any visual properties at will and rest assured they'll be taken into account when the page gets rendered to the browser."
From MSDN2 Introduction to the ASP.NET Master Pages Template Set > Themes versus StylesheetThemes