update tutorial tools packs net mvc microsoft language instalar asp c# .net asp.net-mvc-3

c# - tutorial - ASP.NET MVC Helpers, fusionando dos objetos htmlAttributes juntos



mvc c# tutorial (4)

Tengo una situación en la que necesito escribir un HTML Helper para extender otro helper html. Normalmente, el ayudante se vería así.

@Html.TextAreaFor(model => model.Content, new { @class = "some css", @data_bind = "some other stuff..." })

Esto funciona bien, pero tiene que estar envuelto en otro HTML que siempre es el mismo. Quería encapsularlo por conveniencia, así.

public static MvcHtmlString CondensedHelperFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) { var stringBuilder = new System.Text.StringBuilder(); var tag = new TagBuilder("div"); tag.AddCssClass("some_css"); stringBuilder.Append(toolbar.ToString(TagRenderMode.SelfClosing)); stringBuilder.Append(htmlHelper.TextAreaFor(expression, htmlAttributes)); // more tags and such... return new MvcHtmlString(stringBuilder.ToString()); }

La línea stringBuilder.Append(htmlHelper.TextAreaFor... es lo que quiero cambiar. La clase CSS que tiene que ir allí siempre estará presente. Así que prefiero incluirla aquí. Sin embargo, me gustaría poder Especifique clases CSS adicionales en el ayudante de nivel superior. Así que ...

@Html.CondensedHelperFor(model => model.Content, new { @class = "some_other_css" })

Y el css estático que siempre estará allí se cubrirá a través del Ayudante.

¿Algunas ideas?


@pszmyd: puede usar ViewDataDictionary que ya toma un objeto en el constructor ex.

// // Summary: // Initializes a new instance of the System.Web.Mvc.ViewDataDictionary class // by using the specified model. // // Parameters: // model: // The model. public ViewDataDictionary(object model);

Además, lo que estás tratando de hacer es lo suficientemente simple: fusionar valores clave. En el caso de los atributos html, no es sencillo. ex. elemento puede contener múltiples clases ex. ''blue dr ltr'', separado por espacios, mientras que el atributo de estilo usa el punto y coma como delimitador ex. ''ancho: 200px; tamaño de letra: 0.8em; ''. Depende de usted analizar y verificar que los valores sean correctos (no fusionar clases de CSS en lugar de dividirlas con espacios, lo mismo con el estilo).

Sugeriría de su parámetro: object htmlAttributes que acaba de crear: var htmlAttr = new ViewDataDictionary<TModel>(htmlAttributes);

luego crea métodos de extensión personalizados para fusionar los atributos ex.

public static class ViewDataDictionaryExtensions { public static ViewDataDictionary<TModel> MergeClasses<TModel>(this ViewDataDictionary<TModel> dict, string classes) { if (dict.ContainsKey("class")) dict["class"] += " " + classes; else dict.Add("class", classes); return dict; } public static ViewDataDictionary<TModel> MergeStyles<TModel>(this ViewDataDictionary<TModel> dict, string styles) { if (dict.ContainsKey("style")) dict["style"] += "; " + styles; else dict.Add("style", styles); return dict; } }

Esta es solo una implementación realmente simple que no tiene en cuenta valores duplicados de atributos o separadores múltiples. ¡Pero espero que entiendas la idea!


Cuando tengo que hacer eso, generalmente creo una vista parcial Luego uso RenderPartial:

@Html.Partial(MVC.Shared.MyPartialView_cshtml, new ViewDataDictionary() { { "Content", model.Content}, { "Css", "some_other_css"}, });

Por lo general, también creo una clase Model para evitar utilizar magic string (y ViewDataDictionary) como en el ejemplo anterior. En tu opinión puedes

  • usar el Contenido: @ ViewBag.Content
  • prueba y usa el CSS predeterminado si no se especifica uno personalizado (@ ViewBag.Css)

Una pequeña nota: también es posible cambiar la plantilla predeterminada utilizada al llamar a TextAreaFor (u otro método similar). Para esto, eche un vistazo a http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-3-default-templates.html


Es posible que pueda hacerlo con el método de ayuda MVC estándar.

HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)

htmlAttributes es un objeto


Primero, cree un método (lo mejor sería crear un método de extensión) que convierta un objeto a IDictionary a través de la reflexión de tipo:

public static IDictionary<string, object> ToDictionary(this object data) { if(data == null) return null; // Or throw an ArgumentNullException if you want BindingFlags publicAttributes = BindingFlags.Public | BindingFlags.Instance; Dictionary<string, object> dictionary = new Dictionary<string, object>(); foreach (PropertyInfo property in data.GetType().GetProperties(publicAttributes)) { if (property.CanRead) { dictionary.Add(property.Name, property.GetValue(data, null)); } } return dictionary; }

Ahora, use C # 4.0 ExpandoObject, que permite agregar propiedades en tiempo de ejecución. Usted terminaría con algo como esto:

public static MvcHtmlString CondensedHelperFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) { { var dictAttributes = htmlAttributes.ToDictionary(); var result = new ExpandoObject(); var d = result as IDictionary<string, object>; //work with the Expando as a Dictionary if(dictAttributes != null) { foreach (var pair in dictAttributes) { d[pair.Key] = pair.Value; } } // Add other properties to the dictionary d here // ... var stringBuilder = new System.Text.StringBuilder(); var tag = new TagBuilder("div"); tag.AddCssClass("some_css"); stringBuilder.Append(toolbar.ToString(TagRenderMode.SelfClosing)); stringBuilder.Append(htmlHelper.TextAreaFor(expression, result)); return new MvcHtmlString(stringBuilder.ToString()); }