ASP.NET MVC Helpers,将两个对象htmlAttributes合并在一起

时间:2011-05-17 23:19:07

标签: c# .net asp.net-mvc-3

我有一种情况需要编写HTML Helper来扩展另一个html帮助器。通常,帮助器看起来像这样。

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

这样可以正常工作,但它必须包含在其他一些始终相同的HTML中。为了方便,我想封装它,就像这样。

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

我希望改变stringBuilder.Append(htmlHelper.TextAreaFor...行。必须去那里的CSS类是总是将出现。所以我宁愿把它包括在这里。但是,我希望能够在顶级助手中指定其他CSS类。所以...

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

永远存在的静态css会通过Helper覆盖。

有什么想法吗?

4 个答案:

答案 0 :(得分:50)

您可以使用标准MVC帮助程序方法执行此操作。

HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)

htmlAttributes是一个对象

答案 1 :(得分:22)

首先,创建一个方法(最好的方法是创建一个扩展方法),通过类型反射将对象转换为IDictionary:

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

现在,使用C#4.0 ExpandoObject,它允许在运行时添加属性。 你最终会得到这样的东西:

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

答案 2 :(得分:2)

@pszmyd:您可以使用已经在构造函数ex中获取对象的ViewDataDictionary。

//
// 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);

您正在尝试做的事情也很简单 - 合并键值。在html属性的情况下,它不是直截了当的。恩。 element可以包含多个类ex。 'blue dr ltr',用空格分隔,而style属性使用分号作为分隔符ex。 “宽度:200像素;字体大小:0.8em;”。您可以自行解析并检查值是否正确(不合并css类而不是用空格分割它们,与样式相同)。

我建议您使用参数:object htmlAttributes 你只需创建:var htmlAttr = new ViewDataDictionary<TModel>(htmlAttributes);

然后创建自定义扩展方法以合并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;
    }
}

这只是一个非常简单的实现,没有考虑重复的属性值或多个分隔符。但希望你明白这个想法!

答案 3 :(得分:0)

当我这样做时,我通常会创建一个局部视图 然后我使用RenderPartial:

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

我通常还会创建一个Model类,以避免使用上面示例中的魔术字符串(和ViewDataDictionary)。 在您的视图中,您可以

  • 使用内容:@ ViewBag.Content
  • 如果未指定自定义css,则测试并使用默认css(@ ViewBag.Css)

小注意:也可以更改调用TextAreaFor(或其他类似方法)时使用的默认模板。为此,请查看http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-3-default-templates.html