We have a custom model binder that deserialises json into a list of objects and I want to use that model binder for several views, each of which uses a different viewmodel.
What we want to avoid is having to register the model binder for each view model as follows:
ModelBinders.Binders.Add(typeof(ViewModelOne), new JsonPropertyBinder());
ModelBinders.Binders.Add(typeof(ViewModelTwo), new JsonPropertyBinder());
What we'd like to do is have the ViewModels derive from a base class (which they do) and register that base class:
ModelBinders.Binders.Add(typeof(ViewModelBase), new JsonPropertyBinder());
where ViewModelOne
and ViewModelTwo
inherit form ViewModelBase
. I've tried this and I didn't have any luck. The problem there, is that the property that needs to be custom-bound is not in the base ViewModel. What we really want is an elegant solution to implementing my model binder in a generic way.
We also have a custom attribute [JsonBindable]
on the properties in my viewmodels that are to be custom-bound, I then check for this attribute in the binder:
public class JsonPropertyBinder : DefaultModelBinder
{
protected override object GetPropertyValue(ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, System.Web.Mvc.IModelBinder propertyBinder)
{
if (propertyDescriptor.Attributes.OfType<Attribute>().Any(x => (x is JsonBindableAttribute)))
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
return JsonConvert.DeserializeObject(value, propertyDescriptor.PropertyType);
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}
I've tried adding the [ModelBinder]
attribute to my viewmodel, but without success. Although, I'm not sure I like this approach, as I'd like to keep the registration of the binder to one place, rather than spread out
-- EDIT --
I guess I could create an intermediary class (e.g. ViewModelIntermediate
) that will inherit from the ViewModelBase
, contain only the property I want to custom-bind, and then have ViewModelOne
and ViewModelTwo
inherit from ViewModelIntermediate
so that I can register the binder once using the derived ViewModel e.g.
ModelBinders.Binders.Add(typeof(ViewModelIntermediate), new JsonPropertyBinder());
but that seems like a clumsy solution. I want to be able to declare the custom binder once and use it with any view model - and not have to abstract my classes to oblivion.
Right now I'm thinking that I might have to declare a new default binder which inherits from DefaultModelBinder and have some logic in there that checks for certain (or custom) attributes and processes them accordingly. something like this