November 2004 - Posts

A Sneak Peak of skmFAQs.NET
30 November 04 03:37 PM | Scott Mitchell

In an earlier blog entry I asked for suggestions on making a FAQ Web application. Those suggestions, along with my own ideas, is nearing release of an open-source product, which I have named skmFAQs.NET. skmFAQs.NET is designed to make it easy for a group of individuals to create a FAQ-based Web site. In a way it's a Wiki with limited access - only explicitly specified users can alter the FAQ contents, and there are varying levels of permissions, from full-blown administrators to folks whose FAQs must be approved by a moderator before they'll appear on the site. (For more on skmFAQs, see this FAQ: What is skmFAQs.NET?)

Anywho, I'm hoping to finally get a beta version out the door by the end of this week. In the mean time, I have crated the “official” skmFAQs.NET site, aptly named, http://skmFAQs.NET. If you have a moment, why not take a gander and let me know if you find any typos, oddities, or have any suggestions on making the site more usable. And please take pity on me, I am by no means a graphical kind of person; the site is all text and done up in a yellow theme.

Once I release a beta of skmFAQs.NET, I'll be very interested in having folks give it a whirl, letting me know what issues they encountered in setting it up, what bugs they find, feature suggestions, etc. If you'd like to be a beta tester, watch this blog as I'll post an entry once the beta is available for download. Thanks!

Filed under:
My Wife's First Contribution to 4Guys
23 November 04 12:29 PM | Scott Mitchell

Like yours truly, my wife also creates ASP.NET applications for a living. I shudder to think what our future children will be like, perhaps they'll be bilingual from an early age, speaking both English and C#. A common question on many newsgroups and online forums is how to use the validation controls in ASP.NET 1.x to limit the user's input to a specified number of characters. The typical answers are, “Use the TextBox's MaxLength property,” or, “Use a CustomValidator.” I was never a big fan of either of these approaches - the former still requires server-side code since a nefarious user could easily cirumvent this setting in their browser; the latter approach is better, but not very portable to other pages or Web sites, as the server-side and (optional) client-side code must be repeated on each page/site that utilizes the CustomValidator.

So, I've always wanted to write a custom validation control that provided this functionality. It's been on my TODO list for a while, but has very low priority. Anywho, my ol' lady had a slow day at work a week ago or so, and decided to whip up this control that's been on my plate for a while. She's not a fan of writing like yours truly, so I whipped up an article describing her code and illustrating how to use the control - you can learn more at Creating a TextBoxLengthValidator Validation Control. There's a live demo available here. You can download the complete source code, along with a compiled assembly, here.

Enjoy!

Filed under:
My Latest Computer Book - One for the Non-Technical Crowd
22 November 04 11:15 AM | Scott Mitchell

A couple months ago my first computer book geared at the non-technical crowd came out: Create Your Own Website Using What You Already Know. The book provides a CD that contains five Web site templates and the Mozilla Composer software, and takes the reader through how to customize the existing templates to create their own websites without needing to know/use a lick of HTML or any other scary acronym. The five templates are designed to provide everyday sites that Joe Average might be interested in creating. There's a template for a Personal/Family site, one for an Organization, and even an eCommerce one, that shows how to use PayPal to accept payments online. (Again, all of this done without the user needing to write a lick of markup. No < or > found between this book's covers!)

Over a year ago I wrote about how, in my opinion, it was best for an author to find his niche and stick with it. That is, if you write computer books for beginner to intermediate Web developers, stick with it. If you write advanced books for brainy system architects, keep writing for that market. When I authored this blog entry, I had just started this Create Your Own Website book, and was hashing out my thoughts on the matter. In retrospect, writing this book took longer and caused for frustration on my part than any other book I've written. Even though this book has 145 pages - several hundred less than most technical computer trade books - it took me roughly nine months of writing to get those 145 pages, compared to the three or so months most ASP-related books require from me.

