Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding the default EditorFor template-selecting in ASP.NET MVC 3 RC

I'm creating a MVC-application which currently uses EditorFor to gennerate a lot of views. The whole view is basically just an EditorForModel, and it works great. However, I've reached one small problem, which I can't seem to find a solution for, and it is important that it works the way I need it to, and that is when trying to render EditorFor an interface. The bindings and everything like that's been taken care of, but the problem is that the EditorFor sees that it's an interface, and defaults to the "Object" template. I need it to look at the interface and see if it can find a template with that name, and if it can't, I need it to look trough all the interfaces present to see if it matches any of them. To explain it more simply look at this example:

interfaces:

public interface IAppProvider
{
    string Name { get; set; }
}

public interface IAppMusicProvider : IAppProvider
{
    int GetPlaylistCount();
} // Yeah, I know, this interface is not smart, but it's only for show.

If I now create a View with model = "IAppMusicProvider", and run Html.EditorForModel(), I need it to find the "~Views\Shared\EditorTemplates\IAppProvider.cshtml"-template. Is there any simple way I can achieve this?

like image 284
Alxandr Avatar asked Nov 12 '10 02:11

Alxandr


2 Answers

You could try changing ModelMetadata.TemplateHint in the ModelMetadataProvider. One way to do this is with the decorator pattern:

public class AbstractTypeTemplateSupportingModelMetadataProvider : ModelMetadataProvider
{
    private readonly ModelMetadataProvider wrapped;

    public AbstractTypeTemplateSupportingModelMetadataProvider(ModelMetadataProvider wrapped)
    {
        this.wrapped = wrapped;
    }

    public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType)
    {
        var result = this.wrapped.GetMetadataForProperties(container, containerType).ToList();
        result.ForEach(ChangeTemplateHint);

        return result;
    }

    public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
    {
        var result = this.wrapped.GetMetadataForProperty(modelAccessor, containerType, propertyName);
        ChangeTemplateHint(result);

        return result;
    }

    public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType)
    {
        var result = this.wrapped.GetMetadataForType(modelAccessor, modelType);
        ChangeTemplateHint(result);

        return result;
    }

    private void ChangeTemplateHint(ModelMetadata source)
    {
        if (source.Model is IAppProvider) //Or use source.ModelType if you want to support the model being null.
            source.TemplateHint = typeof(IAppProvider).Name;
    }
}

And in your start-up logic:

ModelMetadataProviders.Current = new AbstractTypeTemplateSupportingModelMetadataProvider(ModelMetadataProviders.Current);
like image 106
Sam Avatar answered Oct 05 '22 12:10

Sam


Did you try using the [TemplateHint] attribute?

like image 35
John Farrell Avatar answered Oct 05 '22 13:10

John Farrell