validations net mvc example asp asp.net-mvc asp.net-mvc-3 views anonymous-types

asp.net mvc - net - EditorFor() y adicionalViewData: ¿cómo agregar datos en la clase auxiliar?



validation asp net mvc (5)

¿Has probado simplemente añadiendo los datos a

helper.ViewData[xxx] = yyy;

La colección ViewData es una colección global para ViewContext, por lo que creo que simplemente agregarla a ViewData global estará disponible cuando se presente la plantilla de editor.

MÁS INFORMACIÓN: Según tengo entendido, la propiedad adicionalViewdata es solo una forma fácil de agregar una colección / cualquier cosa a los Datos de Vista globales sobre la marcha después de que haya decidido qué control representar. Todavía usa la misma colección contextual y no es un objeto tan diferente como una forma tardía y limpia de agregar al mismo diccionario de contexto.

No he intentado esto todavía, así que si me falta el punto, dígalo y eliminaré la respuesta.

EditorFor () puede tomar un parámetro object additionalViewData que el método típico para rellenar es algo como:

EditorFor(model => model.PropertyName, new { myKey = "myValue" })

¿Cómo puedo inspeccionar el contenido deViewData adicional, agregar o anexar al valor existente para una clave, etc. en un ayudante HTML personalizado?

He intentado estos enfoques:

  • convertir a Dictionary<string, object>() y agregar / agregar valores: no funciona como parece que la implementación de EditorFor en MVC usa el nuevo RouteValueDictionary (additionalViewData) que incrusta el diccionario dentro de un diccionario
  • convertir a RouteValueDictionary utilizando el new RouteValueDictionary(additionalViewData) pero que tiene el mismo problema (o muy similar) que el anterior

También estoy abierto a "lo estás haciendo mal", tal vez me esté perdiendo un enfoque más simple. Tenga en cuenta que lo que estoy tratando de hacer es escribir un ayudante de HTML que sea reutilizable y agregue algunos valores a los Datos de Vista adicionales para ser usados ​​por vistas personalizadas. Algunos de los valores dependen de los metadatos de la propiedad, por lo que no es tan fácil como usar solo un montón de plantillas diferentes.

Actualizar con el ejemplo de lo que estoy haciendo:

public static MvcHtmlString myNullableBooleanFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> choice, string templateName, object additionalViewData) { ModelMetadata metadata = ModelMetadata.FromLambdaExpression(choice, htmlHelper.ViewData); /* here need to add to additionalViewData some key values among them: { propertyName, metadata.PropertyName } */ StringBuilder sb = new StringBuilder(); sb.AppendLine(htmlHelper.EditorFor(choice, templateName, additionalViewData).ToString()); MvcHtmlString validation = htmlHelper.ValidationMessageFor(choice); if (validation != null) sb.AppendLine(validation.ToString()); return new MvcHtmlString(sb.ToString()); }

Actualice con lo que sucede cuando convierto el objeto anónimo a un Dictionary<string, object>() y le paso ese diccionario a EditorFor() :

Puse un punto de interrupción en la vista de Razor y examiné ViewData. Parece que el diccionario pasado a EditorFor() se coloca dentro de otro Dictionary<string, object>() . En la "Ventana Inmediata", ViewData se ve así:

ViewData.Keys Count = 4 [0]: "Comparer" [1]: "Count" [2]: "Keys" [3]: "Values"

¿Ves cómo el diccionario tiene el contenido de un diccionario dentro de él? Sí, los datos reales se encuentran en ese diccionario interno, sin embargo, como era de esperar, esto no funciona.

Bounty añadido.


RouteValueDictionary utiliza la infraestructura TypeDescriptor ( TypeDescriptor.GetProperties(obj) ) para reflejar el objeto adicionalViewData. Como esta infraestructura es extensible, puede crear una clase que implemente el ICustomTypeDescriptor y proporcione propiedades falsas de su elección, por ejemplo, basadas en un diccionario interno. En el siguiente artículo encontrará una implementación: http://social.msdn.microsoft.com/Forums/en/wpf/thread/027cb000-996e-46eb-9ebf-9c41b07d5daa Con esta implementación puede agregar fácilmente "propiedades" con nombre y valor arbitrario y pásalo como el objeto addtionalViewData. Para obtener las propiedades existentes del objeto inicial, puede usar el mismo método que MVC usará más adelante, llame a TypeDescriptor.GetProperties, enumere las propiedades y obtenga el nombre y el valor de la propiedad, y agréguelos a su nuevo "objeto" como bien.


