Custom Binder for Internal Model

I have a model like this:

public class MainModel { public string Id {get;set;} public string Title {get;set;} public TimePicker TimePickerField {get;set;} } 

TimePicker is an internal model that looks like this:

 public class TimePicker { public TimeSpan {get;set;} public AmPmEnum AmPm {get;set;} } 

I am trying to create a custom model binding for an internal model: TimePicker

The question arises: how to get values ​​in the binding of a custom model that were presented in the form in the fields of the TimePicker model?

If I try to do it like this:

 var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 

I just get null in value .

I'm not sure how to implement model binding correctly.

 public class TimePickerModelBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException("bindingContext"); } var result = new TimePicker(); var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (value != null) { bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value); try { //result = Duration.Parse(value.AttemptedValue); } catch (Exception ex) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex.Message); } } return result; } } 
+4
source share
1 answer

The following works for me.

Model:

 public enum AmPmEnum { Am, Pm } public class TimePicker { public TimeSpan Time { get; set; } public AmPmEnum AmPm { get; set; } } public class MainModel { public TimePicker TimePickerField { get; set; } } 

Controller:

 public class HomeController : Controller { public ActionResult Index() { var model = new MainModel { TimePickerField = new TimePicker { Time = TimeSpan.FromHours(1), AmPm = AmPmEnum.Pm } }; return View(model); } [HttpPost] public ActionResult Index(MainModel model) { return View(model); } } 

View ( ~/Views/Home/Index.cshtml ):

 @model MainModel @using (Html.BeginForm()) { @Html.EditorFor(x => x.TimePickerField) <button type="submit">OK</button> } 

A custom editor template ( ~/Views/Shared/EditorTemplates/TimePicker.cshtml ), which combines the properties of Time and AmPm in one input field and for the subsequent subsequent splitting into them, a custom connecting device will be required.

 @model TimePicker @Html.TextBox("_picker_", string.Format("{0} {1}", Model.Time, Model.AmPm)) 

and model binder:

 public class TimePickerModelBinder : IModelBinder { public object BindModel( ControllerContext controllerContext, ModelBindingContext bindingContext ) { var key = bindingContext.ModelName + "._picker_"; var value = bindingContext.ValueProvider.GetValue(key); if (value == null) { return null; } var result = new TimePicker(); try { // TODO: instead of hardcoding do your parsing // from value.AttemptedValue which will contain the string // that was entered by the user return new TimePicker { Time = TimeSpan.FromHours(2), AmPm = AmPmEnum.Pm }; } catch (Exception ex) { bindingContext.ModelState.AddModelError( bindingContext.ModelName, ex.Message ); // This is important in order to preserve the original user // input in case of error when redisplaying the view bindingContext.ModelState.SetModelValue(key, value); } return result; } } 

and finally register your connecting device in Application_Start :

 ModelBinders.Binders.Add(typeof(TimePicker), new TimePickerModelBinder()); 
+11
source

Source: https://habr.com/ru/post/1392773/


All Articles