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
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
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
You might be able to do so with the standard MVC helper method.
HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)
htmlAttributes
is a an object
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());
}
@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!