ASP.NET MVC Helpers, Merging two object htmlAttributes together

后端 未结 4 606
感情败类
感情败类 2021-02-01 03:46

I have a situation where I need to write an HTML Helper to extend another html helper. Normally, the helper would look like this.

@Html.TextAreaFo

相关标签:
4条回答
  • 2021-02-01 04:02

    When I've to do that, I usually create a partial view Then I use RenderPartial:

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

    I usually also create a Model class to avoid using magic string ( and ViewDataDictionary) like in the sample above. In your view you can

    • use the Content: @ViewBag.Content
    • test and use the default css if a custom one is not specified (@ViewBag.Css)

    A small note: it's also possible to change the default template used when calling TextAreaFor (or other similar method). For this, have a look at http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-3-default-templates.html

    0 讨论(0)
  • 2021-02-01 04:03

    You might be able to do so with the standard MVC helper method.

    HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)
    

    htmlAttributes is a an object

    0 讨论(0)
  • 2021-02-01 04:06

    First, create a method (the best would be to create an extension method) that converts an object to IDictionary via type reflection:

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

    Now, make use of C# 4.0 ExpandoObject, which allows adding properties at runtime. You would end up with something like this:

    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());
    }
    
    0 讨论(0)
  • @pszmyd: You can use the ViewDataDictionary that already takes an object in the 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);
    

    Also what you are trying to do is simple enough - merging key values. In the case of html attributes it is not straight forward. ex. element can contain multiple classes ex. 'blue dr ltr', separated by spaces, while the style attribute uses the semi-colon as delimiter ex. 'width:200px; font-size:0.8em;'. It is really up to you to parse and check that the values are correct (not merging css classes instead of splitting them with spaces, same with the style).

    I'd suggest from your parameter:object htmlAttributes you just create: var htmlAttr = new ViewDataDictionary<TModel>(htmlAttributes);

    then create custom extension methods to merge the attributes 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;
        }
    }
    

    This is only a really simple implementation not taking into account duplicate attribute values or multiple separators. But hope you get the idea!

    0 讨论(0)
提交回复
热议问题