27 votes

ASP.NET MVC Aides, la Fusion de deux objets htmlAttributes ensemble

J'ai une situation où j'ai besoin d'écrire un Helper HTML pour prolonger un autre helper html. Normalement, l'aide devrait ressembler à ceci.

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

Cela fonctionne bien, mais il doit être enveloppé dans certains autres HTML c'est toujours la même. Je voulais de l'encapsuler pour des raisons de commodité, comme ceci.

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 ligne stringBuilder.Append(htmlHelper.TextAreaFor... est ce que je veux changer. La classe CSS qui doit y aller est toujours va être présent. Donc, je préfère l'inclure ici. Cependant, je voudrais être en mesure de spécifier d'autres classes CSS dans le haut-niveau de l'aide. Alors ...

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

Et la statique de la css qui sera toujours là obtenir recouvert par le Helper.

Des idées?

38voto

Noam Points 268

Vous pourriez être en mesure de le faire avec la norme MVC méthode d'assistance.

HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)

htmlAttributes est un objet

22voto

Piotr Szmyd Points 9465

Tout d'abord, créez une méthode (le mieux serait de créer une méthode d'extension), qui convertit un objet à IDictionary par type de réflexion:

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

Maintenant, faire usage de C# 4.0 ExpandoObject, qui permet d'ajouter des propriétés au moment de l'exécution. Vous vous retrouvez avec quelque chose comme ceci:

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

2voto

Schalk Points 286

@pszmyd: Vous pouvez utiliser la ViewDataDictionary qui déjà prend un objet dans le constructeur 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);

Aussi de ce que vous essayez de faire est assez simple de fusion de valeurs de clé. Dans le cas d'attributs html, il n'est pas simple. ex. l'élément peut contenir plusieurs classes ex. 'blue dr ltr', séparés par des espaces, tandis que l'attribut de style utilise le point-virgule comme séparateur ex. 'width:200px; font-size:0.8 em;'. C'est vraiment à vous de les analyser et de vérifier que les valeurs sont correctes (pas de fusion des classes css au lieu de les diviser avec des espaces, même avec le style).

Je te suggère de partir de votre paramètre:object htmlAttributes vous venez de créer: var htmlAttr = new ViewDataDictionary<TModel>(htmlAttributes);

ensuite, créer des méthodes d'extension pour fusionner les attributs 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;
    }
}

Ce n'est qu'une très simple de mise en œuvre de la non-prise en compte des valeurs d'attribut ou plusieurs séparateurs. Mais j'espère que vous obtenez l'idée!

0voto

Fabske Points 1408

Quand j'ai à faire, j'ai l'habitude de créer une vue partielle Puis-je utiliser RenderPartial:

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

J'ai l'habitude aussi de créer un Modèle de classe pour éviter d'utiliser de la magie de la chaîne ( et ViewDataDictionary) comme dans l'exemple ci-dessus. À votre avis, vous pouvez

  • utiliser le Contenu: @ViewBag.Contenu
  • tester et d'utiliser le css par défaut si un programme personnalisé n'est pas spécifié (@ViewBag.Css)

Une petite remarque: il est également possible de modifier le modèle par défaut utilisé lors de l'appel de TextAreaFor (ou autre méthode similaire). Pour cela, jetez un oeil à http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-3-default-templates.html

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X