Elegant way to make CustomValidator work with ValidationSummary messagebox - asp.net

I have run into this problem before but never quite solved it. I have a form with several validators and also a CustomValidator.
<asp:Label ID="lblMemberNum" runat="server" Text="Membership #:" CssClass="LabelMedium" ></asp:Label>
<asp:TextBox ID="txtMemberNum" runat="server" CssClass="TextBox" ></asp:TextBox>
<asp:RequiredFieldValidator ID="rfvMemberNum" SetFocusOnError="True" runat="server"
ControlToValidate="txtMemberNum" ErrorMessage="[ Membership # ] is required"
CssClass="ValidationMessage" Display="Dynamic" >*</asp:RequiredFieldValidator>
<asp:RegularExpressionValidator ID="revMemberNum" Display="Dynamic" runat="server"
ControlToValidate="txtMemberNum" CssClass="ValidationMessage"
ErrorMessage="[ Membership # ] can only contain letters"
ValidationExpression="^([a-zA-Z\d]+)$" >*</asp:RegularExpressionValidator>
<asp:CustomValidator ID="cvMemberNum" runat="server"
CssClass="ValidationMessage" Display="Dynamic"
ControlToValidate="txtMemberNum" ValidateEmptyText="false"
OnServerValidate="cvMemberNum_Validate"
ErrorMessage="This membership number is already registered">*</asp:CustomValidator>
<asp:ValidationSummary ID="ValidationSummary1" runat="server"
CssClass="ValidationMessage"
ShowMessageBox="True" ShowSummary="False" />
and on the server side:
protected void cvMemberNum_Validate(object source, ServerValidateEventArgs args)
{
try
{
args.IsValid = (!CampaignRegistration.IsMemberRegistered(args.Value));
}
catch
{
args.IsValid = false;
}
}
My problem is: The ValidationSummary never shows the message from CustomValidator. This question has been asked in several places, but I havent seen a satisfactory answer.

Try using a ValidationGroup property across all your validators and the ValidationSummary.
EDIT: Another possibility could be the Server Validation Code
args.IsValid = (!CampaignRegistration.IsMemberRegistered(args.Value));
if CampaignRegistration.IsMemberRegistered(args.Value) is returning false, "!" is making it true and therefore making it valid. I think you should get rid of the "!" as follows:
args.IsValid = CampaignRegistration.IsMemberRegistered(args.Value);
UPDATE: In order for the ValidationSummary to display your custom validator message in a messagebox, you need to have ClientValidationFunction Code. If you need to display just the summary without a popup, this is not needed.
<asp:CustomValidator ID="cvMemberNum" runat="server"
CssClass="ValidationMessage" Display="Dynamic"
ControlToValidate="txtMemberNum" ValidateEmptyText="false"
OnServerValidate="cvMemberNum_Validate"
ClientValidationFunction = "ClientValidate"
ErrorMessage="This membership number is already registered">*</asp:CustomValidator>
//JavaScript Code.
function ClientValidate(source, args)
{
args.IsValid = false; //you need to add validation logic here
}
MORE: If you don't want to do ClientSide Validation, try this trick to show the alert. Make this change to your CustomValidator ServerValidate method:
protected void cvMemberNum_Validate(object source, ServerValidateEventArgs args)
{
bool isValid = true;
try
{
isValid = (!CampaignRegistration.IsMemberRegistered(args.Value));
}
catch
{
isValid = false;
}
args.IsValid = isValid;
if(!isValid)
{
if(!Page.IsClientScriptBlockRegistered("CustomValidation"))
Page.RegisterClientScriptBlock("CustomValidation", "<script>alert('This membership number is already registered');</script>");
}
}