This longer than usual schedule was due to a couple of factors:

  • With Creating Your Own Website I had to write to an audience I hadn't written to before. When talking about and explaining Web development, the words flow from my fingers, but writing a paragraph about centering text in a Web page, or explaining and demonstrating how different parts of the page could be stylized with different fonts, text sizes, colors, and so forth? Let's just say it took a bit of trial and error before I got into a groove when writing about these topics.
  • The book's goals were not clearly defined at the start of the project. As the writing schedule progressed, the book's intent shifted from more of a “create a website from scratch without HTML,“ to, “use these pre-designed templates to build your own website.“ This change, which was the publisher's decision (and the right one, IMO), caused a bit of frustration since I had already written about 80% of the book detailing creating pages from scratch.
  • The review/publisher markup period took longer than with standard computer trade books. Usually when an author completes a book, there's typically two to three months of editing, author reviews, and layout before the book gets shipped to the printer. This book took several weeks longer (even though it was shorter) because it's a lot fancier than most computer trade books. It's a multi-colored book, for example (not black and white); on virtually all pages there's a margin with sidebars pointing out other websites, “dos and don'ts,“ and other such miscellany. As you can imagine, this stuff takes time to compile, organize, and layout. The end result, though, is a very stylin' book. The book would have no trouble fitting in in one of those pictures of a modern, ultra-hip urban apartment. Can't say the same about, oh, ASP.NET: Tips, Tutorials, and Code.

At this point, I'm glad I did this book if, for nothing else, the experience. I sincerely doubt I will ever write a book for computer newbies again, although I've yet to receive the first report on book sales... if the book is selling wildly I may change my tune, naturally. It was a neat experience, just a bit too slow going and frustrating to want to tackle a similar project. Writing about ASP.NET is easily, more enjoyable, and in a topic area where I can leverage my name/past experience. (That is, there are existing ASP.NETers who know of my books and might be more inclined to pick up another book by yours truly; I doubt any computer newbies, outside of friends or family, has any idea who I am or my past writing.)

And for those interested in learning more about the book, I think this Amazon.com reviewer's comments sum up the book very well:

Mitchell gives us a breezy, confident approach to making a website. He emphasises that, within reason, you can do it all by yourself. With the aid of this book, naturally. He walks us through the templates on the CD that comes with the book. The general thrust is to show how if you can read and write, well, then you can make a website. There is certainly more involved than in merely using a browser to peruse the web. But the book gives a total absence of HTML tags, until the last chapter.

You can regard this book as an independent validation of the claims made for the above tools. Indeed, no programming appears to be necessary for a simple website. Along these lines, the chapter on making an online storefront is well done. The example storefront is actually quite limited. But even so, to have it running without programming is impressive.

Of course you are strongly encouraged to purchase multiple copies for your non-computer savvy friends and family members. :-)

Filed under:
Maintaining Scrollback Position Across Postbacks
19 November 04 12:07 PM | Scott Mitchell

Anyone who's created a large Web Form that entails a load of postbacks can atest to the user interface problem that occurs in such scenarios:

  • “Flashing“ of the UI elements, and
  • Loss of scroll position (namely, if you scroll down to the bottom of a page, and then interact with the UI as to cause a postback, the scroll position is lost)

One option to curing these ills is to use SmartNavigation. SmartNavigation uses a hidden IFRAME to handle the postback, so the window being viewed by the user is not “refreshed,” per se, and therefore doesn't suffer from the flashing and loss of the scroll position. There are, however, two main problems with SmartNavigation:

  1. It only works with IE 5.5+
  2. As others have noted, it seems to cause more harm than good. There are a myriad of confusing, hard to diagnose errors that can creep up once SmartNavigation is turned on.

Recently Steve Stchur authored an article for 4Guys that demonstrated how to obtain scroll position on postback without using SmartNavigation. In Maintaining Scroll Position on Postback, Steve introduces a custom server control that, once plopped down on a page, automatically maintains scroll position for the page on postback (see a live demo). For a recent project of mine, I took this idea and tweaked it so that rather than using a server control, I extend the System.Web.UI.Page class, and have those pages that need to maintain scroll position on postback inherit from this extended class. Here is the code for my extended Page class:

Imports System.text

Public Class SmartScollerPage
Inherits Page

