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.


Using PageDataSource (was ParentDataSource) with ASP.NET Web Site projects

I got a request today from somebody who was trying to use my ParentDataSource (now called PageDataSource) control in an ASP.NET Web Site project and was running into problems. The bound control kept throwing an InvalidOperationException saying it couldn’t load the type. Turns out the problem is with the difference in the way that pages and assemblies are dynamically compiled and loaded by the BuildManager in web site projects. The fix involves simple adding the dynamic assembly name to the type name so that the BuildManager can locate it correctly when the bound control asks for it:

if (ctl.Parent == null)
{
    // At the top of the control tree and user control was not found, use page base type instead
    this.TypeName = Assembly.CreateQualifiedName(
        this.Page.GetType().Assembly.FullName,
        this.Page.GetType().BaseType.FullName);
    _parentHost = this.Page;
    return;
}

You can download the updated control class file from here.


AccessKeyHighlighter v1.0.1 released

I’ve released an update to the AccessKeyHighlighter to resolve a couple of issues and add some new features. Check it out on its CodePlex page.


Announcing the AccessKeyHighlighter control for ASP.NET AJAX

I’ve finally managed to get the first release of my AccessKeyHighlighter control for ASP.NET AJAX published. When the control is placed on a form it highlights the access keys assigned to form fields when the user holds down the access key shortcut key in their browser (Alt for IE & Safari, Shift+Alt for Firefox). You can see a live demo of it in action here.

If you find it useful, be sure to leave me a note on the CodePlex site, and if you find any issues or have any suggestions for improvements or new features, please create a feature request on the Issue Tracker page.


IE8 beta 1 browser version targeting and ASP.NET Themes

You might find that when you add the meta tag required to force IE8 beta 1 into IE7 (or 6) rendering mode into your ASP.NET page (or Master Page) that it doesn’t work as expected…. IE8 continues to render the site in full standards mode. The reason might be that in beta 1 of IE8 the “X-UA-Compatible” meta tag must be the first element in the page head element and by default ASP.NET will insert the link tags to the CSS files in your theme at the beginning of the head element of your page (or Master Page), before any other elements that might actually be defined in the .aspx or .master file.

To fix this, add the following code to the page’s (or Master Page’s) PreRender event handler (Page_PreRender):

// Move browser tageting meta tag to top of header
HtmlMeta meta = null;
int metaXUACompatIndex = -1;
foreach (Control ctl in page.Header.Controls)
{
    meta = ctl as HtmlMeta;
    if (meta == null)
        continue;

    if (meta.HttpEquiv == "X-UA-Compatible")
    {
        // Grab index
        metaXUACompatIndex = page.Header.Controls.IndexOf(meta);
        break;
    }
}
if (metaXUACompatIndex > 0)
{
    HtmlMeta metaXUACompat = page.Header.Controls[metaXUACompatIndex] as HtmlMeta;
    page.Header.Controls.RemoveAt(metaXUACompatIndex);
    page.Header.Controls.AddAt(0, metaXUACompat);
}

This will move the browser targeting meta tag to the top of the list so that IE8 will honour it.


ASP.NET: Binding the ObjectDataSource to the hosting page or user control

In my recent RDN presentation on CSS Layout with ASP.NET & Visual Studio 2008 I mentioned a technique I use to get some extra value from the ObjectDataSource control. From speaking with other developers I’ve found that many people try to use the control only to be dissatisfied with the way it works, and in particular the need to provide it with a separate business logic or gateway class to look for its CRUD methods on. This is most apparent when you are trying to separate the data-access, business and presentation logic through way of a MVC or MVP pattern like “Passive View“. In this scenario most people go back to the method of setting the DataSource property of their data bound control directly and manually calling the DataBind() method. This is a shame as it prevents you from realising the productivity gains and ease of maintenance of the no-code support for paging and sorting when using a data source control (plus the ability to fully style the GridView as described in my previous article).

Rather than abandoning the ObjectDataSource altogether, we can make it work for us, by sub-classing it and changing it’s behaviour slightly so that it looks for its methods on the hosting page or user control instead, i.e. its parent in the control hierarchy.

(If you have never looked at the data-source controls and two-way data-binding support in ASP.NET 2.0 before, it might be worth first checking out Scott Mitchell’s excellent tutorials on the subject.)

Start out by adding a new class to your project called ParentDataSource.cs. I like to put all custom and user controls in a Controls sub-folder and thus namespace.

add a new class to your project

Make this new class inherit from the ObjectDataSource class and a ToolboxData attribute to define its tag name (you’ll need to add some using statements to import the appropriate namespaces too):

