Created
January 24, 2012 18:07
-
-
Save chaoaretasty/1671586 to your computer and use it in GitHub Desktop.
Timespan model binding for MVC3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
========================= | |
Based on the Simple Time Picker Modelbinder by Sergi Papseit Valls | |
http://www.sharpedgesoftware.com/Blog/2011/06/08/simple-time-picker-model-binder-for-aspnet-mvc-3-and-how-to-save-it-to-sql-ce-4-using-ef-41 | |
========================= | |
I tried the modelbinder above but found a few issues with it including not being able to deal with blank options or partially filled out TimeSpans. This is my current version of it (it is a bit messy I warn you). | |
This is designed to bind to nullable TimeSpans, I haven't tried it with non-nullable ones, but you can overcome this by adding a [Required] attribute. The features are: | |
1. Will allow showing an error in a single part of the TimeSpan but put the message for the main property. For example if hours were given but no minutes the drop down for the minutes property would be highlighted but you can get the error message via ValidationMessageFor(x=>x.MyTimeSpan) which should help it fit in better with your form design rather than checking each sub-property. | |
2. Will gracefully handle unexpected inputs (eg if you use textboxes rather than dropdowns or it the form data is altered to send something other than numbers in the right range). | |
3. Won't display a validation error if neither hours or minutes are selected. Will display an error message if the TimeSpan has the [Required] attribute | |
4. Can take a variable in the editor's ViewData to limit the number of options for the minutes | |
PS Remember to add the following line to your Application_Start() method: | |
ModelBinders.Binders.Add(typeof(TimeSpan?), new MyApp.ModelBinders.TimeBinder()); | |
Update | |
====== | |
Added ViewData properties to limit the hours range |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class TimeBinder : IModelBinder | |
{ | |
public object BindModel(ControllerContext controllerContext, | |
ModelBindingContext bindingContext) | |
{ | |
// Ensure there's incoming data | |
var keyH = bindingContext.ModelName + ".Value.Hours"; | |
var keyM = bindingContext.ModelName + ".Value.Minutes"; | |
var valueProviderResultH = bindingContext.ValueProvider | |
.GetValue(keyH); | |
var valueProviderResultM = bindingContext.ValueProvider | |
.GetValue(keyM); | |
if ((valueProviderResultH == null || valueProviderResultH.AttemptedValue == "") && (valueProviderResultM == null || valueProviderResultM.AttemptedValue == "")) | |
{ | |
return null; | |
} | |
// Preserve it in case we need to redisplay the form | |
bindingContext.ModelState | |
.SetModelValue(keyH, valueProviderResultH); | |
bindingContext.ModelState | |
.SetModelValue(keyM, valueProviderResultM); | |
// Parse | |
var hours = ((string)valueProviderResultH.AttemptedValue); | |
var minutes = ((string)valueProviderResultM.AttemptedValue); | |
// A TimeSpan represents the time elapsed since midnight | |
int Hours = 0; | |
int Minutes = 0; | |
bool valid = true; | |
try | |
{ | |
Hours = Convert.ToInt32(hours); | |
if (Hours < 0 || Hours > 23) | |
{ | |
throw new ArgumentOutOfRangeException(); | |
} | |
} | |
catch | |
{ | |
bindingContext.ModelState.AddModelError( | |
bindingContext.ModelName+".Value.Hours",""); | |
bindingContext.ModelState.AddModelError( | |
bindingContext.ModelName, | |
"Hours must be between 0 and 23"); | |
valid = false; | |
} | |
try | |
{ | |
Minutes = Convert.ToInt32(minutes); | |
if (Minutes < 0 || Minutes > 59) | |
{ | |
throw new ArgumentOutOfRangeException(); | |
} | |
} | |
catch | |
{ | |
bindingContext.ModelState.AddModelError( | |
bindingContext.ModelName + ".Value.Minutes",""); | |
bindingContext.ModelState.AddModelError( | |
bindingContext.ModelName, | |
"Minutes must be between 0 and 59"); | |
valid = false; | |
} | |
var time = new TimeSpan(Convert.ToInt32(Hours), | |
Convert.ToInt32(Minutes), 0); | |
if (!valid) | |
return null; | |
return time; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@model TimeSpan? | |
@{ | |
int firstHour = (int)(ViewData["firstHour"] ?? 0); | |
int lastHour = (int)(ViewData["lastHour"] ?? 23); | |
int minuteIntervals = (int)(ViewData["minuteIntervals"] ?? 1); | |
//If there's an error for the TimeSpan but not for its subproperties add an error for both subproperties, this will add the css class for both fields | |
string modelPrefix = ViewData.TemplateInfo.HtmlFieldPrefix; | |
string minPrefix = modelPrefix + ".Value.Minutes"; | |
string hourPrefix = modelPrefix + ".Value.Hours"; | |
var subProps = ViewData.ModelState.Where(x => x.Key == hourPrefix || x.Key== minPrefix); | |
bool modelError = ViewData.ModelState[modelPrefix] != null && ViewData.ModelState[modelPrefix].Errors.Any(); | |
if(!subProps.Any(x => x.Value.Errors.Any()) && modelError) | |
{ | |
ViewData.ModelState.AddModelError(minPrefix, ""); | |
ViewData.ModelState.AddModelError(hourPrefix, ""); | |
} | |
} | |
@Html.DropDownListFor(x=>x.Value.Hours, Enumerable.Range(firstHour, (lastHour-firstHour+1)) | |
.Select(i => new SelectListItem | |
{ | |
Value = i.ToString(), | |
Text = i.ToString() | |
}), "") : | |
@Html.DropDownListFor(x=>x.Value.Minutes, Enumerable.Range(0, (int)Math.Floor(60f/minuteIntervals)).Select(x => x * minuteIntervals) | |
.Select(i => new SelectListItem | |
{ | |
Value = i.ToString(), | |
Text = i.ToString() | |
}), "") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment