My Model contains a property named Title
, and in my Create
view I set the page title using ViewBag.Title
.
This creates the fol
I found partial solution myself.
Just use:
@Html.EditorForModel()
instead of:
@foreach (var property in Model.GetMetadata().Properties)
{
<div class="editor-label">
@Html.Label(property.PropertyName)
</div>
<div class="editor-field">
@Html.Editor(property.PropertyName)
@Html.ValidationMessage(property.PropertyName)
</div>
}
Html.EditorForModel() method return same results, but without described problem.
As suggested by the other answers, using EditorFor
instead of Editor
seems to work around the problem. However, using EditorFor
requires knowledge of the model type and property type at compile-time, which isn't the case for Object.cshtml
.
You can still do this by building up and calling the correct generically-constructed EditorFor
method using reflection. The code to do this is really messy, so here are some re-usable extension methods to do it for you.
Use them like this in Object.cshtml
where prop
is an instance of ModelMetadata
like 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);
}
}
I would recommend using EditorFor
instead of Editor
.
Html.EditorFor(x => x.Title)
instead of:
Html.Editor("Title")
This way not only that the view takes advantage of your view model but it behaves as expected in this case.
Example with ASP.NET MVC 3.0 RTM (Razor):
Model:
public class MyViewModel
{
public string Title { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Title = "ViewBag title";
ViewData["Title"] = "ViewData title";
var model = new MyViewModel
{
Title = "Model title"
};
return View(model);
}
}
View:
@model AppName.Models.MyViewModel
@{
ViewBag.Title = "Home Page";
}
@Html.EditorFor(x => x.Title)
@{
ViewBag.Title = "Some other title";
}
So no matter how much we try to abuse here the editor template uses the correct model title (which is not the case if we used Html.Editor("Title")
).
I solve same problem. Use this syntax instead Html.Editor
@(Html.EditorFor(p => property.Model))