模型中名为 Title 的属性与视图中名为 View.Title 的属性之间的绑定冲突(在 MVC 中)

发布于 2024-10-06 13:22:21 字数 3422 浏览 1 评论 0原文

我的模型包含一个名为 Title 的属性,在我的 Create 视图中,我使用 ViewBag.Title 设置页面标题。

这会产生以下问题:Html.Editor 生成的表单将显示 ViewBag.Title 中的文本,而不是模型的 Title 值。

我发现的唯一解决方法是首先调用 Html.Editor,然后设置 View.Title

有人有更好的解决方案吗?

编辑 1:我正在使用 MVC 3。

编辑 2:这是我的 DisplayTemplates/Object.cshtml

@model dynamic
@using Iconum.VS10CS040.Library.Web.MVC3.Helpers

@if (ViewData.TemplateInfo.TemplateDepth > 1) {
    <span class="editor-object simple">@ViewData.ModelMetadata.SimpleDisplayText</span>
} else {
    foreach (var prop in ViewData.ModelMetadata.Properties.Where(
            pm => 
                pm.ShowForEdit 
                && !ViewData.TemplateInfo.Visited(pm)      
                && pm.ModelType != typeof(System.Data.EntityState)
                && !pm.IsComplexType             
            )
        ) 
        {
        if (prop.HideSurroundingHtml) {
            <text>@Html.Editor(prop.PropertyName)</text>
        } else {
            string css = "";
            if (prop.Model != null && prop.Model.GetType() != null)
            {
                css += " " + prop.Model.GetType().ToString().ToLower().Replace('.', '-');
            }
            if (prop.DataTypeName != null)
            {
                css += " " + prop.DataTypeName.ToLower();
            }
            if (prop.IsRequired && prop.ModelType.FullName != "System.Boolean")
            {
                css += " required";
            }

            <div class="editor-container @css">
                 <div class="editor-label">
                    @if (!String.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString()))
                    {
                        // Use LabelWithForThatMatchesTheIdOfTheInput instead of Label because of a bug (fixed in MVC 3)
                       @Html.LabelWithForThatMatchesTheIdOfTheInput(prop.PropertyName)
                    }
                    @if (prop.IsRequired && prop.ModelType.FullName != "System.Boolean")
                    {
                        @Html.Raw(" <span class=\"required\">*<span>");
                    }
                </div>
                <div class="editor-field">
                    @* This the line that causes my problem *@
                    @Html.Editor(prop.PropertyName) 
                    @Html.ValidationMessage(prop.PropertyName)
                </div>
            </div>
        }
        } //foreach

    // Loop though all items in the Model with an TemplateHint (UIHint)
    foreach (var prop in ViewData.ModelMetadata.Properties.Where(
           pm => pm.ShowForEdit
           && !ViewData.TemplateInfo.Visited(pm)
           && pm.ModelType != typeof(System.Data.EntityState)
           && !pm.IsComplexType
           && pm.TemplateHint != null
           && (
            pm.TemplateHint == "jWYSIWYG0093"
            ||
            pm.TemplateHint == "jQueryUIDatepicker"
            ||
            pm.TemplateHint == "CKEditor"
           )
           )
       )
    {
        // TODO: check for duplicate js file includes
        @Html.Editor(prop.PropertyName, prop.TemplateHint + "-Script")
    }    

}

My Model contains a property named Title, and in my Create view I set the page title using ViewBag.Title.

This creates the following problem: the form generated by Html.Editor will display the text from ViewBag.Title, instead of the model's Title value.

The only workaround I have found is first calling Html.Editor, and then setting the View.Title.

Does anyone have a better solution?

Edit 1: I am using MVC 3.

Edit 2: This is my DisplayTemplates/Object.cshtml:

@model dynamic
@using Iconum.VS10CS040.Library.Web.MVC3.Helpers

