3

Iam trying to validate textbox based on checkbox value. please view my model class and IsValid override method.

public class Product
{
    //Below property value(HaveExperiance)
    [MustBeProductEntered(HaveExperiance)] 
    public string ProductName { get; set; }

    public bool HaveExperiance { get; set; }
}

public class MustBeTrueAttribute : ValidationAttribute
{
    //Here i need the value of HaveExperiance property which 
    //i passed from  [MustBeProductEntered(HaveExperiance)]  in product class above.
    public override bool IsValid(object value)
    {
        return value is bool && (bool)value;
    }
}

You can see above in ProductName property in product class where iam trying to pass the HaveExperiance class property value, If it checked then user must have to fill the ProductName textbox.

So my orignal question is that how can i validate the ProductName textbox based on the HaveExperiance value, thanks in advance.

EDIT:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;

namespace Mvc.Affiliates.Models
{
    public class MyProducts
    {
        [Key]
        [Required(ErrorMessage = "Please insert product id.")]
        public string ProductId { get; set; }

        [RequiredIf("HaveExperiance")]
        public string ProductName { get; set; }

        public bool HaveExperiance { get; set; }
        public List<MyProducts> prolist { get; set; }
    }

    public class RequiredIfAttribute : ValidationAttribute
    {
        private RequiredAttribute _innerAttribute = new RequiredAttribute();

        public string Property { get; set; }

        public object Value { get; set; }

        public RequiredIfAttribute(string typeProperty)
        {
            Property = typeProperty;
        }

        public RequiredIfAttribute(string typeProperty, object value)
        {
            Property = typeProperty;
            Value = value;
        }

        public override bool IsValid(object value)
        {
            return _innerAttribute.IsValid(value);
        }
    }

    public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
    {
        public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute) : base(metadata, context, attribute) { }

        public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
        {
            return base.GetClientValidationRules();
        }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            PropertyInfo field = Metadata.ContainerType.GetProperty(Attribute.Property);

            if (field != null)
            {
                var value = field.GetValue(container, null);

                if ((value != null && Attribute.Value == null) || (value != null && value.Equals(Attribute.Value)))
                {
                    if (!Attribute.IsValid(Metadata.Model))
                    {
                        yield return new ModelValidationResult { Message = ErrorMessage };
                    }
                }
            }
        }
    }

Controller

public class HomeController : Controller
    {
        //
        // GET: /Home/

        MvcDbContext _db = new MvcDbContext();
        public ActionResult Index()
        {
          return View();
        }

        [HttpPost]
        public ActionResult Index(MyProducts model)
        {
            string ProductId = model.ProductId;
            string ProductName = model.ProductName;
            //bool remember = model.HaveExperiance;
            return View();
        }
    }

View

@model   Mvc.Affiliates.Models.MyProducts
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Details</h2>
<br />

<div style="height:200px; width:100%">
  @using  (Html.BeginForm("Index","Home", FormMethod.Post))
    {

   <h2>Details</h2>

        @Html.LabelFor(model => model.ProductId)
        @Html.TextBoxFor(model => model.ProductId)

        @Html.LabelFor(model => model.ProductName)
        @Html.EditorFor(model => model.ProductName)
        @Html.ValidationMessageFor(model => model.ProductName, "*")

        @Html.CheckBoxFor(model => model.HaveExperiance)
        @Html.ValidationMessageFor(model => model.HaveExperiance, "*")
       <input type="submit" value="Submit" />
  }

</div>

So far i have tried the above code, Actually i needed when i click on checkbox then it should start validate my ProductName textbox, if uncheck then not. iam missing a little thing in above code, please help and rectify me.

1
  • 2
    Suggest you use a foolproof [RequiredIfTrue] or similar validation attribute. But if you want to write your own, then this article is a good starting point Commented Jan 27, 2016 at 7:27

1 Answer 1

5

This is a complete example of how to create a custom validation attribute based on another attribute:

public class RequiredIfAttribute : ValidationAttribute
{
    private RequiredAttribute _innerAttribute = new RequiredAttribute();

    public string Property { get; set; }

    public object Value { get; set; }

    public RequiredIfAttribute(string typeProperty) {
        Property = typeProperty;
    }

    public RequiredIfAttribute(string typeProperty, object value)
    {
        Property = typeProperty;
        Value = value;
    }

    public override bool IsValid(object value)
    {
        return _innerAttribute.IsValid(value);
    }
}

public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
{
    public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute) : base(metadata, context, attribute) { }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        return base.GetClientValidationRules();
    }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        PropertyInfo field = Metadata.ContainerType.GetProperty(Attribute.Property);

        if (field != null) {
            var value = field.GetValue(container, null);

            if ((value != null && Attribute.Value == null) || (value != null && value.Equals(Attribute.Value))) {
                if (!Attribute.IsValid(Metadata.Model)) {
                    yield return new ModelValidationResult { Message = ErrorMessage };
                }
            }
        }
    }
}

In Global.asax file in Application_Start add this part:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequiredIfValidator));

This part is needed to registate the RequiredIfValidator otherwise MVC will use only RequiredIfAttribute ignoring RequiredIfValidator.

If you want to validate ProductName if HaveExperiance have a value:

[RequiredIf("HaveExperiance")]
public string ProductName { get; set; }

If you want to validate ProductName only if HaveExperiance is false:

[RequiredIf("HaveExperiance", false)]
public string ProductName { get; set; }

If you want to validate ProductName only if HaveExperiance is true:

[RequiredIf("HaveExperiance", true)]
public string ProductName { get; set; }

In RequiredIfAttribute class I created a RequiredAttribute object only to validate the value passed to IsValid method.

Property field is used to save the name of property that activate the validation. It is used in class RequiredIfValidator to get the field current value using reflection (field.GetValue(container, null)).

When you call validation in your code (for example when you do if(TryValidateModel(model))) first you call RequiredIfValidator class that then call RequiredIfAttribute (through Attribute.IsValid(Metadata.Model)) class if some conditions are valid ((value != null && Attribute.Value == null) || (value != null && value.Equals(Attribute.Value))).

One last thing. Because RequiredIfAttribute inherits from ValidationAttribute you can also use error messages in the same way of any other validation attribute.

[RequiredIf("HaveExperiance", true, ErrorMessage = "The error message")]
public string ProductName { get; set; }

[RequiredIf("HaveExperiance", true, ErrorMessageResourceName = "ResourceName", ErrorMessageResourceType = typeof(YourResourceType))]
public string ProductName { get; set; }
Sign up to request clarification or add additional context in comments.

14 Comments

would be great if you add some explanation as well
@ArijitMukherjee I hope it's better now :-)
great, yes that would be helpfull to everyone now
Thanks for the reply, your example look cool, will run and respond today, thanks for detail explanation.
@erikscandola I have tried your code. Please see EDIT: code in my orignal question, please help to rectify me what iam missing?, thanks.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.