Classeur de modèle personnalisé pour le modèle intérieur
-
13-11-2019 - |
Question
J'ai un modèle comme ceci:
public class MainModel
{
public string Id {get;set;}
public string Title {get;set;}
public TimePicker TimePickerField {get;set;}
}
TimePicker
est un modèle intérieur qui ressemble à ceci:
public class TimePicker
{
public TimeSpan {get;set;}
public AmPmEnum AmPm {get;set;}
}
J'essaie de créer une liaison de modèle personnalisée pour le modèle intérieur: TimePicker
La question est: comment puis-je obtenir des valeurs dans le classeur de modèle personnalisé qui a été soumis sous forme TimePicker
champs modèles?
Si j'essaye de l'obtenir comme ceci:
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
Je viens juste null
dans value
.
Je ne sais pas comment implémenter correctement le classeur du modèle.
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;
}
}
La solution
Ce qui suit fonctionne pour moi.
Modèle:
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; }
}
Manette:
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);
}
}
Voir (~/Views/Home/Index.cshtml
):
@model MainModel
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.TimePickerField)
<button type="submit">OK</button>
}
Modèle d'éditeur personnalisé (~/Views/Shared/EditorTemplates/TimePicker.cshtml
) qui fusionne le Time
et AmPm
Propriétés en un seul champ de saisie et qui nécessitera un liant de modèle personnalisé plus tard afin de les diviser lorsque le formulaire est soumis:
@model TimePicker
@Html.TextBox("_picker_", string.Format("{0} {1}", Model.Time, Model.AmPm))
Et le classeur du modèle:
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;
}
}
Et enfin enregistrer votre classeur de modèle dans Application_Start
:
ModelBinders.Binders.Add(typeof(TimePicker), new TimePickerModelBinder());