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

时间:2021-09-10 15:26:50

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 Helper来扩展另一个html帮助器。通常,帮助器看起来像这样。

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

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

This works fine, but it has to be wrapped in some other HTML that is always the same. I wanted to encapsulate it for convenience, like this.

这工作正常,但它必须包含在其他一些始终相同的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());
        }

The line stringBuilder.Append(htmlHelper.TextAreaFor... is what I want to change. The CSS class that has to go there is always going to be present. So I would rather include it here. However I would like to be able to specify additional CSS classes in the top-level helper. So ...

该生产线stringBuilder.Append(htmlHelper.TextAreaFor ......是我想改变,有去的CSS类也总是会存在的。所以,我宁愿它包含在这里。不过,我想能够在*助手中指定其他CSS类。所以......

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

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

And the static css that will always be there get blanketed in through the Helper.

永远在那里的静态css通过Helper被覆盖。

Any ideas?

有任何想法吗?

4 个解决方案

#1


22  

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

首先,创建一个方法(最好的方法是创建一个扩展方法),通过类型反射将对象转换为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;
}

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

现在,使用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


50  

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

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

HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)

htmlAttributes is a an object

htmlAttributes是一个对象

#3


2  

@pszmyd: You can use the ViewDataDictionary that already takes an object in the constructor ex.

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

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).

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

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

我建议你从你的参数:object htmlAttributes你刚刚创建:var htmlAttr = new ViewDataDictionary (htmlAttributes);

then create custom extension methods to merge the attributes ex.

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

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

#4


0  

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

当我这样做时,我通常创建一个局部视图然后我使用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

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

  • use the Content: @ViewBag.Content
  • 使用内容:@ ViewBag.Content
  • test and use the default css if a custom one is not specified (@ViewBag.Css)
  • 如果未指定自定义css,请测试并使用默认css(@ 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

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

#1


22  

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

首先,创建一个方法(最好的方法是创建一个扩展方法),通过类型反射将对象转换为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;
}

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

现在,使用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


50  

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

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

HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)

htmlAttributes is a an object

htmlAttributes是一个对象

#3


2  

@pszmyd: You can use the ViewDataDictionary that already takes an object in the constructor ex.

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

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).

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

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

我建议你从你的参数:object htmlAttributes你刚刚创建:var htmlAttr = new ViewDataDictionary (htmlAttributes);

then create custom extension methods to merge the attributes ex.

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

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

#4


0  

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

当我这样做时,我通常创建一个局部视图然后我使用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

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

  • use the Content: @ViewBag.Content
  • 使用内容:@ ViewBag.Content
  • test and use the default css if a custom one is not specified (@ViewBag.Css)
  • 如果未指定自定义css,请测试并使用默认css(@ 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

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