18 December 2006 - 1:00 PM / by Dominic Pettifer. 3 Comments for JavaScript/CSS and Custom Controls.
Technical Article - This guide will show you how to implement JavaScript code in your custom ASP.NET server controls, and implement them in such a way to decouple your custom control from the web application implementation. We’ll also look at CSS for your custom controls.
Almost every week I'm discovering something new and amazing with ASP.NET, that makes my life easier. Custom controls allow the developer to group together complex functionality (typically a load of HTML markup), so that it can be called via just a single construct...
<asp:MyCustomControl runat="server" ID="cstTest" ...
Recently I was designing a custom server custom for form input error messages ("Missing field" etc.). This control used dynamic DHTML/JavaScript effects to produce a hovering div that displayed additional information about the error. For a demonstration try to post a comment with all the fields blank, click on the (info) link in the error message, you'll see a hovering element fade in showing more info about the error. This is a custom server control that relies heavily on JavaScript and complex style attributes, so a custom server control seemed the best solution, rather than copy/paste the same boiler plate code everywhere.
While I knew about custom controls already, the new and amazing discovery was learning how to implement JavaScript and CSS into the control itself, without having to manually add them to the ASPX page in the web application itself. If a custom control relies on JavaScript for its implementation, or requires lots of CSS markup, we can programmatically add these to the Page header directly via the control itself, using the Page property for the control…
this.Page.Header.Controls.Add(someControl);
We could just manually add the JavaScript code to the page ourselves, or use an external script code file, but this creates a dependency between the page or web application and our custom control. We might want to reuse the control in another web application, and drop it straight in without having to mess around with any other implementation details. By getting the control itself to add these items, we're decoupling the custom control from the web application, so we can easily reuse it in other pages or applications.
Here is an example of rendering some JavaScript code...
protected override void OnPreRender(EventArgs e)
{
String scriptBlock = “alert(‘Hello World!’);”;
this.Page.ClientScript.RegisterClientScriptBlock(
this.GetType(), "Script Name", scriptBlock, true);
base.OnPreRender(e);
}The second argument 'Script Name' is a unique key that identifies the client script block. By using a specific name in our custom control, we ensure that this script block is only rendered once no matter how many instances of the control we have in the page. In the above example, if we had five controls on the page we would only see a 'Hello World' alert box once. If instead we wanted script blocks to be rendered multiple times for each control in the page, we would set its script name as the control's ID like so...
this.Page.ClientScript.RegisterClientScriptBlock(
this.GetType(), this.ID, scriptBlock, true);With five controls on the page, we would get five 'Hello World' alert boxes. For more info on how to dynamically render JavaScript code on the page, see page 2 of my previous blog, Get a<HEAD> with ASP.NET 2.0.
Dealing with page level style blocks is a little trickier, because they will be rendered multiple times for each control instance no matter what. The trick is to set an ID property for the style element being rendered by the custom control, and to look for it in the Pages header beforehand, to determine if it's already been rendered by another identical control type in the Page.
protected override void OnPreRender(EventArgs e)
{
HtmlGenericControl style =
(HtmlGenericControl)this.Page.Header.FindControl("Name);
if (style == null)
{
style = new HtmlGenericControl("style");
style.Attributes.Add("type", "text/css");
style.ID = “Name”;
style.InnerText = “p { font-weight: bold; }”;
this.Page.Header.Controls.Add(style);
}
}In this instance, the ID has been given a value of 'Name', but you might want to take steps to ensure this doesn't conflict with another style element in the head with the same ID name. A good practice is the use the custom controls fully qualified namespace and custom control ID like so...
style.ID = "__WebAppUtils.FormTools.FormMessageControl_" + this.ID;
Some caution must be taken when using Page Level styles for custom controls. When dealing with CSS there are three ways to implement them into HTML document. The best way is to use an external CSS file specified as a link in the header. A second way is the write them as page level styles which sit in the header eg...
<head>
<style type=”text/css”>
p { font-weight: bold; }
</style>
</head>If a page level style conflicts with an external style sheet file, the page level style has priority. A third way is the implement inline styles as a style attribute in the HTML element itself...
<p style="font-weight: normal;">Test here</p>
Inline styles offer the lowest level of granularity and can be messy to deal with, but will also override any conflicting page level styles and/or external styles sheets. So the priority levels goes as thus, inline styles over page level styles over external styles sheet files.
This overriding behaviour is what the developer of custom controls needs to look out for. If the web page uses an external CSS file that has styles set for elements that the custom control uses, the page level CSS styles for the control will win out, which is what we want. Also if the web page has some hand written page level styles, the dynamically added page level styles will be added at the end of the <head> controls collection, and because they come last in the document flow, will have priority over the hand written CSS styles, if they conflict.
However, if a bunch of page level styles are added dynamically by another process, and conflict with styles set for the custom control, we could potentially have a problem with the custom control not being rendered correctly. So maybe it's best to simply render the CSS styles inline (which override all other conflicting styles) for the custom control. This is something the built in ASP.NET controls do, such as the Calendar Control.
Also adding page level styles to the <head> requires the runat="server" attribute to be set for the page (something a custom control can’t do by itself). If it's not then it throws a runtime error, not a compile time error. You could programmatically check for this in the custom control...
if (this.Page.Header != null)
{
// Implement CSS code
}
that's all cool & great tips to read & try, thank you very much for sharing.
Posted on 25 May 2010 - 5:22 PM / by JavaScript Countdown Timer
Agreed. Very good article, thanks.
Posted on 27 January 2009 - 3:11 PM / by Patrick
Thank you sooo much for the insight. I've been searching like a madman last couple of days on how to insert CSS directly into a page from a custom control. No luck until now...thanks again.
Posted on 12 January 2008 - 4:09 AM / by kerrigan
Thank you sooo much for the insight. I've been searching like a madman last couple of days on how to insert CSS directly into a page from a custom control. No luck until now...thanks again.
Bugatti Veyron - As a photo mosaic. (from the blog Photo Mosaic Generator - Fun Adventures With Silverlight )
@andrewmy I'm thinking of using Git myself, what Windows client do you use? Is TortoiseGit the defacto standard?
about 5 hours ago from EchofonThe new Google Logo is awesome, almost as good as Pacman
about 8 hours ago from Echofon"Normalization is from the devil" - do you agree? http://ayende.com/Blog/archive/2010/09/06/normalization-is-from-the-devil.aspx
12:48 PM September 6th from Echofon@ellieemptylemon Thanks for those, although company I work for is looking to send us on training courses.
11:19 AM September 6th from Echofon