2010-12-07 2 views
8

Mein Modell enthält eine Eigenschaft mit dem Namen Title, und in meiner Create Ansicht legte ich den Seitentitel mit ViewBag.Title.Binding-Konflikt zwischen einer Eigenschaft namens Titel in meinem Modell und View.Title in meiner Ansicht (in MVC)

Dies verursacht das folgende Problem: das Formular generiert von Html.Editor wird den Text von ViewBag.Title statt Title des Modells anzeigen.

Die einzige Problemumgehung, die ich gefunden habe, ist zuerst Html.Editor aufrufen und dann View.Title einstellen.

Hat jemand eine bessere Lösung?

Edit 1: Ich bin mit MVC 3.

Edit 2: Das ist mein 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") 
    }  

} 
+0

FYI: die gleichen "over-aggressive" Bindung passiert in einem DisplayFor. –

+0

Was ist 'View.Title'? Meintest du "ViewBag.Title"? Welche Version von ASP.NET MVC verwenden Sie? Welche View Engine? –

+0

Ja, hier geht es um ViewBag.Title, sieht aus wie ein Editor, der an diese Variable gebunden ist. – gimalay

Antwort

2

Ich würde empfehlen, EditorFor statt Editor verwenden.

Html.EditorFor(x => x.Title) 

statt:

Html.Editor("Title") 

Auf diese Weise nicht nur, dass der Blick auf die Nutzung Ihrer Ansicht Modell nimmt aber es verhält sich wie in diesem Fall zu erwarten.

Beispiel mit ASP.NET MVC 3.0 RTM (Razor):

Modell:

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

Ausblick:

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

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

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

Also egal Wie sehr versuchen wir hier zu missbrauchen? Die Editor-Vorlage verwendet den korrekten Modelltitel (was bei Verwendung von Html.Editor("Title") nicht der Fall ist).

+0

Danke, aber ich verstehe das. Ich muss View sehen, der nicht über den konkreten Modelltyp Bescheid wusste. Es verwendet also Reflektion, um Eigenschaftennamen zu erhalten und Editoren zu erstellen. Sie können mir Ratschläge geben, wie ich Formulare aus Metadaten ohne Html.Editor (String) -Methode erzeugen kann. Oder wie Sie die Bindung an die ViewBag-Eigenschaften vermeiden können. – gimalay

+0

@ Gimalay, oh ich verstehe jetzt. Sie können sich den [folgenden Blogpost] (http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-3-default-templates.html) ansehen, der erklärt, wie Editor-Vorlagen sind implementiert. Die 'Object.ascx'-Editorvorlage zeigt, wie Sie ein dynamisches Formular erstellen können, wenn Sie den Modelltyp nicht kennen. –

+0

Dieses Beispiel enthält genau den gleichen Code, den ich habe. Nur Html.Editor (String) Aufrufe. – gimalay

-1

Ich habe Teillösung selbst gefunden.

Verwenden Sie einfach:

@Html.EditorForModel() 

statt:

@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() -Methode gleiche Ergebnisse zurück, aber ohne beschriebenes Problem.

+0

Würde es Ihnen etwas ausmachen, zu zeigen, wie Ihre EditorTemplates/Object.cshtml nach der Anwendung dieser Problemumgehung aussieht? Wenn ich 'Html.EditorForModel()' in meiner Object.cshtml Vorlage versuche, scheint es, alle Eigenschaften zu ignorieren und nichts darzustellen. Vielen Dank. –

+0

Ich habe die Objektvorlage in keiner Weise geändert. – gimalay

+0

Wenn Sie erwarten, dass EditorForModel Ihr Objektdiagramm tief durchläuft, müssen Sie den MVC-Quellcode bearbeiten. Es wird standardmäßig nur 2 Ebenen tief sein (was nur native Eigenschaften in Ihrem Ansichtsmodell bedeutet). –

-1

Ich löse das gleiche Problem. Verwenden Sie diese Syntax stattdessen Html.Editor

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

Das sah zunächst nach einer guten Idee aus, aber nachdem ich es versucht hatte, stellte ich fest, dass es einige Probleme hatte. Es registriert den falschen Eigenschaftsnamen (und möglicherweise Typ) mit dem Framework. Dies scheint verschiedene Dinge durcheinander zu bringen, einschließlich des generierten HTML, der Modellbindung, der Validierung und wahrscheinlich anderer. – Sam

1

Wie die anderen Antworten vorgeschlagen, mit EditorFor statt Editor, um das Problem zu funktionieren scheint. Die Verwendung von EditorFor erfordert jedoch die Kenntnis des Modelltyps und des Eigenschaftstyps zur Kompilierungszeit, was für Object.cshtml nicht der Fall ist.

Sie können dies immer noch tun, indem Sie die korrekte generisch konstruierte Methode EditorFor mit Reflektion aufbauen und aufrufen. Der Code, um dies zu tun, ist wirklich chaotisch, also hier sind einige wiederverwendbare Erweiterungsmethoden, um es für Sie zu tun.

sie so in Object.cshtml verwenden, wo prop eine Instanz von ModelMetadata wie in der Frage ist:

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

Hier sind die Erweiterungsmethoden:

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

löst eine Exception in der letzten Zeile der 'MakeStronglyTypedHtmlHelper' Methode aus, wenn komplexe Datenstrukturen verwendet werden:' @ Html.DisplayFor (x => x.Address.Street) 'Irgendeine Idee, wie man es beheben kann? – Dmitry

+0

@Dmitry, nicht sicher, sorry. Ich kann mich nicht einmal daran erinnern, wie der Code funktioniert! :) – Sam

Verwandte Themen