| 
 | 1 | +---  | 
 | 2 | +title: Use Custom DataAnnotations Validator  | 
 | 3 | +description: Learn how to implement and integrate custom DataAnnotations validation with Telerik Blazor components such as Form, Grid, ValidationMessage, ValidationTooltip, and others.  | 
 | 4 | +type: how-to  | 
 | 5 | +page_title: How to Use Custom DataAnnotations Validator with Telerik UI for Blazor  | 
 | 6 | +slug: validation-kb-custom-dataannotations-validator  | 
 | 7 | +tags: telerik, blazor, validation, form, grid  | 
 | 8 | +ticketid: 1666005, 1665269, 1658101, 1560189, 1558247, 1543336  | 
 | 9 | +res_type: kb  | 
 | 10 | +---  | 
 | 11 | + | 
 | 12 | +## Environment  | 
 | 13 | + | 
 | 14 | +<table>  | 
 | 15 | +    <tbody>  | 
 | 16 | +        <tr>  | 
 | 17 | +            <td>Product</td>  | 
 | 18 | +            <td>  | 
 | 19 | +                UI for Blazor, <br />  | 
 | 20 | +                Grid for Blazor, <br />  | 
 | 21 | +                Form for Blazor, <br />  | 
 | 22 | +                ValidationMessage for Blazor, <br />  | 
 | 23 | +                ValidationSummary for Blazor, <br />  | 
 | 24 | +                ValidationTooltip for Blazor  | 
 | 25 | +            </td>  | 
 | 26 | +        </tr>  | 
 | 27 | +    </tbody>  | 
 | 28 | +</table>  | 
 | 29 | + | 
 | 30 | +## Description  | 
 | 31 | + | 
 | 32 | +This KB answers the following questions:  | 
 | 33 | + | 
 | 34 | +* How to use conditional required validation with Telerik UI for Blazor components?  | 
 | 35 | +* How to make a Form field required, depending on the value of another field?  | 
 | 36 | +* How to implement a conditional `DataAnnotations` validator and integrate it with the Telerik Blazor Form or Grid?  | 
 | 37 | +* How to display inline validation messages or validation tooltips when using a custom validator?  | 
 | 38 | + | 
 | 39 | +## Solution  | 
 | 40 | + | 
 | 41 | +1. Implement a class that inherits from [`ValidationAttribute`](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validationattribute).  | 
 | 42 | +1. Override the [`IsValid()` method overload](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validationattribute.isvalid), which accepts a [`ValidationContext`](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validationcontext) and returns a [`ValidationResult`](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validationresult).  | 
 | 43 | +1. Return a `ValidationResult` that includes the failing field name(s) as a second argument of type `IEnumerable<string>`. This step is crucial in order to apply invalid state to the respective input component and display an inline validation message next to it.  | 
 | 44 | +1. (optional) Override the `FormatErrorMessage` method to provide a custom validation message.  | 
 | 45 | + | 
 | 46 | +> Creating a custom `DataAnnotations` validator does not involve Telerik APIs and is outside the Telerik support scope. The following implementation is just an example that shows that Telerik Blazor components can work with a custom validator. The exact validator implementation <a href="https://stackoverflow.com/questions/26354853/conditionally-required-property-using-data-annotations" target="_blank">depends on the specific requirements and can vary</a>.  | 
 | 47 | +
  | 
 | 48 | +>caption Use custom conditional required DataAnnotations validator with Telerik components for Blazor  | 
 | 49 | +
  | 
 | 50 | +````CSHTML  | 
 | 51 | +@using System.ComponentModel.DataAnnotations  | 
 | 52 | +
  | 
 | 53 | +@using System.Reflection  | 
 | 54 | +
  | 
 | 55 | +<h1>Conditional Validation</h1>  | 
 | 56 | +
  | 
 | 57 | +<p><code>ShippingAddress</code> is required when <code>UseBillingAddressForShipping</code> is <code>false</code>.</p>  | 
 | 58 | +
  | 
 | 59 | +<h2>Form</h2>  | 
 | 60 | +
  | 
 | 61 | +<TelerikForm Model="@FormModel">  | 
 | 62 | +    <FormValidation>  | 
 | 63 | +        <DataAnnotationsValidator></DataAnnotationsValidator>  | 
 | 64 | +        <TelerikValidationSummary />  | 
 | 65 | +    </FormValidation>  | 
 | 66 | +    <FormItems>  | 
 | 67 | +        <FormItem Field="@nameof(OrderDelivery.BillingAddress)" LabelText="Billing Address"></FormItem>  | 
 | 68 | +        <FormItem Field="@nameof(OrderDelivery.UseBillingAddressForShipping)" LabelText="Use Billing Address for Shipping"></FormItem>  | 
 | 69 | +        <FormItem Field="@nameof(OrderDelivery.ShippingAddress)" LabelText="Shipping Address"></FormItem>  | 
 | 70 | +    </FormItems>  | 
 | 71 | +</TelerikForm>  | 
 | 72 | +
  | 
 | 73 | +<h2>Grid</h2>  | 
 | 74 | +
  | 
 | 75 | +<TelerikGrid Data="@GridData"  | 
 | 76 | +             EditMode="@GridEditMode.Inline"  | 
 | 77 | +             OnUpdate="@OnGridUpdate"  | 
 | 78 | +             OnCreate="@OnGridCreate">  | 
 | 79 | +    <GridToolBarTemplate>  | 
 | 80 | +        <GridCommandButton Command="Add">Add Item</GridCommandButton>  | 
 | 81 | +    </GridToolBarTemplate>  | 
 | 82 | +    <GridColumns>  | 
 | 83 | +        <GridColumn Field="@nameof(OrderDelivery.BillingAddress)" Title="Billing Address" />  | 
 | 84 | +        <GridColumn Field="@nameof(OrderDelivery.UseBillingAddressForShipping)" Title="Use Billing Address for Shipping" />  | 
 | 85 | +        <GridColumn Field="@nameof(OrderDelivery.ShippingAddress)" Title="Shipping Address" />  | 
 | 86 | +        <GridCommandColumn>  | 
 | 87 | +            <GridCommandButton Command="Edit">Edit</GridCommandButton>  | 
 | 88 | +            <GridCommandButton Command="Save" ShowInEdit="true">Save</GridCommandButton>  | 
 | 89 | +            <GridCommandButton Command="Cancel" ShowInEdit="true">Cancel</GridCommandButton>  | 
 | 90 | +        </GridCommandColumn>  | 
 | 91 | +    </GridColumns>  | 
 | 92 | +</TelerikGrid>  | 
 | 93 | +
  | 
 | 94 | +<style>  | 
 | 95 | +    h1 {  | 
 | 96 | +        font-size: 1.5rem;  | 
 | 97 | +    }  | 
 | 98 | +
  | 
 | 99 | +    h2 {  | 
 | 100 | +        font-size: 1.2rem;  | 
 | 101 | +    }  | 
 | 102 | +</style>  | 
 | 103 | +
  | 
 | 104 | +@code {  | 
 | 105 | +    private OrderDelivery FormModel { get; set; } = new() { Id = 1 };  | 
 | 106 | +
  | 
 | 107 | +    #region Grid  | 
 | 108 | +
  | 
 | 109 | +    private List<OrderDelivery> GridData { get; set; } = new();  | 
 | 110 | +
  | 
 | 111 | +    private int LastId { get; set; }  | 
 | 112 | +
  | 
 | 113 | +    private void OnGridCreate(GridCommandEventArgs args)  | 
 | 114 | +    {  | 
 | 115 | +        var createdItem = (OrderDelivery)args.Item;  | 
 | 116 | +
  | 
 | 117 | +        createdItem.Id = ++LastId;  | 
 | 118 | +
  | 
 | 119 | +        GridData.Insert(0, createdItem);  | 
 | 120 | +    }  | 
 | 121 | +
  | 
 | 122 | +    private void OnGridUpdate(GridCommandEventArgs args)  | 
 | 123 | +    {  | 
 | 124 | +        var updatedItem = (OrderDelivery)args.Item;  | 
 | 125 | +        var originalItemIndex = GridData.FindIndex(i => i.Id == updatedItem.Id);  | 
 | 126 | +
  | 
 | 127 | +        if (originalItemIndex != -1)  | 
 | 128 | +        {  | 
 | 129 | +            GridData[originalItemIndex] = updatedItem;  | 
 | 130 | +        }  | 
 | 131 | +    }  | 
 | 132 | +
  | 
 | 133 | +    #endregion Grid  | 
 | 134 | +
  | 
 | 135 | +    #region Model  | 
 | 136 | +
  | 
 | 137 | +    public class OrderDelivery  | 
 | 138 | +    {  | 
 | 139 | +        public int Id { get; set; }  | 
 | 140 | +
  | 
 | 141 | +        [Required]  | 
 | 142 | +        [Display(Name = "Billing Address")]  | 
 | 143 | +        public string BillingAddress { get; set; } = string.Empty;  | 
 | 144 | +
  | 
 | 145 | +        [Display(Name = "Use Billing Address For Shipping")]  | 
 | 146 | +        public bool UseBillingAddressForShipping { get; set; }  | 
 | 147 | +
  | 
 | 148 | +        [ConditionalRequired(nameof(OrderDelivery.UseBillingAddressForShipping), false)]  | 
 | 149 | +        [Display(Name = "Shipping Address")]  | 
 | 150 | +        public string ShippingAddress { get; set; } = string.Empty;  | 
 | 151 | +    }  | 
 | 152 | +
  | 
 | 153 | +    #endregion Model  | 
 | 154 | +
  | 
 | 155 | +    #region Custom Validator  | 
 | 156 | +
  | 
 | 157 | +    public class ConditionalRequired : ValidationAttribute  | 
 | 158 | +    {  | 
 | 159 | +        private string DependentPropertyName { get; set; }  | 
 | 160 | +        private string DependentPropertyDisplayName { get; set; } = string.Empty;  | 
 | 161 | +        private object? DependentPropertyExpectedValue { get; set; }  | 
 | 162 | +        private object? DependentPropertyValue { get; set; }  | 
 | 163 | +
  | 
 | 164 | +        public override bool RequiresValidationContext  | 
 | 165 | +        {  | 
 | 166 | +            get { return true; }  | 
 | 167 | +        }  | 
 | 168 | +
  | 
 | 169 | +        public ConditionalRequired(string dependentPropertyName, object dependentPropertyExpectedValue)  | 
 | 170 | +        : base("The {0} field is required when {1} is equal to {2}.")  | 
 | 171 | +        {  | 
 | 172 | +            DependentPropertyName = dependentPropertyName;  | 
 | 173 | +            DependentPropertyExpectedValue = dependentPropertyExpectedValue;  | 
 | 174 | +        }  | 
 | 175 | +
  | 
 | 176 | +        public override string FormatErrorMessage(string requiredPropertyName)  | 
 | 177 | +        {  | 
 | 178 | +            return string.Format(  | 
 | 179 | +                System.Globalization.CultureInfo.CurrentCulture,  | 
 | 180 | +                base.ErrorMessageString,  | 
 | 181 | +                requiredPropertyName,  | 
 | 182 | +                DependentPropertyDisplayName,  | 
 | 183 | +                DependentPropertyValue);  | 
 | 184 | +        }  | 
 | 185 | +
  | 
 | 186 | +        protected override ValidationResult IsValid(object? validatedValue, ValidationContext validationContext)  | 
 | 187 | +        {  | 
 | 188 | +            if (validationContext == null)  | 
 | 189 | +            {  | 
 | 190 | +                throw new ArgumentNullException("validationContext");  | 
 | 191 | +            }  | 
 | 192 | +
  | 
 | 193 | +            PropertyInfo? dependentProperty = validationContext.ObjectType.GetProperty(DependentPropertyName);  | 
 | 194 | +            DependentPropertyValue = dependentProperty?.GetValue(validationContext.ObjectInstance);  | 
 | 195 | +            DependentPropertyDisplayName = dependentProperty?.GetCustomAttribute<DisplayAttribute>()?.Name ?? DependentPropertyName;  | 
 | 196 | +
  | 
 | 197 | +            if ((DependentPropertyValue == null && DependentPropertyExpectedValue == null)  | 
 | 198 | +                || (DependentPropertyValue != null && DependentPropertyValue.Equals(DependentPropertyExpectedValue))  | 
 | 199 | +                && string.IsNullOrEmpty(validatedValue?.ToString()))  | 
 | 200 | +            {  | 
 | 201 | +                return new ValidationResult(FormatErrorMessage(validationContext.DisplayName), new List<string> { validationContext.DisplayName });  | 
 | 202 | +            }  | 
 | 203 | +
  | 
 | 204 | +            return ValidationResult.Success!;  | 
 | 205 | +        }  | 
 | 206 | +    }  | 
 | 207 | +
  | 
 | 208 | +    #endregion Custom Validator  | 
 | 209 | +}  | 
 | 210 | +````  | 
 | 211 | + | 
 | 212 | +@[template](/_contentTemplates/common/form-validation.md#note-telerik-role-in-validation)  | 
 | 213 | + | 
 | 214 | +## See Also  | 
 | 215 | + | 
 | 216 | +* [Conditional Form Validation Options]({%slug form-kb-conditional-validation%})  | 
 | 217 | +* [Form Validation]({%slug form-validation%})  | 
 | 218 | +* [Grid Validation]({%slug grid-editing-validation%})  | 
 | 219 | +* [Validation Tools Overview]({%slug validation-tools-overview%})  | 
0 commit comments