Protected Overrides Sub OnPreRender(ByVal e As EventArgs)
If Not Request.Form("scrollLeft") Is Nothing Then
RegisterHiddenField("scrollLeft", Request.Form("scrollLeft"))
RegisterHiddenField("scrollTop", Request.Form("scrollTop"))
Else
RegisterHiddenField("scrollLeft", String.Empty)
RegisterHiddenField("scrollTop", String.Empty)
End If

Dim scrollScript As New StringBuilder(1000)
scrollScript.Append("<script language = ""javascript"">")
scrollScript.Append(String.Concat(vbCrLf, "<!--", vbCrLf))
scrollScript.Append("function SmartScroller_GetCoords() {")
scrollScript.Append(String.Concat(vbCrLf, "var scrollX, scrollY;", vbCrLf))
scrollScript.Append("if (document.all) {")
scrollScript.Append(String.Concat(vbCrLf, "if (!document.documentElement.scrollLeft)", vbCrLf))
scrollScript.Append("scrollX = document.body.scrollLeft;")
scrollScript.Append(String.Concat(vbCrLf, "else", vbCrLf))
scrollScript.Append("scrollX = document.documentElement.scrollLeft;")
scrollScript.Append(String.Concat(vbCrLf, "if (!document.documentElement.scrollTop)", vbCrLf))
scrollScript.Append("scrollY = document.body.scrollTop;")
scrollScript.Append(String.Concat(vbCrLf, "else", vbCrLf))
scrollScript.Append("scrollY = document.documentElement.scrollTop; }")
scrollScript.Append(String.Concat(vbCrLf, "else {", vbCrLf))
scrollScript.Append("scrollX = window.pageXOffset; scrollY = window.pageYOffset; }")
scrollScript.Append(String.Concat(vbCrLf, vbCrLf))
scrollScript.Append("document.getElementById('scrollLeft').value = scrollX;")
scrollScript.Append(vbCrLf)
scrollScript.Append("document.getElementById('scrollTop').value = scrollY;")
scrollScript.Append(String.Concat(vbCrLf, "}", vbCrLf))

scrollScript.Append(String.Concat(vbCrLf, "function SmartScroller_Scroll() {", vbCrLf))
scrollScript.Append("var x = document.getElementById('scrollLeft').value;")
scrollScript.Append(String.Concat(vbCrLf, "var y = document.getElementById('scrollTop').value;", vbCrLf))
scrollScript.Append("window.scrollTo(x, y); }")

scrollScript.Append(String.Concat(vbCrLf, vbCrLf, "window.onload = SmartScroller_Scroll;"))
scrollScript.Append(String.Concat(vbCrLf, "window.onscroll = SmartScroller_GetCoords;", vbCrLf))
scrollScript.Append("window.onclick = SmartScroller_GetCoords; window.onkeypress = SmartScroller_GetCoords;")
scrollScript.Append(String.Concat(vbCrLf, "// -->", vbCrLf))
scrollScript.Append("</script>")

RegisterClientScriptBlock("scrollCode", scrollScript.ToString())
End Sub

End Class

If you compare my code to Steve's (which is written in C#), you'll notice a few subtle changes. Rather than inject the code in the OnInit() method, as Steve does, I have mine emitted in the OnPreRender() method instead. There's no reason the script can't be emitted in the Initialization stage, but typically client-side script is not generated until the PreRender stage, since typically the script's values could depend on properties/methods set/called by the page developer during stages after Initialization (but before PreRender).

