I’m talking about asp.net MVC2
The point:
if you are implementing an IModelBinder for a type, you should add a call to
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueFromRequest);
this is done to make sure that at your controller action you can access ModelState[nameOfThisProperty] and nameOfThisProperty will be present as a key at ModelState also for successful binding.
And for the details: ( Partial form validation including custom modelBinding for trimming strings)
why am I using ModelState[nameOfThisProperty] ??
usually you don’t need to access ModelState[nameOfThisProperty] like this, it is ugly and stringly typed 
but lets say you have a form that has two options: “save”,”save draft”
“save” should trigger the entire form’s validation.
”save draft” should trigger validation only on a filed named Title.
I’m using the DataAnnotations validation attributes and no chance in hell that I’m going to create two separate objects to enforce this validation rules.
so I have two separate action methods and at SaveDraft I have this code:
public ActionResult SaveDraft(ArticleModel articleModel )
{
if (ModelState[ArticleModel .ARTICLE_TITLE_FIELD_KEY].Errors.Count > 0)
{
ModelStateDictionary mDic = new ModelStateDictionary();
ModelState titleModelState = ModelState[ArticleModel .ARTICLE_TITLE_FIELD_KEY];
ModelState.Clear();
//ugly way to preserve this error message if it exists
ModelState.Add(ArticleModel .ARTICLE_TITLE_FIELD_KEY, titleModelState);
return View(“New", articleModel );
}
//party with articleModel ignoring all other validation errors
..
At the client side I have this function(I use jQuery and jQuery validation):
function BindSaveDraftButton()
{
var jqBtnSave = $("#btnSaveDraft");
jqBtnSave.click(function ()
{
var jqTxbTitle = $(".txbTitle:first");
if (!jqTxbTitle.valid()) //trigger only this field’s validation manually
{
jqTxbTitle.focus();
return false;
}
// the button holds the saveDraft action at a specific actionUrl attribute
// (I know I know I shuld change this to data-actionUrl
$("#formPostArticle").attr("action", $(this).attr("actionUrl"));
//NOTE: I don’t return anything here so the form will submit as a regular form, just to a different action
});
}
oh.. and the button for “save draft” has the class “cancel” so it won’t trigger client validations
And the connection to custom ModelBinding??
Everything was working great(some bits a it ugly, but this is the case for now) till I decided to implement a TrimmedStringModelBinder so all bound strings will be trimmed during binding.
public class TrimmedStringModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException("bindingContext");
}
ValueProviderResult valueFromRequest =
bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueFromRequest);
if (valueFromRequest == null)
{
return null;
}
if (!string.IsNullOrEmpty(valueFromRequest.AttemptedValue))
{
return valueFromRequest.AttemptedValue.Trim();
}
return valueFromRequest.AttemptedValue;
}
}
registered it as follows at the Global.asax
ModelBinders.Binders.Add(typeof(string), new TrimmedStringModelBinder());
Note the row with yellow BG – it was missing at the first implementation because I had no idea I need it.
without it the check for -
ModelState[ArticleModel .ARTICLE_TITLE_FIELD_KEY].Errors.Count
threw an exception since this property was valid(had value in it) so during the server validation this key wasn’t entered into the ModelState.
when I removed the registration to the TrimmedStringModelBinder I saw that even if the property is valid – a key would be added the ModelState stating there are no errors here.
Looked a bit at Stackoverflow but didn’t find anything useful..
The solution can always be found at the source:
So opened the mvc2-rtm-sources\src\SystemWebMvc\Mvc\DefaultModelBinder.cs
and the solution is right there at the BindModel method :
…
// Simple model = int, string, etc.; determined by calling TypeConverter.CanConvertFrom(typeof(string))
// or by seeing if a value in the request exactly matches the name of the model we're binding.
// Complex type = everything else.
if (!performedFallback) {
ValueProviderResult vpResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (vpResult != null) {
return BindSimpleModel(controllerContext, bindingContext, vpResult);
}
}.
…
went directly to the Simple model condition since this is what I’m binding(string remember?)
that led to the BindSimpleModel method, where the first line is:
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
So here it is:
you must add all all the keys to the ModelState , during the binding, the validation only updates faulty ones.
(it is so awesome that we have access to the MVC code, it is not a huge codebase it is self explanatory, and have some comments where needed) .
This post has become too long, and I have more things to elaborate on the TrimmedStringModelBinder - so will do it at another post later.
till then - happy coding