The ShowMessageBox option is fully client-side, so it will only evaluate if you have set the ClientValidationFunction on the CustomValidator.
You can also fake it by registering a script that makes an alert, so when you get back from the server's validation, it'll prompt with the error message. This can either be registered in the ServerValidate method (per #Jose Basilio), or you can call the following method during the PreRender event to register a popup with all invalid validators on the page:
/// <summary>
/// Registers a script to display error messages from server-side validation as the specified <see cref="UserControl"/> or <see cref="Page"/> loads from a postback.
/// </summary>
/// <remarks>
/// Must be called in the PreRender if used to validate against the Text property of DNNTextEditor controls, otherwise Text will not be populated.
/// Must set the ErrorMessage manually if using a resourcekey, otherwise the resourcekey will not have overridden the ErrorMessage property.
/// </remarks>
/// <param name="ctrl">The <see cref="UserControl"/> or <see cref="Page"/> which is being posted back.</param>
/// <param name="validationGroup">The validation group against which to validate.</param>
public static void RegisterServerValidationMessageScript(TemplateControl ctrl, string validationGroup)
{
if (ctrl != null && ctrl.Page.IsPostBack)
{
ctrl.Page.Validate(validationGroup);
if (!ctrl.Page.IsValid)
{
StringBuilder errorMessage = new StringBuilder("<script language='javascript'>alert('");
for (int i = 0; i < ctrl.Page.Validators.Count; i++)
{
IValidator validator = ctrl.Page.Validators[i];
if (!validator.IsValid)
{
errorMessage.Append("- " + validator.ErrorMessage);
if (i < ctrl.Page.Validators.Count - 1)
{
errorMessage.Append(#"\r\n");
}
}
}
errorMessage.Append("');</script>");
ctrl.Page.ClientScript.RegisterStartupScript(typeof(IValidator), "validationAlert", errorMessage.ToString(), false);
}
}
}

I've recently had same problem. ValidationSummary was not showing the ErrorMessage from CustomValidator when ServerValidate stated validation failure. Since by default (as my little reverse engineering showed) validation summary is rendered client side on postback I've simply added a script that checks all validators on document load/async postback completion and triggers validation summary creation for failed validation groups:
$(document).ready(function () {
var displayAlert = function () {
if (typeof Page_Validators == 'undefined') return;
var groups = [];
for (i = 0; i < Page_Validators.length; i++)
if (!Page_Validators[i].isvalid) {
if (!groups[Page_Validators[i].validationGroup]) {
ValidationSummaryOnSubmit(Page_Validators[i].validationGroup);
groups[Page_Validators[i].validationGroup] = true;
}
}
};
displayAlert();
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(
function () {
displayAlert();
});
}
);
In my scenario I had nested user controls with validators, update panel and validation summary at the parent page.
More details here.

You should write a property
ValidationGroup="ValidationSummary1"
at every validator in your case.
Also check if your page has
AutoEventWireup="true"

bduke's RegisterServerValidationMessageScript is "faking it", but it isn't. It really fixes the problem. Every Utility namespace needs this function somewhere.

I have found a workaround for when javascript is disabled and the ValidationSummary does not show the errorMessage property of the CustomValidator. This is required as injecting script or alerts, as described above, will not work.
Add a new Validator control, let's call it CustomValidatorProxy, set its ControlToValidate property to any one of the controls on the form and EnableClientScript=false.
Within the ServerValidate event handler perform your custom validation and if validation fails set the IsValid property of the CustomValidator and CustomValidatorProxy to false and similarly set both ErrorMessage properties.
If the validation in the ServerValidate is passed ensure that the IsValid property of the CustomValidatorProxy is set to true.
Provided the CustomValidatorProxy is before the CustomValidator in the ValidatorCollection of the Page then the ServerValidate handler will override the IsValid property value that the CustomValidatorProxy would have returned having validated the ControlToValidate value and your ErrorMessage from your CustomValidator will be displayed in the ValidationSummary.

This worked for me:
<asp:CustomValidator runat="server" ID="cv"
ClientValidationFunction="ValidateFunction"
ErrorMessage="Default error
message">*</asp:CustomValidator>
<script type="text/javascript">
function ValidateFunction(sender, args)
{
var msg ='';
var formValid = true;
[various checks setting msg and formValid]
if (msg.length > 0) { sender.errormessage = msg; }
args.IsValid = formValid;
}
</script>

Related

Client-side validation for two separate conditions

Is there a way to validate asp.net text box for input using built-in validators for two distinct conditions? The text box is used in conjunction with a date picker and presently I have RegularExpressionValidator like this to validate:
<asp:TextBox ID="tbStartDate" runat="server" Width="100" CssClass="TextCompact">yyyy</asp:TextBox>
<asp:HyperLink ID="hlStartDate" runat="server" ImageUrl="~/Images/iconCalendar.gif"></asp:HyperLink><br />
<asp:RegularExpressionValidator ID="revStartDate" ControlToValidate="tbStartDate"
CssClass="TextCompact" runat="server" ErrorMessage="* Invalid Date" ValidationExpression="^(\d{4})$" />
which fires everytime an invalid date is entered - you can use the date picker or you can type in a date string.
So the issue is that I only want to validate if there is data in the text box. If the text box is empty I want it to pass through without validation.
Is this possible or would a custom client side validation be required?
Thanks,
Risho.
Update:
I've decided to write my own validator class derived from the BaseValidator class and wired it up but I get a compiler error.
namespace Abcdefg1234
{
public class Custom_Validator : BaseValidator
{
public Custom_Validator()
{
}
protected override bool EvaluateIsValid()
{
String value = this.GetControlValidationValue(this.ControlToValidate);
return ValidateControls(value.Trim());
}
public static bool ValidateControls(string value)
{
if(Regex.IsMatch(value, #"^(\d{4})$") == true)
return true;
else if(value == "")
return true;
else
return false;
}
}
}
Then I included the page directive tag:
<%# Register TagPrefix="Cv" Namespace="Abcdefg1234" TagName="CustomValidaitonX" %>
and the actual tag:
Cv:Custom_Validator ID="cvtbStartDate" ControlToValidate="tbStartDate" runat="server" ErrorMessage="* Invalid Date" />
I get a unknown server tag. Any ideas? Thanks.
The only built-in validator that fires on an empty text box would be the RequiredFieldValidator. If a textbox is empty, the other validators associated with it won't fire on form submission.
In other words, you should be fine just as you are, with the RegularExpressionValidator not firing if you submit with the textbox still empty. Are you experiencing different behavior?
ETA: You need to specify the Assembly name in your Register page directive for the Custom_Validator to be recognized. I don't believe the tag name is required for a custom control.

CustomValidator using CustomValidationScript

I have an ASP.NET TextBox with a CustomValidation control that invokes client side validation script.
<asp:TextBox ID="txtSubsContrRbtAmt" runat="server"
CssClass="textEntry NumericInput" Width="150px"
Text="" onKeyUp="SumValues();" MaxLength="16"></asp:TextBox>
<asp:CustomValidator ID="cvalSubsContrRbtAmt" runat="server" ClientValidationFunction="ValidatetxtSubsContrRbtAmt"
ControlToValidate="txtSubsContrRbtAmt" CssClass="errlable" ErrorMessage="Max Decimals = 7"
SetFocusOnError="True" ValidationGroup="CarbsAdd"></asp:CustomValidator>
Here's the Client script:
function ValidatetxtSubsContrRbtAmt(source, args) {
var txtSubsContrRbtAmt = document.getElementById("<%=txtSubsContrRbtAmt.ClientID%>");
var amount = txtSubsContrRbtAmt.value;
args.IsValid = ValidAmount(amount);
if (!args.IsValid)
txtSubsContrRbtAmt.focus();
}
function ValidAmount(amount) {
if (isNumber(amount)) {
return (RoundToXDecimalPlaces(amount, 7) == amount);
}
else {
return true;
}
In the ValidatetxtSubsContrRbtAmt function, the "source" parameter is the CustomValidator. That control has a property "ControlToValidate." If I can get to it, I can programmatically retrieve the value from that control and not have to have a separate function to validate each textbox.
jQuery is too much for me at this point, I'm looking for a plain old Javascript approach, please.
You don't have to get the text box. You can get the value from args.Value. The focus should be set automatically if you set SetFocusOnError="true".
function ValidatetxtSubsContrRbtAmt(source, args) {
var amount = args.Value;
args.IsValid = ValidAmount(amount);
}
You should be able to get to the control from the source object.
function ValidatetxtSubsContrRbtAmt(source, args) {
var controlToFocusOn = source.ControlToValidate;
you can switch that out with "document.getElementByID()" to get the ID or whatever attribute you need
var controlId = document.getElementById(source.ControlToValidate).id;
}
now you can focus or do what you need with the control. I had to access the the actual ControlToValidate earlier today from a CustomValidator.

asp.net - manually run client-side validation code from another event

I want to run whatever client-side validation routine is hooked up to a particular text input element.
The validation has been set up using CustomValidator:
<asp:textbox id="AddEstTime" runat="server" Width="55px"></asp:textbox><br />
<asp:CustomValidator ID="AddEstTimeCustomValidator" ClientValidationFunction="AddEstTimeCustomValidator_ClientValidate" OnServerValidate="AddEstTimeCustomValidator_ServerValidate" ErrorMessage="Please enter a time" ControlToValidate="AddEstTime" runat="server" Display="Dynamic" ValidateEmptyText="true"/>
<asp:CheckBox ID="AddIsTM" runat="server" Text="T&M" />
and the javascript:
function AddEstTimeCustomValidator_ClientValidate(sender, args) {
var checkbox = $("input[id$='IsTM']");
args.IsValid = checkbox.is(":checked") || args.Value.match(/^\d+$/);
}
When the CheckBox "AddIsTM" state changes, I want to revalidate the textbox "AddEstTime", using its hooked-up CustomValidator "AddEstTimeCustomValidator".
I am aware of focus -> add a character refocus -> remove character. I am trying to find a more correct way. New to asp.NET.
After looking through the Microsoft client-side code, I came up with this which seems to work:
// client-side validation of one user-control.
// pass in jquery object with the validation control
function ValidateOneElement(passedValidator) {
if (typeof (Page_Validators) == "undefined") {
return;
}
$.each(Page_Validators, function (index, value) {
if ($(value).attr("id") == passedValidator.attr("id")) {
ValidatorValidate(value, null, null);
}
});
}
This was after examining the Page_ClientValidate function:
function Page_ClientValidate(validationGroup) {
Page_InvalidControlToBeFocused = null;
if (typeof(Page_Validators) == "undefined") {
return true;
}
var i;
for (i = 0; i < Page_Validators.length; i++) {
ValidatorValidate(Page_Validators[i], validationGroup, null);
}
ValidatorUpdateIsValid();
ValidationSummaryOnSubmit(validationGroup);
Page_BlockSubmit = !Page_IsValid;
return Page_IsValid;
}
thx sennett (voted)
i just ran the simplest JS
Page_ClientValidate();
if you have a validation group then is
Page_ClientValidate("validationGroupName")
If you want stick with ASP.NET validators eventually you can abuse Validation Groups, but I think that this approach will give you nothing but trouble. Other option is to use jQuery on the client (nice list) only then you will have to duplicate validation on the server side, or to avoid that call server methods from client validations.

Required field validator issue for multiple controls

In my aspx page in a submission form i have following html.
<p><asp:CheckBox ID="chkWishToDonateFrmTrust " runat="server" onclick="chkWishToDonateFrmTrustHandle(this)" />
Wish to donate from following Trust for future transactions</p><p> </p><p><textarea name="textarea" cols="45" rows="5" class="txtTrustDetails" runat="server"
id="txtTrustDetails" ></textarea></p>
I need to have a required field validation control to validate txtTrustDetails text area only if chkWishToDonateFrmTrust is checked by the user without server post backs for this.only javascript library I am using is Microsoft Ajax Framework.I also have to include this to validation group in form with some other for controls.I already know as my knowledge one required field validator control can only validate single UI control(asp.net forum thread) does anyone in community dealt with this kind of issue in intuitive way?
As another answer has said, use a Custom Validator and do something like this (not tested so may not be quite right...):
<script type="text/javascript">
function validate(sender, args) {
var checkBox = document.getElementById('<%=CheckBox1.ClientID %>');
var textBox = document.getElementById('<%=TextBox1.ClientID %>');
if (checkBox.checked == 1) {
if (textBox.value == '') {
args.IsValid = false;
} else { args.IsValid = true; }
}
}
</script>
<asp:CheckBox ID="CheckBox1" runat="server" />
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:CustomValidator ID="CustomValidator1" ControlToValidate="TextBox1" runat="server"
ErrorMessage="CustomValidator" ClientValidationFunction="validate"></asp:CustomValidator>
Use the custom validator and use the clientValidationfunction to call a javascript function to check your values.
You should also have a servervalidate function incase javascript is off.
you can use a js function or a jquery function to do this is a sample code of how to achieve this
function chkValidate()
{
if($("#chkWishToDonateFrmTrust ").checked)
{
if($("#txtTrustDetails").val()=='')
{
args.IsValid = false;
//your custom message according to you
}
}
}
call this function on your sumbit button onclick;

How to set Page.IsValid in ASP.Net

When the page class property IsValid is read only, how can I set it using my own validation method?
So far all I've been able to do is set this property by calling Page.Validate().
How can I write my own functionality that will change the IsValid property just like Page.Validate()?
You don't set IsValid directly instead you call Validate() method of the Page object. If you have your custom validation methods then you need to use CustomValidator object and set that function in its server side validation property.
<asp:CustomValidator ID="YourValidator" runat="server" SetFocusOnError="true"
ControlToValidate="YourControl"
ClientValidationFunction="YOUR_JAVASCRIPT_FUNCTION"
OnServerValidate="YOUR_SERVER_VALIDATION_FUNCTION" Text="*" />
I know this is old, but, I needed to do something similar, basically forcing the IsValid property to false (don't ask why). Here is what I did basically (what you see here is my proof of concept):
Added this to the .aspx page:
<asp:TextBox ID="txtDummy" runat="server" Visible="false" />
<asp:RangeValidator ID="rvDummy" ControlToValidate="txtDummy" runat="server" MinimumValue="1" MaximumValue="2" />
And then I added this to the code behind:
bool makeMyPageInvalid = true;
if (makeMyPageInvalid)
txtDummy.Text = "0";
Page.Validate();
if (Page.IsValid)
ScriptManager.RegisterStartupScript(Page, Page.GetType(), "test", "alert('valid');", true);
else
ScriptManager.RegisterStartupScript(Page, Page.GetType(), "test", "alert('not valid');", true);
You can see that this only allows you to force the page validation to an invalid state. You can use any validator or reason to set this. Hope this helps someone!
The IsValid property is read-only because it is intended for use with server and client-side validators like the RequiredFieldValidator and RegularExpressionValidator. It's read-only because you can't force a page to be valid programmatically. "Valid" in this context means all the validators on the page evaluate to true.
If you feel like using some JavaScript you can do it in the client-side by modifying the variable Page_IsValid like this:
function pageLoad() {
Page_IsValid = false;
}
I use this just in case someone clicks the submit button w/o entering data. Then I can display an alert like this:
function valid() {
if (!Page_IsValid) {
alert("Some Questions Remain Unanswered and are Marked with a Red Asterisc. ( * )");
}
(at the beginning I thought 'who would submit a form w/o data' but sooner rather than later I realized it happens)
This is a really old question, but it came up in a search so I thought I'd add my answer to it. First, create an extension method in one of your helper classes.
public static IEnumerable<T> GetAllControlsOfType<T>(this Control parent) where T : Control
{
var result = new List<T>();
foreach (Control control in parent.Controls)
{
if (control is T)
{
result.Add((T)control);
}
if (control.HasControls())
{
result.AddRange(control.GetAllControlsOfType<T>());
}
}
return result;
}
Now in your code behind file, loop over every validator on the page that is not validating.
foreach (var validator in Page.GetAllControlsOfType<BaseValidator>().Where(w => !w.IsValid))
{
validator.IsValid = true;
}

Resources