Monday, September 6, 2010

ValidationSummary and displaying error message for CustomValidator on ServerValidate

Validation in ASP.NET WebForms is a rather solid block. Various validator controls can be easily fitted into page validation pipeline with absolutely no pain. One of the controls that perfectly fits ASP.NET validation system is ValidationSummary. It's aim is to collect error messages for all validators of a specific validation group when they signalize there's an error in user input.

The standard control works perfectly when dealing with client side validation. But it has a significant bug when doing server side validation with CustomValidator: no messages are displayed for failed controls.

On the picture below you may see a screen shot of a Wizard page that shows user input errors being validated client side:


In this sample mandatory fields can be easily validated client side. But what if you need to do server request to check something against DB? In this sample I'd like to check whether there're prices persisted to DB for all sites when a checkbox 'Same prices for all sites' is checked. The easiest way is to use the standard CustomValidator control and add necessary checks to CustomValidator.ServerValidate event (the control also perfectly works with async postbacks and UpdatePanels):


protected void sitesCustomValidator_ServerValidate(object sender, ServerValidateEventArgs e)
{
if (samePriceCheckBox.Enabled)
{
e.IsValid = ..; // validation result
}
}

But even though when custom server validation fails (there're no prices for all sites) in code behind the Page.Valid property is 'false' and there's an opportunity to block next wizard step - validation summary doesn't show the error message for the CustomValidator failed:


The problem here is that ValidationSummary is only updated on postback to server. In the case when server side validation failed validators and their results are rendered to the response and NO ValidationSummary update occurs.

To override this drawback the following script can be used (jQuery is required):

$(document).ready(function () {
var displayAlert = function () {
if (typeof Page_Validators == 'undefined') return;

var groups = [];

for (i = 0; i < Page_Validators.length; i++){
var validationGroup = typeof Page_Validators[i].validationGroup == "undefined" ? "": Page_Validators[i].validationGroup;
if (!Page_Validators[i].isvalid) {
if (!groups[validationGroup]) {
ValidationSummaryOnSubmit(validationGroup);
groups[validationGroup] = true;
}
}
}
};

displayAlert();

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(function () {
displayAlert();
});
});

What it does is checking all validation controls on page load or async postback completion and triggering ValidationSummary updates for those validation groups that have failed validators.
The script reference can be added at a master page:


<body>
<form runat="server">
<asp:ScriptManager ID="scriptManager" runat="server" EnableScriptLocalization="true"
EnableScriptGlobalization="true" >
<Scripts>
<asp:ScriptReference Path="~/Scripts/jquery-1.4.2.min.js" />
<asp:ScriptReference Path="~/Scripts/validationsummary.fix.js" />
</Scripts>
</asp:ScriptManager>
...

NOTE: I've tested this solution only for a case of a single custom validator with a server side validation event. ValidationSummary was placed at the bottom of a content page. Validation controls resided in a child user control of the page.