Question

Hi i've used this post to create a wizard in asp.net mvc: multi-step registration process issues in asp.net mvc (splitted viewmodels, single model)

It works only with dataannotations on the concrete classes behind the interface IStepViewModel

Is it possible in the StepViewModelBinder to add some functionality to execute a modelbinder on the concrete steps?

Thanks in advance

Was it helpful?

Solution

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
{ ... }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top