.
, , .
, ( ), , , :
public class MyViewModel
{
[DisplayName("Date of birth:")]
[TrippleDDLDateTime(ErrorMessage = "Please select a valid DOB")]
[Required(ErrorMessage = "Please select your DOB")]
[MinAge(18, ErrorMessage = "You must be at least 18 years old")]
public DateTime? Dob { get; set; }
}
:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
return Content(
string.Format(
"Thank you for selecting your DOB: {0:yyyy-MM-dd}",
model.Dob
)
);
}
}
(~/Views/Home/Index.cshtml):
@model MyViewModel
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.Dob)
<button type="submit">OK</button>
}
, 3 DateTime (~/Views/Shared/EditorTemplates/TrippleDDLDateTime.cshtml):
@{
var now = DateTime.Now;
var years = Enumerable.Range(0, 150).Select(x => new SelectListItem { Value = (now.Year - x).ToString(), Text = (now.Year - x).ToString() });
var months = Enumerable.Range(1, 12).Select(x => new SelectListItem { Value = x.ToString("00"), Text = x.ToString() });
var days = Enumerable.Range(1, 31).Select(x => new SelectListItem { Value = x.ToString("00"), Text = x.ToString() });
var result = ViewData.ModelState[ViewData.TemplateInfo.HtmlFieldPrefix];
if (result != null)
{
var values = result.Value.RawValue as string[];
years = new SelectList(years, "Value", "Text", values[0]);
months = new SelectList(months, "Value", "Text", values[1]);
days = new SelectList(days, "Value", "Text", values[2]);
result.Value = null;
}
}
<div class="trippleddldatetime">
@Html.Label("")
@Html.DropDownList("", years, "-- year --")
@Html.DropDownList("", months, "-- month --")
@Html.DropDownList("", days, "-- day --")
@Html.ValidationMessage("")
</div>
, [TrippleDDLDateTime]:
public class TrippleDDLDateTimeAttribute : ValidationAttribute, IMetadataAware
{
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.TemplateHint = "TrippleDDLDateTime";
}
public override bool IsValid(object value)
{
return true;
}
}
, IMetadataAware, , (TrippleDDLDateTime.cshtml).
[MinAge]:
public class MinAgeAttribute : ValidationAttribute
{
private readonly int _minAge;
public MinAgeAttribute(int minAge)
{
_minAge = minAge;
}
public override bool IsValid(object value)
{
if (value == null)
{
return true;
}
DateTime date = Convert.ToDateTime(value);
long ticks = DateTime.Now.Ticks - date.Ticks;
int years = new DateTime(ticks).Year;
return years >= _minAge;
}
}
, , [TrippleDDLDateTime], :
public class TrippleDDLDateTimeModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var metadata = bindingContext.ModelMetadata;
var trippleDdl = metadata.ContainerType.GetProperty(metadata.PropertyName).GetCustomAttributes(typeof(TrippleDDLDateTimeAttribute), true).FirstOrDefault() as TrippleDDLDateTimeAttribute;
if (trippleDdl == null)
{
return base.BindModel(controllerContext, bindingContext);
}
var prefix = bindingContext.ModelName;
var value = bindingContext.ValueProvider.GetValue(prefix);
var parts = value.RawValue as string[];
if (parts.All(string.IsNullOrEmpty))
{
return null;
}
bindingContext.ModelState.SetModelValue(prefix, value);
var dateStr = string.Format("{0}-{1}-{2}", parts[0], parts[1], parts[2]);
DateTime date;
if (DateTime.TryParseExact(dateStr, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
{
return date;
}
bindingContext.ModelState.AddModelError(prefix, trippleDdl.ErrorMessage);
return null;
}
}
, , . , DateTime, ddl. DateTime? Application_Start:
ModelBinders.Binders.Add(typeof(DateTime?), new TrippleDDLDateTimeModelBinder());
, , . , . , - .
, , , . , .
, , 2 IClientValidatable, .
[TrippleDDLDateTime]:
public class TrippleDDLDateTimeAttribute : ValidationAttribute, IMetadataAware, IClientValidatable
{
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.TemplateHint = "TrippleDDLDateTime";
}
public override bool IsValid(object value)
{
return true;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = ErrorMessage;
rule.ValidationType = "trippleddldate";
yield return rule;
}
}
[MinAge]:
public class MinAgeAttribute : ValidationAttribute, IClientValidatable
{
private readonly int _minAge;
public MinAgeAttribute(int minAge)
{
_minAge = minAge;
}
public override bool IsValid(object value)
{
if (value == null)
{
return true;
}
DateTime date = Convert.ToDateTime(value);
long ticks = DateTime.Now.Ticks - date.Ticks;
int years = new DateTime(ticks).Year;
return years >= _minAge;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = ErrorMessage;
rule.ValidationType = "minage";
rule.ValidationParameters["min"] = _minAge;
yield return rule;
}
}
, GetClientValidationRules . .
javascript, . , trippleddlAdapters.js:
(function ($) {
$.fn.getDateFromTrippleDdls = function () {
var year = this.find('select:nth(0)').val();
var month = this.find('select:nth(1)').val();
var day = this.find('select:nth(2)').val();
if (year == '' || month == '' || day == '') {
return NaN;
}
var y = parseInt(year, 10);
var m = parseInt(month, 10);
var d = parseInt(day, 10);
var date = new Date(y, m - 1, d);
var isValidDate = date.getFullYear() == y && date.getMonth() + 1 == m && date.getDate() == d;
if (isValidDate) {
return date;
}
return NaN;
};
$.validator.unobtrusive.adapters.add('trippleddldate', [], function (options) {
options.rules['trippleddldate'] = options.params;
if (options.message) {
options.messages['trippleddldate'] = options.message;
}
});
$.validator.addMethod('trippleddldate', function (value, element, params) {
var parent = $(element).closest('.trippleddldatetime');
var date = parent.getDateFromTrippleDdls();
console.log(date);
return !isNaN(date);
}, '');
$.validator.unobtrusive.adapters.add('minage', ['min'], function (options) {
options.rules['minage'] = options.params;
if (options.message) {
options.messages['minage'] = options.message;
}
});
$.validator.addMethod('minage', function (value, element, params) {
var parent = $(element).closest('.trippleddldatetime');
var birthDate = parent.getDateFromTrippleDdls();
if (isNaN(birthDate)) {
return false;
}
var today = new Date();
var age = today.getFullYear() - birthDate.getFullYear();
var m = today.getMonth() - birthDate.getMonth();
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age >= parseInt(params.min, 10);
}, '');
})(jQuery);
, 3 , :
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/trippleddlAdapters.js")" type="text/javascript"></script>