ASP.NET Multiple Validation Group Button
In ASP.NET validation controls, the Validation Group allows to break down the set of page validation controls into different units that can be executed independently from each other. Then, a single validation group can be specified at button level so a particular action is glued to a validation controls subset. There are some situations though, where you might be interested in binding a button to multiple validation groups, one of these situations is described in this feedback entry at Microsoft Connect site.
That was back in 2005 when the Validation Group concept was a brand new upcoming feature in .NET 2.0, which by that time was still in beta version. Well, despite the fact that guys from Microsoft seemed to be receptive to the suggestion, it's 2009, ASP.NET is in it 3.5 version and we all know that this feature hasn't quite made it :(. The workaround suggested is to call Page.Validate(group) in server-side but then it means that we'd need to ditch client-side validation which I'm not very happy about.
So let's pick up this suggestion and create a new asp.net button control that includes a coma delimited validation group list property.
public class MultipleValidationGroupButton: Button
{ /// <summary> /// Gets or sets the validation group list /// comma delimited. /// </summary> /// <value>The validation group list.</value> /// <remarks> /// Empty string represents validation controls that /// don't define validation group property /// </remarks>public string ValidationGroupList
{ get
{ return ViewState["ValidationGroupList"] as string;
}
set
{ ViewState["ValidationGroupList"] = value;
}
}
protected override void OnPreRender(EventArgs e)
{if (!String.IsNullOrEmpty(this.ValidationGroupList))
{this.CausesValidation = false;
string script = String.Format("ValidateGroups('{0}','{1}');",
this.ClientID, this.ValidationGroupList);
if (Page.IsAsyncPostBack()) {ScriptManager.RegisterStartupScript(Page, typeof(Page), this.ClientID, script, true);
}
else {Page.ClientScript.RegisterStartupScript(typeof(Page), this.ClientID, script, true);
}
}
base.OnPreRender(e);}
/// <summary> /// Validates the groups. /// </summary>public void ValidateGroups()
{if (!String.IsNullOrEmpty(this.ValidationGroupList))
{string[] list = this.ValidationGroupList.Split(',');
foreach (string item in list)
{Page.Validate(item);
}
}
}
}
The server side implementation is pretty straightforward: we check if the ValidationGroupList propety has been set and if so we disable the regular ASP.NET validation mechanism (CausesValidation=false) and we register the call to the script that will actually do the job (For the client-side registration I use the Page.IsAsyncPostBack partial method already implemented while creating my jQuery ConfirmationButton). The purpose of ValidateGroups method is to remove the need of hardcoding the validation groups in code behind to enforce server side validation, so instead of doing this:
protected void Button_Click(object sender, EventArgs e)
{ Page.Validate("Group1"); Page.Validate("Group2"); Page.Validate("Group3"); if (Page.IsValid) { //Button Actions}
}
We don’t need to scatter our validation groups in both markup and code-behind because we can do this:
protected void Button_Click(object sender, EventArgs e)
{ this.Button.ValidateGroups(); if (Page.IsValid) { //Button Actions}
}
And to end with, server-side wise, this is how we define our button in ASP.NET markup language.
<cc:MultipleValidationGroupButton ID="btn" runat="server"
ValidationGroupList="Group1,Group2" onclick="Button_Click" Text="Click" />
Now it's time to code the javascript function that performs validation on button click. For the implementation, I've used some of the syntactic sugar that jQuery offers but as I'm not using a great deal of things of it you could potentially rewrite this function with plain javascript without much complexity, although I must say that after you start using jQuery (if you haven't yet), you will never want to go back to plain javascript again!
function ValidateGroups(buttonId, validationGroupList) { //Bind the logic to the specified button with jQuery$("#" + buttonId).click(function(e) {
var list = validationGroupList.split(',');
//Loop through the entire set of page validators $.each(Page_Validators, function() {if ((this.validationGroup && ExistsGroup(list, this.validationGroup))
||(!this.validationGroup && ExistsGroup(list, ''))) {
ValidatorValidate(this, this.validationGroup);
Page_IsValid = Page_IsValid && this.isvalid;}
});
//Reflect validation in ValidationSummary's if there are any in the page $.each(list, function() { ValidationSummaryOnSubmit(this);});
//If Page_IsValid is false the execution flow will be interrupted return Page_IsValid;});
}
function ExistsGroup(list, group) {var found = false;
for (i = 0; i < list.length; i++) { if (list[i] == group) { found = true; break;}
}
return found;}
And that’s it, the implementation is ready but before we wrap it up, there are a few things that are worth mentioning: The second clause in the if statement is there to support the scenario where we would potentially want to combine “ungrouped” with “grouped” validation controls and in that situation we’d define our control like this:
<cc:MultipleValidationGroupButton ID="btn" runat="server"
ValidationGroupList=",Group1" onclick="Button_Click" Text="Click" />
Finally, you may have noticed that I've used extensively the ASP.NET validation client-side API (ValidatorValidate, Page_Validators, etc), if you want to have more details about this API I recommend you using our friend Reflector to get acquainted to this library (System.Web assembly, Resources folder, WebUIValidation.js resource), that helped me a lot.
Hope this helps!
UPDATE: I've uploaded the code for this post here.
I've created a library project with ASP.NET custom controls for Button, ImageButton and LinkButton; the associated javascript code is embedded in that project (apart from jQuery js file).