namespace RDN.CSSReadiDepth.Controls
{
    [ToolboxData("<{0}:ParentDataSource runat=server></{0}:ParentDataSource>")]
    public class ParentDataSource : ObjectDataSource
    {

Now add a couple of constructors. These do a few things. First they enable paging and sorting by default (you don’t need to do this but I use paging and sorting more often than not). Secondly they wire-up some events of the control to local handler methods:

public ParentDataSource()
    : this(String.Empty, String.Empty)
{ }

public ParentDataSource(string typeName, string selectMethod)
    : base(typeName, selectMethod)
{
    this.EnablePaging = true;
    this.SortParameterName = "sortExpression";

    this.Init += new EventHandler(ParentDataSource_Init);
    this.ObjectCreating += new ObjectDataSourceObjectEventHandler(ParentDataSource_ObjectCreating);
    this.ObjectDisposing += new ObjectDataSourceDisposingEventHandler(ParentDataSource_ObjectDisposing);
}

Now we need to add our event handler methods. They’re going to do the following:

  • Init handler: walk the control tree to find the page or user control the data source is being hosted on and then set the TypeName property to the type of the hosting page or user control;
  • ObjectCreating handler: set the instance of the object we want our data source control to bind to, which will be the page or user control it is hosted on; and
  • ObjectDisposing handler: cancel the default behaviour of the ObjectDataSource which is to dispose of the object it is bound to.
void ParentDataSource_Init(object sender, EventArgs e)
{
#if DEBUG
    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();
#endif
    FindParentHost(this);
#if DEBUG
    sw.Stop();
    System.Diagnostics.Debug.WriteLine(String.Format("Finding parent host for data source took {0}", sw.Elapsed));
#endif
}

void ParentDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
{
    e.ObjectInstance = _parentHost;
}

void ParentDataSource_ObjectDisposing(object sender, ObjectDataSourceDisposingEventArgs e)
{
    e.Cancel = true;
}

Note I’ve also included some timing code in the Init handler so you can measure the impact of this method (it’s negligible).

Finally we need to add some private members to support the handler methods:

Object _parentHost;

/// <summary>
/// Walks the control tree to find the hosting parent page or user control
/// </summary>
/// <param name="ctl">The control to start the tree walk at</param>
private void FindParentHost(Control ctl)
{
    if (ctl.Parent == null)
    {
        // User control was not found, use page base type instead
        this.TypeName = this.Page.GetType().BaseType.FullName;
        _parentHost = this.Page;
        return;
    }

    // Find the user control base type
    UserControl parentUC = ctl.Parent as UserControl;
    MasterPage parentMP = ctl.Parent as MasterPage;
    if (parentUC != null && parentMP == null)
    {
        Type parentBaseType = ctl.Parent.GetType().BaseType;
        this.TypeName = parentBaseType.FullName;
        _parentHost = ctl.Parent;
        return;
    }
    else
    {
        FindParentHost(ctl.Parent);
    }
}

The method FindParentHost, walks the control tree from the data source control until it finds the user control it is hosted on. If it can’t find a hosting user control it assumes the control is hosted on a page. Once it finds the host it sets the TypeName property accordingly (note that the MasterPage class actually inherits from UserControl so we explicitly exclude that case).

Now that we have our customised data source control it’s time to use it in anger! Add a declaration to the web.config to import the controls from the DLL containing the ParentDataSource control:

<pages styleSheetTheme="Theme1">
  <controls>
    <add tagPrefix="cc" namespace="RDN.CSSReadiDepth.Controls" assembly="RDN.CSSReadiDepth" />
  </controls>
</pages>

Now you can place the control in your page or user control mark-up file (.aspx or .ascx) and set the DataSourceID of your data-binding target control:

<asp:GridView ID="gvExample" runat="server" DataSourceID="pdsExample" DataKeyNames="Id"
    AllowPaging="true" AllowSorting="true" PageSize="10" AutoGenerateColumns="false"
    CssClass="customers-grid">
    <Columns>
        <asp:BoundField HeaderText="First Name" DataField="FirstName" SortExpression="FirstName"
            HeaderStyle-CssClass="first-name" ItemStyle-CssClass="first-name"
            AccessibleHeaderText="The customer's first name" />
        <asp:BoundField HeaderText="Last Name" DataField="LastName" SortExpression="LastName"
            HeaderStyle-CssClass="last-name" ItemStyle-CssClass="last-name"
            AccessibleHeaderText="The customer's last name" />
        <asp:BoundField HeaderText="Age" DataField="Age" SortExpression="Age"
            HeaderStyle-CssClass="age" ItemStyle-CssClass="age"
            AccessibleHeaderText="The age of the customer" />
        <asp:BoundField HeaderText="Member for" DataField="YearsAsMember" SortExpression="YearsAsMember"
            HeaderStyle-CssClass="years-as-member" ItemStyle-CssClass="years-as-member"
            AccessibleHeaderText="How many years the customer has been a member for" />
    </Columns>
</asp:GridView><cc:ParentDataSource ID="pdsExample" runat="server"
    SelectMethod="GetData" SelectCountMethod="GetDataRowCount" />

In this example I need to implement two methods on my page’s code-behind file: GetData and GetDataRowCount. The first gets the current page of data to display, the second gets the total number of records (this is needed so the data source controls can calculate paging information):

#region << Data Binding Methods >>
public IEnumerable<Customer> GetData(string sortExpression, int maximumRows, int startRowIndex)
{
    if (this.Customers == null || this.Customers.Count == 0)
        return this.Customers;

    var sortFunc = this.Customers[0].GetPropertySelector<Customer, object>(sortExpression.Replace(" DESC", ""));

    if (sortExpression.ToUpper().IndexOf(" DESC") >= 0)
    {
        return this.Customers
                // Sort descending
                .OrderByDescending(sortFunc)
                // Grab a single page of data
                .Skip((startRowIndex / maximumRows) * maximumRows)
                .Take(maximumRows);
    }
    else
    {
        return this.Customers
                // Sort ascending
                .OrderBy(sortFunc)
                // Grab a single page of data
                .Skip((startRowIndex / maximumRows) * maximumRows)
                .Take(maximumRows);
    }
}

public int GetDataRowCount()
{
    return (this.Customers != null ? this.Customers.Count : 0);
}

#endregion

In this example the data is stored in a property on the page class itself and I’m using LINQ to simulate sorting and paging. The property is just wrapping calls into the application’s session state. The data itself was loaded into session state when the application started up. In a real world example these calls might look up the data from a web-service or database, return data from cache, or in the MVP scenario first raise an event that the presenter class will handle and then return the value of a property on the page (who’s contents was set by the event handler in the presenter class). Also, because these methods are instance methods on the actual page object you are free to manipulate other controls or properties of the page class.

You can use this method equally well with any of the ASP.NET controls that support the data source controls, such as GridView, DetailsView, FormView and Repeater. It also works with two-way data-binding.

Download the sample solution used in this article here


ASP.NET & CSS: Using skins to “zero out” default style and set default CSS class names

ASP.NET includes a feature called “Themes” that allows you to organise styling artifacts including CSS stylesheets and images into logical units that can be easily applied to a page or entire site through configuration. Part of this feature is the ability to create .skin files that set default values for certain properties of ASP.NET server controls as part of a theme.

Not all properties of ASP.NET controls are skinnable. Indeed the initial intention of .skin files was to enable the setting of default values for properties such as FontColor and BorderWidth, which when serving to CSS supporting browsers ASP.NET will render as inline style attributes on the emitted HTML controls. So what place do they have when we apply all of our style rules via way of attached CSS stylesheets and selectors (the way we should)?

Despite their evil initial purpose we can put .skin files to good use for two purposes:

  1. Assigning CSS classes to all instances of particular ASP.NET server controls; and
  2. Zeroing out the default inline style that ASP.NET sets on some controls.

So how does this work?

In your theme folder add a new .skin file called Controls.skin:

Add a skin file to your theme folder

Now we want to add some declarations to the file to:

  1. Zero out the inline style of the ASP.NET validator controls; and
  2. Assign a default CSS class name to the Button, LinkButton, TextBox and validator controls.
<asp:RequiredFieldValidator runat="server" ForeColor="" CssClass="validation-error" />
<asp:RangeValidator runat="server" ForeColor="" CssClass="validation-error" />
<asp:CompareValidator runat="server" ForeColor="" CssClass="validation-error" />
<asp:RegularExpressionValidator runat="server" ForeColor="" CssClass="validation-error" />
<asp:CustomValidator runat="server" ForeColor="" CssClass="validation-error" />

<asp:LinkButton runat="server" CssClass="button" />
<asp:Button runat="server" CssClass="button" />
<asp:TextBox runat="server" CssClass="text-box" />

And that’s it! Now when ASP.NET renders these controls when they appear on a page (or in a site) with your theme configured, they’ll automatically pick up properties defined in this .skin file.