Also, Steve uses HtmlInputHidden controls to hold his values across postback. This is probably a better approach, as these controls automatically hold their own view state. Instead, I use the RegisterHiddenField() method to squirt out the hidden form fields directly. Of course, in doing this I need to check the Request.Forms() collection and manually add in the values across postbacks (this is what the HtmlInputHidden control's do for us with their view state automagically).

Finally, in the client-side script to reference my hidden form fields I use document.getElementById() rather than document[formID].hiddenInputID, like Steve uses. The benefit of my approach is that I don't need to get the form's ID, which Steve does in his control's code by recursing through the control hierarchy. (For completeness, my extended Page class should have a call to VerifyRenderingInServerForm().)

Of course, all of this will be moot when ASP.NET 2.0 ships, but for those (like myself) still developing and deploying ASP.NET 1.x solutions, this code provides a better user interface by returning to the previous scroll position on postback. Enjoy!

Filed under:
Rob Howard's 10 Tips for Writing High-Performance Web Applications
17 November 04 11:32 AM | Scott Mitchell

Rob Howard recently authored an article for MSDN Magazine titled 10 Tips for Writing High-Performance Web Applications. This article, which contains a lot of material from Rob's Black-Belt ASP.NET Talk given at TechEd this year, offers the following performance tips:

  1. Return multiple recordsets
  2. Efficiently page data access
  3. Connection pooling
  4. Use the ASP.NET Cache API
  5. Per-Request Caching
  6. Background Processing
  7. Page Output Caching & Proxy Servers
  8. Run IIS 6.0 for Kernel Caching
  9. Use GZip Compression
  10. Watch That Server Control View State

I think the #1 performance issue for many ASP.NET applications is bloated view state. Yes, all of the other tips will improve your performance, but slimming down a page's view state bloat can cut several kilobytes to thousands of kilobytes from your page, thereby increasing both the download and upload (i.e., postback) speed of the page. Trust me, I know a thing or two about large view state.

When Rob mentioned tip 5, Per-Request Caching, in his TechEd talk, the topic interested me so I wrote an article on it: HttpContext.Items - a Per-Request Cache Store.

Another gem from Rob's article is the Common Performance Myths sidebar, which points out the following:

  • C# code is not faster than VB.NET code
  • ASP.NET page's using code-behind are not faster than ASP.NET page's using the inline method
  • Placing logic in .NET components does not provide a performance benefit over putting that logic directly in the ASP.NET page.
  • Web services should only be used to connect disparate systems or provide remove access to system functionality. They should not be used as the conduit between two similar, local systems. As Rob says in the article: “Optimizing any kind of process hop in your application is the first place to start to achieve better performance.“ And Web services are far from performant when compared to technologies designed to transmit data in a specialized protocol optimized for a particular architecture or platform.

One thing I wonder about, though, is Rob's suggestion for paging through large amounts of data. He suggests using a stored procedure with a temporary table into which you dump the entire contents of the data to page through, and then use a join to snip out the particular records for the specified page (granted, you're just dumping in the primary key field, which is likely already (partially) in memory, so the operation should go fast). However, in some very rough and very unscientific tests I ran on my database server a couple years ago, this technique started taking several seconds per page once there were hundreds of thousands of records being paged through. I ought to rerun these tests, though, and in a more controlled environment, since this is the technique used for the ASP.NET Forums, and the ASP.NET Forums seem to page through data efficiently enough.

What I'd really be interested in is comparing this technique to Justin Lovell's technique for SQL pagination, which uses a temporary table like Rob's approach, but rather than dumping the entire contents in, uses a cursor to just snipe out the appropriate number. (This, of course, requires that you remember the last ID of the page of data you are stepping through, which prohibits you from allowing users to jump to an arbitrary page.) If limiting oneself to only Next/Previous paging functionality is acceptable, I typically use this approach, myself, which I found to be able to scream through large amounts of data that the stored procedure technique Rob discussed choked on (performance-wise).

Has anyone out there run any sound tests comparing various paging techniques? I'd be interested in seeing the data...

Filed under:
Author Agreements
16 November 04 09:31 AM | Scott Mitchell

If you find yourself in the position where you have pitched an idea to a book publisher, or if a book publisher has come to you, asking if you'd contribute to a book, the next step is signing of an author agreement. This document spells out the terms of the arrangement, including things like:

  • Your royalty rate, and the disbursement schedule of the advance
  • The writing schedule, spelling out how much of the book you'll need to have completed by what dates
  • Legalese spelling out the responsibilities of the publisher and author
  • Legalese spelling out who owns what. Typically, the publisher will own the rights to the work as long as they're keeping it in print, returning the rights to the author once the book is taken out of print.

