We all loathe writing validation for form elements, however no web developer would be worth their salt if they didn't incorporate at least server-side validation on forms. The problem arises when we're asked to provide client-side validation. With the ever increasing ubiquity of Javascript adoption due to the recent AJAX craze, I think it's safe to say that we'll see a resurgence of fat-clients built inside a browser.
The Purpose
As forms become more complex, there is greater incentive to minimize trips to the server. Furthermore, ASP.NET doesn't play nice with dynamically generated controls (something that occurs with any moderate AJAX development. The following is a way of utilizing the tools in the Microsoft ASP.NET framework without rewriting key components (for an example of this, look at the MonoRail project or the new Microsoft MVC framework).
In the Microsoft camp, usually you're encouraged to use validators that let act as decorators to existing ASP.NET textboxes and other form elements. To make matters more complicated, the Patterns and Practices group as well as the AJAX.NET group in Microsoft have competing components for dealing with client-side validation.
I propose a third method that employs a webservice and allows you to dynamically generate your validation through an asynchronous postback of your form to the server. I've been testing this approach and I've been pretty successful thus far:

On Using a Data Transfer Object (DTO)
A Data Transfer object is simply a container where you can store a collection of variables in an organized manner. A DTO doesn't have any behaviour, just public properties. For example, an Employee object might have the following properties:
Employee employee = EmployeeRepository.GetNewEmployee();
employee.HiredAt = DateTime.Now();
employee.Position = "Sales Manager";
employee.OfficeExtension = 4556;
To simplify things, I use Castleproject's ActiveRecord libraries to create a simple relationship between my database tables and my DTO's.
I've broken down the requests in terms of a linear timeline of events on the page.
Why Bother Mapping to a Data Transfer Object?
The Microsoft Enterprise Library enables you to decorate public properties (as well as methods) with validators. Furthermore, you create custom validators and include them in the same infrastructure that Microsoft provides for its own validators.
1) The page is rendered and delivered to the client's browser: for the most part, it's just a plain old aspx page. However, I've made certain minor modifications that help later on in the dynamic mapping of a DTO from the form back to the server:
A form element looks something like this:
<ASP:TextBox id="tbEmployeePosition" dto="Employee" property="Position" runat="server" />
In my form, I've provided two attributes, dto and property, which can be used on in the web service to dynamically map the form elements back to the DTO for validation.
2) Use a fake button to post the form asynchronously to a webservice in your project: all the latest javascript frameworks (my personal favourite is Prototype) support form serialization through utility of helper methods (see $F() in prototype). The JSON object you want to send to the webservice might look like this:
{ "EmployeeForm" : [ [ "id" : "aspnet_dynamic_form_tbEmployeePosition"] [ "value" : "Sales Manager"] [ "property" : "Position"] [ "dto" : "Employee"] ] [ [ "id" : "aspnet_dynamic_form_tbEmployeeOfficeExtension"] [ "value" : "4556"] [ "property" : "OfficeExtension"] [ "dto" : "Employee"] ] }
3) The Mapping Layer grabs a DTO from the Repository and maps the values using the Data from the JSON posted to the webservice: it's very easy to take this data and, using the PropertyInfo class and a bit of type checking, map these values to a DTO. Once their mapped, you can easily run Entlib Validation.
4) The ValidationManager validates the DTO, collects the EntLib Validation messages in a Hashtable: The Hashtable can have a key / value pair with the submitted form element's ID and the error message associated with the invalid element. Error messages are pulled from the MessageTemplate field in your validator's, whether you're adding attributes, or adding validation through your web.config.
5) The Hashtable is returned to the client: Microsoft's webservices don't support Hashtables natively, however you can get around this by creating an ArrayList containing string[] objects.
6) The values are inserted back onto the form: with a Hashtable containing element ID's and their respective messages, it becomes trivial to add these server-side messages to the client-side. In Prototype, it's a matter of Element.insert( responseJSON[i][0] , { "after" : "<span class='error'>"+responseJSON[i][1] + "</span>" }); where i corresponds with to the message in the list, 0 is the client's element ID and 1 is the message.
7) The Form is automatically submitted to the server if the form is valid: if responseJSON is empty, we simply fire the .click(); on the ASP.NET textbox as though the user did it himself. This means keeping track of the ASP.NET prefix which is added on all IDs that have the runat="server" directive.
The beauty of this approach is that it's highly extensible, you could serialize and post any part of your form if you needed to. Furthermore, the mapping that happens when the actual form controls are posted to the server would be nearly identical and the validation would be consolidated into one place. Lastly, you could provide the same error messages on server-side postback of the form (however it would be a little tricky to make them display as easily as it is with the web service.
I'll be providing a Zend sample of this in the week to come.