Found a solution. Unfornutanetely it is not at generic as the original - but it allows modelbinders for the viewmodels.
I replaced the original StepViewModelBinder with the following:
public class StepViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var stepTypeValue = bindingContext.ValueProvider.GetValue("StepType");
var stepType = Type.GetType((string)stepTypeValue.ConvertTo(typeof(string)), true);
var step = Activator.CreateInstance(stepType);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => step, stepType);
return step;
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
//bind using default binding, and calls the overriden "CreateModel"
var model = base.BindModel(controllerContext, bindingContext);
//if the modelstate is not valid return to controller with found error
if (!bindingContext.ModelState.IsValid)
return model;
//if the modelstate is valid, call the modelbinder for the concreteType
var ll = Binders.GetBinder(model.GetType());
return ll.BindModel(controllerContext, bindingContext);
}
}
this solution gets the associated modelbinder for the model - the downside is, since the concrete implementation is hidden behind an interface, a modelbinder is required to work for the concrete implementation, since a concrete type cannot be instantiated from an interface
THe modelbinder for the concrete class could then look as follows:
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var step = new Step1();
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => step, step.GetType());
return step;
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = (Step1)base.BindModel(controllerContext, bindingContext);
bindingContext.ModelState.AddModelError("", "This is a test");
return model;
}
the modelbinder is coupled together with the viewmodel as usually, either by
ModelBinders.Binders.Add(typeof(Step1), new Step1ModelBinder());
or by class annotation:
[ModelBinder(typeof(OpretBrugerStep1ModelBinder))]
I've just created a generic default modelbinder for the step-implementations that does not need a specific implementation - just to get them to work with the example:
public class DefaultStepModelBinder<T> : DefaultModelBinder where T : IStepViewModel, new()
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var step = new T();
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => step, step.GetType());
return step;
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = (T)base.BindModel(controllerContext, bindingContext);
return model;
}
}
Thus the concrete steps kan use this modelbinder - example for step1:
[ModelBinder(typeof(DefaultStepModelBinder<Step1>))]
[Serializable]
public class Step1 : IStepViewModel
{ ... }