@if (ViewData.TemplateInfo.TemplateDepth > 1) {
    <span class="editor-object simple">@ViewData.ModelMetadata.SimpleDisplayText</span>
} else {
    foreach (var prop in ViewData.ModelMetadata.Properties.Where(
            pm => 
                pm.ShowForEdit 
                && !ViewData.TemplateInfo.Visited(pm)      
                && pm.ModelType != typeof(System.Data.EntityState)
                && !pm.IsComplexType             
            )
        ) 
        {
        if (prop.HideSurroundingHtml) {
            <text>@Html.Editor(prop.PropertyName)</text>
        } else {
            string css = "";
            if (prop.Model != null && prop.Model.GetType() != null)
            {
                css += " " + prop.Model.GetType().ToString().ToLower().Replace('.', '-');
            }
            if (prop.DataTypeName != null)
            {
                css += " " + prop.DataTypeName.ToLower();
            }
            if (prop.IsRequired && prop.ModelType.FullName != "System.Boolean")
            {
                css += " required";
            }

            <div class="editor-container @css">
                 <div class="editor-label">
                    @if (!String.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString()))
                    {
                        // Use LabelWithForThatMatchesTheIdOfTheInput instead of Label because of a bug (fixed in MVC 3)
                       @Html.LabelWithForThatMatchesTheIdOfTheInput(prop.PropertyName)
                    }
                    @if (prop.IsRequired && prop.ModelType.FullName != "System.Boolean")
                    {
                        @Html.Raw(" <span class=\"required\">*<span>");
                    }
                </div>
                <div class="editor-field">
                    @* This the line that causes my problem *@
                    @Html.Editor(prop.PropertyName) 
                    @Html.ValidationMessage(prop.PropertyName)
                </div>
            </div>
        }
        } //foreach

    // Loop though all items in the Model with an TemplateHint (UIHint)
    foreach (var prop in ViewData.ModelMetadata.Properties.Where(
           pm => pm.ShowForEdit
           && !ViewData.TemplateInfo.Visited(pm)
           && pm.ModelType != typeof(System.Data.EntityState)
           && !pm.IsComplexType
           && pm.TemplateHint != null
           && (
            pm.TemplateHint == "jWYSIWYG0093"
            ||
            pm.TemplateHint == "jQueryUIDatepicker"
            ||
            pm.TemplateHint == "CKEditor"
           )
           )
       )
    {
        // TODO: check for duplicate js file includes
        @Html.Editor(prop.PropertyName, prop.TemplateHint + "-Script")
    }    

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

赢得她心 2024-10-13 13:22:21

我建议使用 EditorFor 而不是 Editor

Html.EditorFor(x => x.Title)

而不是:

Html.Editor("Title")

这样,视图不仅可以利用您的视图模型,而且在这种情况下它的行为也符合预期。

ASP.NET MVC 3.0 RTM (Razor) 示例:

模型:

public class MyViewModel
{
    public string Title { get; set; }
}

控制器:

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);
    }
}

视图:

@model AppName.Models.MyViewModel
@{
    ViewBag.Title = "Home Page";
}

@Html.EditorFor(x => x.Title)

@{
    ViewBag.Title = "Some other title";
}

因此,无论我们如何尝试在这里滥用,编辑器模板都会使用正确的模型标题(如果我们使用 Html.html,则情况并非如此)。编辑器(“标题”))。

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")).

还给你自由 2024-10-13 13:22:21

正如其他答案所建议的,使用 EditorFor 而不是 Editor 似乎可以解决该问题。但是,使用 EditorFor 需要在编译时了解模型类型和属性类型,而 Object.cshtml 则不然。

您仍然可以通过使用反射构建并调用正确的通用构造的 EditorFor 方法来实现此目的。执行此操作的代码确实很混乱,因此这里有一些可重用的扩展方法来为您执行此操作。

Object.cshtml 中像这样使用它们,其中 propModelMetadata 的实例,如问题中所示:

@Html.DisplayFor(prop)
@Html.LabelFor(prop)
@Html.EditorFor(prop)
@Html.ValidationMessageFor(prop)

以下是扩展方法:

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);
    }
}

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);
    }
}
心奴独伤 2024-10-13 13:22:21

我自己找到了部分解决方案。

只需使用:

    @Html.EditorForModel()

而不是:

    @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() 方法返回相同的结果,但没有描述的问题。

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.

_失温 2024-10-13 13:22:21

我解决同样的问题。使用此语法代替 Html.Editor

@(Html.EditorFor(p => property.Model))

I solve same problem. Use this syntax instead Html.Editor

@(Html.EditorFor(p => property.Model))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文