Setting up jQuery for ASP.NET Web Forms projects

During my recent presentation to the Victoria.NET DevSIG on jQuery, I talked about how to get started using jQuery with ASP.NET Web Forms projects. Part of this was around how I like to set up my projects with jQuery to take advantage of the script management features that ASP.NET & Visual Studio 2008 provide out of the box.

ASP.NET provides support for switching in different versions of your JavaScript files at runtime depending on the compilation setting in your web.config file (debug=true or false) via the ScriptManager control. This allows you to have your development script used during development & debug time, and an optimised script (minimised, obfuscated, etc.) used at release time. You can enable this feature of the ScriptManager with your own files (it does it automatically for the MS AJAX framework files) in a few ways, the easiest of which is to set the ScriptMode property of your ScriptReferences to Inherit.

<asp:ScriptManager runat="server">
     <Scripts>
         <asp:ScriptReference Path="~/script/myScript.js" ScriptMode="Inherit" />
     </Scripts>
</asp:ScriptManager>

Further to this, Visual Studio 2008, by way of a hot fix, adds support for a third type of JavaScript file, used only for providing JavaScript IntelliSense within the Visual Studio IDE. These special versions of your script files (known as “VSDoc” files) are carefully constructed to ensure optimal IntelliSense relevance, information & performance and generally are not able to be used at runtime at all.

So we have three file types in all as follows:

  • Release mode file: myscript.js
  • Debug mode file: myscript.debug.js
  • VSDoc file: myscript-vsdoc.js

When you download jQuery, you have the option of the standard file (jquery-1.3.2.js at time of writing) as well as the “production” file, which is minimised (jquery-1.3.2.min.js). Microsoft have also contributed a VSDoc file which you can download from the official jQuery source repository on Google Code.

So there are three types of jQuery files that match up with what ASP.NET & Visual Studio support, just two of them have the wrong extension. All we need to do is rename the files to match the features in the platform & tools:

  • jquery-1.3.2.js => jquery-1.3.2.debug.js
  • jquery-1.3.2.min.js => jquery-1.3.2.js

I have this files in a location on my hard drive where I can always get to them for new projects:

image

To use them in a project, just create a folder to hold them and right-click in solution explorer and choose Add –> Existing Item…

image

Now simply add a script reference for the jQuery files in the same way as you would for your own script. ASP.NET will use the large debug version when the app is in debug mode & the minimised version when it isn’t, plus you’ll get great IntelliSense support from Visual Studio 2008:

<asp:ScriptManager runat="server">
     <Scripts>
         <asp:ScriptReference Path="~/script/jquery-1.3.2.js" ScriptMode="Inherit" />
         <asp:ScriptReference Path="~/script/myScript.js" ScriptMode="Inherit" />
     </Scripts>
</asp:ScriptManager>

It would be nice if the next version of ASP.NET included support for jQuery’s default file extensions in the ScriptManager control, but until then this works very well.


Updating the ASP.NET validator controls to change invalid control’s CSS class on non-JavaScript clients

In my previous post I showed how you could add support for the ASP.NET validation controls to automatically change the CSS class of invalid controls on the client side using some jQuery. But what about when the client doesn’t have JavaScript enabled?

The simple anwser is to sub-class the built-in ASP.NET validation controls and add this behaviour ourselves, then use tag mapping in the web.config file to automatically use our modified validator controls instead of ASP.NET’s built-in versions.

The following is an example of how you could sub-class the RequiredFieldValidator control to support changing the CSS class of the associated control when it fails validation:

using System;
using System.Linq;
using System.Web.UI.WebControls;

namespace WebApplication16.Controls
{
    public class ClassChangingRequiredFieldValidator : RequiredFieldValidator
    {
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            var ctl = NamingContainer.FindControl(ControlToValidate) as WebControl;
            if (ctl != null)
            {
                if (IsValid)
                {
                    var className = String.Join(" ", ctl.CssClass.Split(' ')
                        .Where(c => !c.Equals("error")).ToArray());
                    ctl.CssClass = className;
                }
                else
                {
                    if (ctl.CssClass.Split(' ').FirstOrDefault(c => c.Equals("error")) == null)
                    {
                        ctl.CssClass = String.IsNullOrEmpty(ctl.CssClass) ? "error" : ctl.CssClass + " error";
                    }
                }
            }
        }
    }
}