As with any contract, before signing your soul on the dotted line of an author agreement, it is important that you carefully read the agreement, ask any questions you may have, and negotiate terms as you see fit. Of course the best way to do this is with the aid of a lawyer, but many authors forgo the advice of a legal professional.

Recently, Joe Mayo sent me a link to Dave Taylor's blog entry, What Pitfalls Do I Need to Avoid in an Author's Publishing Contract? Definitely worth reading before signing an author agreement document.

Speaking of book writing, I'll be getting back into the swing of book writing here in a couple of months.

Filed under:
Question: Disabling Request Validation from the Page
12 November 04 06:47 PM | Scott Mitchell

One of the new features in ASP.NET 1.1 was Request Validation, an addition that, by default, disallowed users from submitting form fields that contained potential markup. In the classes I teach I find that most developers are unaware of Request Validation's existence, and don't realize that it's there, checking for HTML markup, until they build their first application where they want to let the user submit HTML markup. And then they get the forboding:

Turning off Request Validation is pretty easy. As the Request Validation FAQ explains, you can turn it off on a page-by-page basis, via the @Page directive:

<%@Page ... ValidateRequest=“False“ %>

Or you can set it for the entire Web application through Web.config, or for your entire Web server through machine.config. Today, though, I found an interesting question on the microsoft.public.dotnet.framework.aspnet.webcontrols newsgroup from Umut Tezduyar, who asks:

I want to disable RequestValidation that consumers of my custom control. Is
there a way for this? ... By using the control that i am developing, my users can write html from the web site. But framework 1.1 is disables it by default. ... I want to disable it or allow only from some of my controls can post html text.

The question boils down to, Is there some property a custom, compiled control could set that would disable Request Validation for the page is exists on (or, perhaps, turn it on)? After a bit of poking around with Reflector, it appears that the answer is, No. In fact, to my knowledge you can't programmatically muck with any of the page-level directive values through a page's source code portion.

As I discussed in Understanding ASP.NET View State (see “The ASP.NET Page Lifecycle” section), when an ASP.NET page is visited for the very first time, or for the first time after the HTML portion has been modified, the ASP.NET engine creates an autogenerated class whose primary responsibility is to construct the control hierarchy programmatically, based on the page's HTML markup and Web controls. During this compilation, the page's directives are inspected, and their values affect the resulting autogenerated class. For example, if you have @Register directive, the compiler adds assembly dependencies; in your @Page directive, if you have Debug=”True,” the compiler ensures that you have sufficient rights.

In any event, during this processing of the directives, which can be found in the ProcessDirective() method of the TemplateParser, TemplateControlParser, and PageParser classes, the PageParser class checks to see if the ValidateRequest attribute has been added to the @Page directive, and assigns a Boolean member variable the result of this attribute (or True, if the attribute it omitted). When the compilation actually occurs via the PageCompiler, TemplateControlCompiler, and BaseCompiler classes, the PageCompiler's BuildFrameworkInitializeMethodContents() method injects a call in the autogenerated class to Request.ValidateInput(), which does the request validation check and throws the HttpRequestValidationException, if needed.

Since this line of code is literally added to the autogenerated ASP.NET page prior to the page even running, I don't see a way on how to programmatically suspend Request Validation on a given page. Does anyone else see any workarounds? Initially I thought it might be possible to somehow HTML encode the received input in, say, the Initialization stage of the page lifecycle, hoping beyond hope that the call to Request.ValidateInput() didn't come until after Initialization, but, alas, the call comes in the Instantiation stage, which preceeds Initialization. One possible option might be to have the custom control emit a bit of client-side JavaScript that HTML encodes the control's input prior to the Web Form being submitted. Any other ideas?

Filed under:
DataGrid Talk Tonight at the San Diego .NET Beginners SIG
11 November 04 04:26 PM | Scott Mitchell

Tonight I'll be giving a talk to the San Diego .NET Beginners SIG, a relatively new Special Interests Group of the San Diego .NET User Group. My talk tonight will focus on the basics of the ASP.NET 1.x DataGrid, the talk being titled “An Introduction to the ASP.NET DataGrid.” If you live in San Diego (and are reading this in time!), the talk is in Del Mar from 6:30 pm to 8:00 tonight and includes free pizza. If you can't make the meeting, but would like to review my slides and demo application, you can download the PowerPoint presentation and example code here.

