asp.net mvc - una - ¿Taquigrafía para crear un ViewDataDictionary con un modelo y elementos ViewData?
renderaction mvc (5)
Creé un método de extensión en HtmlHelper para copiar los nombres de propiedades y los valores de un objeto anónimo a un ViewDataDictionary.
Muestra
Html.RenderPartial("SomePartialView", MyModelObject, new { SomeDisplayParameter = true })
Extensión HtmlHelper
public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, object model, object viewData)
{
var vdd = new ViewDataDictionary(model);
foreach (var property in viewData.GetType().GetProperties()) {
vdd[property.Name] = property.GetValue(viewData);
}
htmlHelper.RenderPartial(partialViewName, vdd);
}
¿Hay alguna forma de crear un ViewDataDictionary
con un modelo y propiedades adicionales con una sola línea de código? Estoy tratando de hacer una llamada RenderPartial
a una vista fuertemente tipificada al ensamblar tanto el modelo como algunas propiedades de configuración de pantalla adicionales sin ensamblar explícitamente el ViewDataDictionary en varias líneas. Parece que sería posible dada la sobrecarga RenderPartial
toma tanto un object
modelo como un ViewDataDictionary
pero parece que simplemente ignora el ViewDataDictionary
cuando ambos se completan.
// FAIL: This will result in ViewData being a ViewDataDictionary
// where Model = MyModelObject and there are no other parameters available.
this.Html.RenderPartial("SomePartialView", MyModelObject, new ViewDataDictionary(new { SomeDisplayParameter = true }));
Encontré a alguien más con el mismo problema , pero su solución es el mismo concepto multilínea que encontré: cree un ViewDataDictionary
discreto con el modelo, agregue los nuevos parámetros y utilícelos en la llamada RenderPartial
.
var SomeViewData = new ViewDataDictionary(MyModelObject);
SomeViewData.Add("SomeDisplayParameter", true);
this.Html.RenderPartial("SomePartialView", SomeViewData);
Siempre puedo ChainedAdd
esa lógica en un método ChainedAdd
que devuelve un diccionario duplicado con el nuevo elemento agregado, pero parece que me falta alguna forma de crear un ViewDataDictionary
que haría esto por mí (y eso es un poco más general que yo estaba esperando).
this.Html.RenderPartial("SomePartialView", new ViewDataDictionary(MyModelObject).ChainedAdd("SomeDisplayParameter", true));
public static ViewDataDictionaryExtensions {
public static ViewDataDictionary ChainedAdd(this ViewDataDictionary source, string key, object value) {
return source.ChainedAdd(new KeyValuePair<string,object>(key, value));
}
public static ViewDataDictionary ChainedAdd(this ViewDataDictionary source, KeyValuePair<string, object> keyAndValue) {
ViewDataDictionary NewDictionary = new ViewDataDictionary(source);
NewDictionary.Add(keyAndValue);
return NewDictionary;
}
}
Además, al intentar ensamblar un ViewDataDictionary
con un Model
y ModelState
explícitos, simplemente se genera un error de compilación porque el ModelState es de solo lectura.
// FAIL: Compilation error
this.Html.RenderPartial("SomePartialView", new ViewDataDictionary { Model = MyModelObject, ModelState = new ViewDataDictionary( new { SomeDisplayParameter = true }});
RESPUESTA (S): Parece que Craig y yo terminamos encontrando dos sintaxis separadas que harán el trabajo. Definitivamente estoy sesgado en este caso, pero me gusta la idea de configurar el modelo primero y "decorarlo" después.
new ViewDataDictionary(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };
new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
{ Model = MyModelObject };
Por supuesto, todavía estaría girando mis ruedas sin su respuesta [finalmente puntual], por lo que, el círculo obtiene el cuadrado.
Esto es lo que funcionó para mí en el viejo estilo mvc aspx view:
<% Html.RenderPartial("ContactPartial", Model.ContactFactuur, new ViewDataDictionary(this.ViewData ) { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Factuur" } }); %>
Lo que ocurre aquí es que en el constructor uso el viewdata actual "nuevo ViewDataDictionary (this.ViewData)", que es un viewdatadictionary que contiene el estado del modelo que necesito para los mensajes de validación.
Use un inicializador de objetos y inicializadores de colección:
new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
{
Model = MyModelObject
}
El ViewDataDictionary interno obtiene su colección inicializada, luego esto rellena el ViewDataDictionary "real" usando la sobrecarga del constructor que toma ViewDataDictionary en lugar de un objeto. Finalmente, el inicializador de objetos establece el modelo.
Luego simplemente pase todo el conjunto sin configurar MyModelObject por separado:
this.Html.RenderPartial("SomePartialView", null,
new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
{ Model = MyModelObject });
Utilizando la respuesta de Craig como punto de partida, ni siquiera sabía que podría combinar una llamada de constructor y un inicializador de objetos. Me topé con este snippet de Palermo que conduce a una combinación que funciona. Utiliza algún tipo de diccionario abreviado que de alguna manera termina poblando ModelState
cuando lo consume el inicializador de objetos ViewDataDictionary.
new ViewDataDictionary(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };
// Of course, this also works with typed ViewDataDictionary objects (what I ended up using)
new ViewDataDictionary<SomeType>(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };
Todavía no veo cómo funciona esto dado que no puede establecer ModelState
explícitamente en un inicializador, pero parece mantener tanto el objeto del modelo original como los parámetros "anexados" para la vista. Definitivamente, hay una serie de otras permutaciones de esta sintaxis que no funcionan (no puede combinar el modelo con el diccionario en un solo objeto o usar la sintaxis del inicializador de objetos para los valores del diccionario), pero la versión anterior parece funcionar.
Vine aquí con la misma pregunta.
Lo que pensé que podría funcionar era esto (perdón por la sintaxis de VB Razor)
@Code Html.RenderPartial("Address", Model.MailingAddress, New ViewDataDictionary(New With {.AddressType = "Mailing Address"}))End Code
Pero, por supuesto, obtienes este error en tiempo de ejecución:
System.InvalidOperationException no fue manejado por el código de usuario
Mensaje = El elemento modelo que se pasó al diccionario es del tipo ''VB $ AnonymousType_1`1 [System.String]'', pero este diccionario requiere un elemento modelo del tipo ''ViewModel.Address''.
Pero lo que encontré, es que lo que realmente quería era usar la Plantilla de Editor de todos modos.
En lugar del uso de RenderPartial:
@Html.EditorFor(Function(model) model.MailingAddress, "Address", New With {.AddressType = "Mailing Address"})
Una plantilla de editor es solo una vista parcial que vive
~ / Views / {Model | Shared} /EditorTemplates/templatename.vbhtml
Mi plantilla para Dirección es una vista parcial muy tipográfica, pero el método EditorFor ofrece la posibilidad de agregar elementos de datos de vista adicionales fácilmente con un objeto anon.
En el ejemplo anterior no tuve que incluir el nombre de la plantilla "Dirección", ya que MVC buscaría una plantilla con el mismo nombre que el tipo de modelo.
También puede anular la plantilla de visualización de la misma manera.