Next you need to add some configuration to your web.config file to force ASP.NET to use this control instead of the build-in version:

<pages styleSheetTheme="SiteTheme">
 <controls>..</controls>
 <tagMapping>
 <add tagType="System.Web.UI.WebControls.RequiredFieldValidator" mappedTagType="WebApplication16.Controls.ClassChangingRequiredFieldValidator" />
 </tagMapping>
</pages>

Make sure you change the namespaces to match your application’s.

Just repeat this process for the other validation controls and combine it with the technique in my previous post to get your invalid controls’ CSS class automatically changed, either client-side if the user has JavaScript enabled, or server-side if not.


Set the CSS class of ASP.NET invalid controls using jQuery

The ASP.NET validation controls are a very quick and effective way to add client-side & server-side validation to your web forms but they lack one very important feature out of the box: the ability to change the CSS class of the controls that are invalid. Changing the class of the invalid controls will allow you to style them in such a way that they help bring attention to the fact that the user needs to do something more to complete the form successfully.

It is quite simple to add this functionality (for clients with JavaScript enabled at least) using a little bit of JavaScript and everybody’s favourite JavaScript library, jQuery!

First of all, set the CssClass property of all your validator controls to “validation-error”. The easiest way to achieve this is by using a skin file. See this post for more details on how to do this.

Then add the following to your site’s JavaScript file (assuming you’re already using jQuery):

 
/// <reference path="jquery-1.3.1.js" />

$(document).ready(function(e) {
    $("span.validation-error")
        .bind("DOMAttrModified propertychange", function(e) {
            // Exit early if IE because it throws this event lots more
            if (e.originalEvent.propertyName && e.originalEvent.propertyName != "isvalid") return;

            var controlToValidate = $("#" + this.controltovalidate);
            var validators = controlToValidate.attr("Validators");
            if (validators == null) return;

            var isValid = true;
            $(validators).each(function() {
                if (this.isvalid !== true) {
                    isValid = false;
                }
            });

            if (isValid) {
                controlToValidate.removeClass("error");
            } else {
                controlToValidate.addClass("error");
            }
        });
});

And there you have it. Now when any validator on your form has the isValid property tripped to false on the client side, the CSS class of the offending control will be changed to “error”.


Visual Studio 2008 XHTML 1.1. Templates – v1.1.1 Released

I’ve just posted a new release of the Visual Studio 2008 XHTML 1.1 Templates on CodePlex that fixes a couple of minor issues. If you use the templates and have any feedback I’d love to hear it.


Speaking at VIC.NET SIG next week on jQuery

I’ll be speaking at the Victoria.NET SIG meeting next week in Melbourne about jQuery. Come along if you want to learn about what jQuery is and how it can help you in your ASP.NET applications. RSVP details below.

 

jQuery: The way JavaScript should be

Thursday 29th January 2009
5:30pm (Pizza and drinks served); 6pm (presentations start)
Microsoft Theatre, Level 5, 4 Freshwater Place, Southbank

Hate the thought of writing JavaScript? Get frustrated by cross-browser DOM quirks? Ever wondered how those spiffy sites do that animation without Flash or Silverlight? Think that JavaScript isn’t a real programming language? Well come and see how jQuery makes writing JavaScript fun again. Microsoft is now shipping and supporting jQuery, an open-source JavaScript library, with ASP.NET and Visual Studio. jQuery is fast, lean, simple and hugely expandable, to enable you to build the most compelling web applications quickly and easily.

About the speaker:

Damian Edwards has presented at several Usergroup meetings, community events and Tech Ed on a wide range of topics. He is also a Microsoft MVP who specialises in ASP .NET and Web Front end development.

If you are planning on coming, please RSVP to help us organise drinks and catering.

To register, simply send an email to info@victoriadotnet.com.au. It is a FREE event and there is no charge for attendance but space is limited so you’ll need to let us know you’re coming so we can make sure there’s space. If you do not wish to receive event notification mails in the future, simply send a mail to info@victoriadotnet.com.au with the word Unsubscribe in the subject.