Hope to see y'all there!

Filed under:
My New "Providing Help" Policy
10 November 04 10:46 AM | Scott Mitchell

Having authored hundreds of online articles and answered hundreds of questions in newsgroups and assorted online forums, I typically get a dozen or so emails per day from folks around the world asking for help on a particular problem. In the past, I usually answered those that could be answered with a quick response, and directed others to articles or recommended that they post their question on ASPMessageboard.com. I have decided to implement a new policy for those who directly ask me for help: I'm not going to answer a single question sent to me directly.

What I am going to do, instead, is direct those who ask me questions directly to http://scottonwriting.net/GettingHelp/. On that page I explain that I will only answer a question that has been posted to specific newsgroups or online forum. Why? If I answer a question directly, I've taken up time helping one person; if they post it on a messageboard, and I answer that question, my time is amortized over the potentially hundreds of readers who have the same or a similar problem.

I know that certain people really dislike asking questions publicly, even if it is behind an alias on an online forum. Or perhaps they have a question that they feel is too sensitive to air out to the world, one that is vital to their core line of business, which they don't want their competitors to see. For those folks, I have decided to start offering Premium Support. With Premium Support I will thoroughly and quickly answer a question for an agreed upon price (the base price starts at $75, and might go up based difficulty/time needed to answer the question).

My hope is that the gaggle of folks who email me directly will post their questions to online venues, and those questions will be answered there, either by myself or someone else, and that those answers will not only help the initial asker, but also future individuals as well.

Random (Perhaps Outdated) Web Service Tip for the Weekend
06 November 04 10:07 PM | Scott Mitchell

With the Web Service Enhancement (WSE) Toolkit it is very easy to view incoming/outgoing SOAP requests - just check the “Enable Message Trace“ option in the option in the Diagnostics tab of the WSE Toolkit, and off you go. But prior to the WSE Toolkit, providing such essential debugging information was, unfortunately, not as straightforward or easy. Older books on Web services, for example, talk about using the SOAP Toolkit, from which you can create a SOAP proxy to record the incoming and outgoing SOAP messages. However, there's a much easier way, as Web service-guru Michele Leroux-Bustamante showed me 14 months ago, almost to the day: in a Web service method, add th following code to save the incoming SOAP content:

HttpContext.Current.Request.SaveAs(filePathToSaveSOAPData, true);

This will dump the incoming request to a file, including the headers (set the second parameter to False to omit the headers). Unfortunately this raw text output is not nicely indented like the WSE Toolkit output, but it provides a simple way to quickly inspect the incoming SOAP requests from the Web service.

Speaking of Michele, she'll be speaking at the ASP.NET Connections conference, which starts up with pre-session tracks tomorrow and continues throughout all next week. I mention this because I'll be there, too. Well, not inside the conference, per se, but in Vegas, at the Mandalay Bay resort. My wife's company is sending her, so I decided to tote along as a bit of a vacation (for myself). So, while my wife's toiling away in the conference sessions, learning the latest and greatest information about ASP.NET 1.x and 2.0, I'll be hitting the blackjack tables! (More accurately I'll likely be holed up in the hotel room, working on my two upcoming MSDN articles, but I promise to at least periodically fantasize about striking it rich with lady luck.)

Filed under:
Programmatically Determining if WebUIValidation.js is Included in a Page?
03 November 04 09:19 PM | Scott Mitchell

I was browsing through the microsoft.public.dotnet.framework.aspnet.webcontrols newsgroup tonight and stumbled upon a post by a fellow named Haz, who asked:

I am building a web custom control and I need to make sure that the file
/aspnet_client/system_web/{framework version}/WebUIValidation.js
is included in the page.

Is there any way to force the page to make sure that it is included?

My understanding is that Haz is wanting to be able to programmatically determine from his custom control whether or not the page has the client-side, JavaScript include in there for the WebUIValidation.js file. I assume he is wanting to have his control's markup somehow interact with this file, so he needs to determine if it's been added or not.