Si entiendo correctamente, está intentando iterar sobre propiedades de un tipo anónimo. Si es así: ¿Cómo itero las propiedades de un objeto anónimo en C #?

[Editar] Ok, es más que eso. Esto realmente me molesta ahora porque amo a C # y no me deja hacer lo que Python hace, lo cual también amo. Así que aquí hay una solución, si está utilizando C # 4, pero está desordenado y funcionará para un problema similar que tengo, pero tal vez no sea exactamente para usted:

// Assume you''ve created a class to hold commonly used additional view data // called MyAdditionalViewData. I would create an inheritance hierarchy to // contain situation-specific (area-specific, in my case) additionalViewData. class MyAdditionalViewData { public string Name { get; set; } public string Sound { get; set; } } /* In your views */ // Pass an instance of this class in the call to your HTML Helper EditorFor(model => model.PropertyName, new { adv = new MyAdditionalViewData { Name = "Cow", Sound = "Moo" } }) ; /* In your HTML helper */ // Here''s the C# 4 part that makes this work. dynamic bovine = new ExpandoObject(); // Copy values var adv = x.adv; if (adv.Name != null) bovine.Name = adv.Name; if (adv.Sound != null) bovine.Sound = adv.Sound; // Additional stuff bovine.Food = "Grass"; bovine.Contents = "Burgers"; // When the MVC framework converts this to a route value dictionary // you''ll get a proper object, not a dictionary within a dictionary var dict = new RouteValueDictionary(bovine);

Tiene que haber una mejor manera.


Un poco tarde, pero hay una solución de trabajo utilizando CodeDom disponible here .

public interface IObjectExtender { object Extend(object obj1, object obj2); } public class ObjectExtender : IObjectExtender { private readonly IDictionary<Tuple<Type, Type>, Assembly> _cache = new Dictionary<Tuple<Type, Type>, Assembly>(); public object Extend(object obj1, object obj2) { if (obj1 == null) return obj2; if (obj2 == null) return obj1; var obj1Type = obj1.GetType(); var obj2Type = obj2.GetType(); var values = obj1Type.GetProperties() .ToDictionary( property => property.Name, property => property.GetValue(obj1, null)); foreach (var property in obj2Type.GetProperties() .Where(pi => !values.ContainsKey(pi.Name))) values.Add(property.Name, property.GetValue(obj2, null)); // check for cached var key = Tuple.Create(obj1Type, obj2Type); if (!_cache.ContainsKey(key)) { // create assembly containing merged type var codeProvider = new CSharpCodeProvider(); var code = new StringBuilder(); code.Append("public class mergedType{ /n"); foreach (var propertyName in values.Keys) { // use object for property type, avoids assembly references code.Append( string.Format( "public object @{0}{{ get; set;}}/n", propertyName)); } code.Append("}"); var compilerResults = codeProvider.CompileAssemblyFromSource( new CompilerParameters { CompilerOptions = "/optimize /t:library", GenerateInMemory = true }, code.ToString()); _cache.Add(key, compilerResults.CompiledAssembly); } var merged = _cache[key].CreateInstance("mergedType"); Debug.Assert(merged != null, "merged != null"); // copy data foreach (var propertyInfo in merged.GetType().GetProperties()) { propertyInfo.SetValue( merged, values[propertyInfo.Name], null); } return merged; } }

Uso:

var merged = Extender.Extend(new { @class }, additionalViewData));

Gracias al autor original, Anthony Johnston!


en mi caso tengo un editor para un campo booleano que quiero ser una radio de sí / no. Uso la propiedad adicionalViewData para configurar el texto de sí / no para que se localice. Parece que funciona muy bien para mí!

Aquí está el código para mi editor personalizado Para:

@model bool? @{ var yes = ViewBag.TrueText ?? @Resources.WebSiteLocalizations.Yes; var no = ViewBag.FalseText ?? @Resources.WebSiteLocalizations.No; } <div class="title"> @Html.LabelFor(model => model) @Html.RequiredFor(model => model) </div> <div class="editor-field"> @Html.RadioButton("", "True", (Model.HasValue && Model.Value))@yes <br /> @Html.RadioButton("", "False", (Model.HasValue && Model.Value == false))@no <br /> @Html.ValidationMessageFor(model => model) </div> @Html.DescriptionFor(model => model)

Así es como llamas a este editor personalizado:

@Html.EditorFor(model => model.IsActive, new {TrueText = @Resources.WebSiteLocalizations.Active, FalseText = @Resources.WebSiteLocalizations.InActive})