Twitter Account and other SIGs:

The user group has a Twitter account. You can follow the happenings in the group at http://twitter.com/vdnug

The Victoria .NET Dev SIG usually meets on the second Tuesday of every month to discuss developer related .NET topics. Victoria .NET also runs an Office and SharePoint group called MOSSIG (that meets on the 4th Wednesday of every month) and a SQL SIG (that meets 12:30-2:00pm on the 2nd Monday of each month) to discuss topics related to SQL Server development and a new Silverlight Developer and Designer Network user group


Adding HTML validity checking to your ASP.NET web site via unit tests

[UPDATED AGAIN: I’ve updated the test helper code to ensure that the W3C validation service is not hit more than once a second in accordance with their API guidelines. Thanks to Scott Baldwin for the tip and Mitch Denny for the threading code.]

[UPDATED: I’ve amended the helper method to return an object to represent the W3C validation check result rather than a simple boolean. This provides more flexibility to the test writer to check the number of errors or warnings returned. I’ve also changed the method to retrieve these values from the custom headers the validation service provides rather than searching the returned HTML.]

When developing standards compliant web sites, it is important to regularly check your mark-up for validity to ensure you are adhering to the standards your HTML documents declare they use (via a DOCTYPE declaration). While the W3C provides an excellent online validator for checking documents, it can be cumbersome to use regularly with internal, dynamically generated web sites, like those under development with ASP.NET.

We generally write unit tests against our .NET code to ensure it functions as expected. Therefore, it makes sense to me to test the HTML validity of our ASP.NET applications and web sites via a unit test. Doing so allows you to easily check the validity of your site while under development using the same tools integrated into Visual Studio that you use to test other parts of your solution. This also allows it to be easily integrated into your automated build and test processes, and break the build, if necessary, when your site’s HTML doesn’t validate.

The following class can be used in conjunction with the unit testing framework in Visual Studio 2008 to test the validity of your site’s runtime HTML against the W3C Markup Validation Service:

using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Threading;

namespace UnitTests
{
    /// <summary>
    /// Represents a result from the W3C Markup Validation Service.
    /// </summary>
    public class W3CValidityCheckResult
    {
        public bool IsValid { get; set; }
        public int WarningsCount { get; set; }
        public int ErrorsCount { get; set; }
        public string Body { get; set; }
    }

    static class TestHelper
    {
        private static AutoResetEvent _w3cValidatorBlock = new AutoResetEvent(true);

        private static void ResetBlocker(object state)
        {
            // Ensures that W3C Validator service is not called more than once a second
            Thread.Sleep(1000);
            _w3cValidatorBlock.Set();
        }

        /// <summary>
        /// Determines whether the ASP.NET page returns valid HTML by checking the response against the W3C Markup Validator.
        /// </summary>
        ///
<param name="testContext">The test context.</param>
        ///
<param name="aspNetServerName">Name of the ASP.NET server.</param>
        ///
<param name="path">The relative path of the resource to check.</param>
        /// <returns>
        /// An object representing indicating whether the HTML generated is valid.
        /// </returns>
        public static W3CValidityCheckResult ReturnsValidHtml(TestContext testContext, string aspNetServerName, string path)
        {
            var result = new W3CValidityCheckResult();
            WebHeaderCollection w3cResponseHeaders = new WebHeaderCollection();

            using (var wc = new WebClient())
            {
                string url = String.Format("{0}{1}",
                    testContext.Properties["AspNetDevelopmentServer." + aspNetServerName].ToString(),
                    path);
                string html = GetPageHtml(wc, url);

                // Send to W3C validator
                string w3cUrl = "http://validator.w3.org/check";
                wc.Encoding = System.Text.Encoding.UTF8;
                var values = new NameValueCollection();
                values.Add("fragment", html);
                values.Add("prefill", "0");
                values.Add("group", "0");
                values.Add("doctype", "inline");

                try
                {
                    _w3cValidatorBlock.WaitOne();
                    byte[] w3cRawResponse = wc.UploadValues(w3cUrl, values);
                    result.Body = Encoding.UTF8.GetString(w3cRawResponse);
                    w3cResponseHeaders.Add(wc.ResponseHeaders);
                }
                finally
                {
                    ThreadPool.QueueUserWorkItem(ResetBlocker); // Reset on background thread
                }
            }

            // Extract result from response headers
            int warnings = -1;
            int errors = -1;
            int.TryParse(w3cResponseHeaders["X-W3C-Validator-Warnings"], out warnings);
            int.TryParse(w3cResponseHeaders["X-W3C-Validator-Errors"], out errors);
            string status = w3cResponseHeaders["X-W3C-Validator-Status"];

            result.WarningsCount = warnings;
            result.ErrorsCount = errors;
            result.IsValid = (!String.IsNullOrEmpty(status) && status.Equals("Valid", StringComparison.InvariantCultureIgnoreCase));

            return result;
        }

