As suggested by other answers, using EditorFor
instead of Editor
seems like a problem. However, using EditorFor
requires knowing the type of the model and the type of property at compile time, which is not the case with Object.cshtml
.
You can still do this by creating and calling the correct general EditorFor
method using reflection. The code for this is really messy, so for this you need to use several extension methods to use.
Use them as in Object.cshtml
, where prop
is an instance of ModelMetadata
, as in the question:
@Html.DisplayFor(prop) @Html.LabelFor(prop) @Html.EditorFor(prop) @Html.ValidationMessageFor(prop)
Here are the extension methods:
using System; using System.Linq.Expressions; using System.Reflection; using System.Web.Mvc; using System.Web.Mvc.Html; using System.Web.Routing; namespace ASP { public static class NonStronglyTypedStronglyTypedHtmlHelpers { public static MvcHtmlString DisplayFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop) { return StronglyTypedHelper(html, h => h.DisplayFor, prop); } public static MvcHtmlString EditorFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop) { return StronglyTypedHelper(html, h => h.EditorFor, prop); } public static MvcHtmlString LabelFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop) { return StronglyTypedHelper(html, h => h.LabelFor, prop); } public static MvcHtmlString ValidationMessageFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop) { return StronglyTypedHelper(html, h => h.ValidationMessageFor, prop); } private static MvcHtmlString StronglyTypedHelper(HtmlHelper html, Func<HtmlHelper<object>, GenericHelper<object>> accessMethod, ModelMetadata prop) { var constructedMethod = MakeStronglyTypedHelper(html, accessMethod, prop); var genericPropertyExpression = MakePropertyExpression(prop); var typedHtmlHelper = MakeStronglyTypedHtmlHelper(html, prop.ContainerType); return (MvcHtmlString)constructedMethod.Invoke(null, new object[] { typedHtmlHelper, genericPropertyExpression }); } private static MethodInfo MakeStronglyTypedHelper(HtmlHelper html, Func<HtmlHelper<object>, GenericHelper<object>> accessMethod, ModelMetadata prop) { var objectTypeHelper = new HtmlHelper<object>(html.ViewContext, html.ViewDataContainer, html.RouteCollection); var runMethod = accessMethod(objectTypeHelper); var constructedMehtod = runMethod.Method; var genericHelperDefinition = constructedMehtod.GetGenericMethodDefinition(); return genericHelperDefinition.MakeGenericMethod(prop.ContainerType, prop.ModelType); } private static object MakeStronglyTypedHtmlHelper(HtmlHelper html, Type type) { var genericTypeDefinition = typeof(HtmlHelper<>); var constructedType = genericTypeDefinition.MakeGenericType(type); var constructor = constructedType.GetConstructor(new[] { typeof(ViewContext), typeof(IViewDataContainer), typeof(RouteCollection) }); return constructor.Invoke(new object[] { html.ViewContext, html.ViewDataContainer, html.RouteCollection }); } private static LambdaExpression MakePropertyExpression(ModelMetadata prop) { var propertyInfo = prop.ContainerType.GetProperty(prop.PropertyName); var expressionParameter = Expression.Parameter(prop.ContainerType); var propertyExpression = Expression.MakeMemberAccess(expressionParameter, propertyInfo); return Expression.Lambda(propertyExpression, expressionParameter); } private delegate MvcHtmlString GenericHelper<TModel>(Expression<Func<TModel, object>> expression); } }