ASP.net MVC-Plantilla de pantalla para una colección
asp.net-mvc asp.net-mvc-3 (4)
En mi pregunta sobre no obtener resultados de vistas, en realidad tengo un ejemplo de cómo crear una plantilla de un modelo con una colección de modelos secundarios y hacer que todos se procesen.
Plantillas de pantalla ASP.NET - Sin salida
Esencialmente, necesita crear un modelo que subclasifique la List<T>
o la Collection<T>
y use esto:
@model ChildModelCollection
@foreach (var child in Model)
{
Html.DisplayFor(m => child);
}
En su plantilla para que el modelo de recopilación itere y represente a los niños. Cada niño necesita escribir fuertemente, por lo que también puede crear sus propios tipos de modelo para los artículos y tener plantillas para ellos.
Entonces para la pregunta OP:
public class ChildModelCollection : Collection<ChildModel> { }
Hará un modelo fuertemente tipado que es una colección que se puede resolver en una plantilla como cualquier otra.
Tengo el siguiente modelo en MVC:
public class ParentModel
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public IEnumerable<ChildModel> Children { get; set; }
}
Cuando quiero mostrar todos los elementos secundarios para el modelo principal, puedo hacer lo siguiente:
@Html.DisplayFor(m => m.Children)
Luego puedo crear una plantilla de visualización ChildModel.cshtml y DisplayFor iterará automáticamente sobre la lista.
¿Qué sucede si quiero crear una plantilla personalizada para IEnumerable?
@model IEnumerable<ChildModel>
<table>
<tr>
<th>Property 1</th>
<th>Property 2</th>
</tr>
...
</table>
¿Cómo puedo crear una plantilla de visualización que tenga un tipo de modelo de IEnumerable<ChildModel>
y luego llamar a @Html.DisplayFor(m => m.Children)
sin quejarse de que el tipo de modelo sea incorrecto?
Esto es en respuesta al comentario de Maslow. Esta es mi primera contribución a SO, por lo que no tengo suficiente reputación para comentar, de ahí la respuesta como respuesta.
Puede establecer la propiedad ''TemplateHint'' en el ModelMetadataProvider. Esto conectaría automáticamente cualquier IEnumerable a una plantilla que especifique. Lo intenté en mi proyecto. Código a continuación -
protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(CachedDataAnnotationsModelMetadata prototype, Func<object> modelAccessor)
{
var metaData = base.CreateMetadataFromPrototype(prototype, modelAccessor);
var type = metaData.ModelType;
if (type.IsEnum)
{
metaData.TemplateHint = "Enum";
}
else if (type.IsAssignableFrom(typeof(IEnumerable<object>)))
{
metaData.TemplateHint = "Collection";
}
return metaData;
}
Básicamente, reemplaza el método ''CreateMetadataFromPrototype'' del ''CachedDataAnnotationsModelMetadataProvider'' y registra su tipo derivado como el ModelMetadataProvider preferido.
En su plantilla, no puede acceder directamente a ModelMetadata de los elementos en su colección. Utilicé el siguiente código para acceder a ModelMetadata para los elementos de mi colección:
@model IEnumerable<object>
@{
var modelType = Model.GetType().GenericTypeArguments[0];
var modelMetaData = ModelMetadataProviders.Current.GetMetadataForType(null, modelType.UnderlyingSystemType);
var propertiesToShow = modelMetaData.Properties.Where(p => p.ShowForDisplay);
var propertiesOfModel = modelType.GetProperties();
var tableData = propertiesOfModel.Zip(propertiesToShow, (columnName, columnValue) => new { columnName.Name, columnValue.PropertyName });
}
En mi opinión, simplemente llamo @ Html.DisplayForModel () y la plantilla se carga. No es necesario especificar ''UIHint'' en los modelos.
Espero que esto tenga algún valor.
La "respuesta válida" real es -IMHO- que no responde correctamente la pregunta. Creo que el OP está buscando una forma de tener una plantilla de lista que se active sin especificar el UIHint.
Las cosas mágicas casi hacen el trabajo
Algunas cargas mágicas la vista correcta para un tipo específico .
Algo más de magia carga la misma vista para una colección de un tipo específico.
Debe haber algo de magia que repita la misma vista para una colección de un tipo especificado.
Cambiar el comportamiento real?
Abra su desensamblador favorito. La magia ocurre en System.Web.Mvc.Html.TemplateHelpers.ExecuteTemplate
. Como puede ver, no hay puntos de extensibilidad para cambiar el comportamiento. Tal vez una solicitud de extracción a MVC puede ayudar ...
Ve con la magia real
Se me ocurrió algo que funciona. Cree una plantilla de visualización ~/Views/Shared/DisplayTemplates/MyModel.cshtml
.
Declara el modelo como tipo de object
.
Si el objeto es una colección, itere y renderice la plantilla nuevamente. Si no es una colección, entonces muestra el objeto.
@model object
@if (Model is IList<MyModel>)
{
var models = (IList<MyModel>)Model;
<ul>
@foreach (var item in models)
{
@Html.Partial("DisplayTemplates/MyModel", item)
}
</ul>
} else {
var item = (MyModel)Model;
<li>@item.Name</li>
}
}
Ahora DisplayFor
funciona sin UIHint
.
Me gusta esto:
@Html.DisplayFor(m => m.Children, "YourTemplateName")
o así:
[UIHint("YourTemplateName")]
public IEnumerable<ChildModel> Children { get; set; }
donde obviamente tendrías ~/Views/Shared/DisplayTemplates/YourTemplateName.cshtml
:
@model IEnumerable<ChildModel>
<table>
<tr>
<th>Property 1</th>
<th>Property 2</th>
</tr>
...
</table>