        private static string GetPageHtml(WebClient wc, string url)
        {
            // Pretend to be Firefox 3 so that ASP.NET renders compliant HTML
            wc.Headers["User-Agent"] = "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1 (.NET CLR 3.5.30729)";
            wc.Headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            wc.Headers["Accept-Language"] = "en-au,en-us;q=0.7,en;q=0.3";

            // Read page HTML
            string html = "";
            using (Stream responseStream = wc.OpenRead(url))
            {
                using (var sr = new StreamReader(responseStream))
                {
                    html = sr.ReadToEnd();
                    sr.Close();
                }
            }

            return html;
        }
    }
}

You use it from your unit test like so:

[TestMethod]
[Description("Tests that the HTML outputted by the home page is valid using the W3C validator")]
[AspNetDevelopmentServer("WebApplication", "WebApplication")]
public void HomePageIsValidHtml()
{
    Assert.IsTrue(TestHelper.ReturnsValidHtml(this.TestContext, "WebApplication", "Default.aspx").IsValid,
        "The home page failed W3C Markup Validation (http://validator.w3.org).");
}

Note the use of the AspNetDevelopmentServer attribute on the test method. This tells the unit testing framework to spin up an instance of Visual Studio’s inbuilt ASP.NET web server (Cassini) with the name and at the path you specify. The runtime URL of that instance is then retrieved from the test class’ property bag (on the TestContext property) by the helper method above.

This sample could be easily extended to do more thorough checking against numerous endpoints automatically if need be, perhaps by reading the website’s .sitemap file, or crawling the hyperlinks found in the response to a given depth.

So now you have no excuses! You can easily incorporate the checking of your ASP.NET site’s runtime HTML for validity into your normal development cycle.


New release of Visual Studio 2008 XHTML 1.1 Templates available now

I’ve uploaded a new release that now includes item templates for C# & VB Web Application Projects and Web Site Projects. Plus, there is now a Project Template for creating C# Web Application Projects pre-configured to be XHTML 1.1 compliant, use a master page & theme, and the CSS Friendly Control Adapters. It has some other goodies in it too (e.g. support for print specific CSS files in themes).

http://www.codeplex.com/VSXHTML11Templates

Feedback always appreciated.


Visual Studio 2008 XHTML 1.1 Templates released to CodePlex

I’ve released a new CodePlex project: Visual Studio 2008 XHTML 1.1 Templates

As the name suggests it is a set of item templates for Visual Studio 2008 web projects based on XHTML 1.1. The initial version includes item templates for the following item types:

  • Web Form
  • Master Page
  • AJAX Web Form
  • AJAX Master Page
  • HTML Page
  • StyleSheet

At this stage only templates for C# Web Application Projects are included but I will add similar templates for VB and Web Site Projects shortly.

There is a simple installer included to make it extra easy.


IE8 bug I submitted has been resolved

One of the IE8 bugs I recently submitted via Connect has been resolved. It involves the use of the url() value for the content CSS property when using CSS content generation. You can see details of the bug on Connect here.


Settling into my new home

So here I am, all moved in at damianedwards.wordpress.com. It’s nice here, very refreshing. I’ve left my old place open so that the content I didn’t bother migrating will live on in case it’s ever helpful for somebody.