The WebUIValidation.js file, as you know, contains client-side JavaScript functions for client-side validation. It is automatically injected into a page when:

  1. The page contains one or more validation Web controls.
  2. The page is being visited by an “uplevel” browser.

I fired up Reflector to sneek a peak at the System.Web.UI.WebControls.BaseValidator class, which is the class from which all validation Web controls are derived. Initially I looked at the RegisterValidationCommonScript() method, which is responsible for squirting out the JavaScript include file reference. As I've waxed on about in my article Working with Client-Side Script, the Page class contains assorted methods for injecting client-side script. When adding such script, you have to give it a key; there are corresponding methods to determine whether or not a script block with a specified key name has been added.

My initial though, then, was to simply check if a script block with key ValidatorIncludeScript had been registered (as this is the key name the BaseValidator uses when adding the JavaScript include reference). However, the RegisterValidationCommonScript() method, from which this script is injected, doesn't happen until the PreRender stage of the Page's lifecycle. If you can afford to wait this long, then this is the definitive approach. To Haz I say this: in your control, you could override the OnPreRender() method, check to see if ValidatorIncludeScript has been added, and, if not, add it yourself. (You can get the necessary code straight out of the BaseValidator class; in fact, I'd recommend you just copy and paste the code for the RegisterValidationCommonScript() method into your control's OnPreRender() method.

But what if you need to determine if the WebUIValidation.js script will be added? That is, what if you need to know in, say, the Load event if this script will be added. Well, the Page class has a public Validator property, which is a collection of the validators on the page. The BaseValidator class has a method definition for the OnInit() method, which runs during the Initialization stage. In this method, the validator control adds itself to the Page's Validator collection. So, you can simply check if Page.Validator.Count > 0 - if so, there are one or more validators on the page and the WebUIValidation.js script will be emitted. ... Right?

Well, maybe not. Remember that the the WebUIValidation.js script is emitted if there are any validators on the page and if the browser visiting the page is an uplevel browser. Huh. The question, then, is how do validators determine if a browser is uplevel or not? As I discuss in Client-Side Validation in DownLevel Browsers, the BaseValidator class has a DetermineRenderUplevel() method that does the following check: a browser is considered “uplevel” if it has an MS DOM version of 4 or greater.

Note: By default, ASP.NET only consideres IE 4.0+ uplevel; however, you can enhance your Web application and specify that other browsers (FireFox, Mozilla, etc.) should be considered uplevel. The problem here - as I discuss in Client-Side Validation in DownLevel Browsers - is that the actual script emitted by WebUIValidation.js is non-standards compliant, and only works with IE... Bummer, eh? There are work-arounds, as I discuss in my article.

So, if you need to know prior to the PreRender stage whether or not the WebUIValidation.js script will be emitted, you can determine if it will if both Page.Validators.Count > 0 and Request.Browser.MSDomVersion.Major >= 4.

When digging around, one thing concerned me: what if I add a dynamic validation control - what happens then? Is the Page.Validator collection updated accordingly? The answer is, Yes. Whenever you add a control to another control's Controls property, it takes the added control through the Initialization and LoadViewState stages, at minimum, and through the Load stage as well, if the parent control is past the Load stage itself. So, Page.Validators will be updated accordingly, but the one caveat is that if you are checking at, say, the custom control's Load stage, and another control in the hierarchy later adds a validator dynamically, the first control will “miss” that the WebUIValidation.js script will be emitted. (I hope that doesn't sound too confusing.)

Filed under:
More Posts

Archives

My Books

  • Teach Yourself ASP.NET 4 in 24 Hours
  • Teach Yourself ASP.NET 3.5 in 24 Hours
  • Teach Yourself ASP.NET 2.0 in 24 Hours
  • ASP.NET Data Web Controls Kick Start
  • ASP.NET: Tips, Tutorials, and Code
  • Designing Active Server Pages
  • Teach Yourself Active Server Pages 3.0 in 21 Days

I am a Microsoft MVP for ASP.NET.

I am an ASPInsider.