I’m not sure why ASP.NET MVC was shipped without a file input type for forms. Maybe it’ll come in MVC 2.0 or 3.0. Meanwhile, I created one. I spent two or three hours trying to figure out how to go from Object to IDictionary<String, Object> to follow the same ASP.NET MVC style where you have methods like:

TextBox(HtmlHelper, String, Object, IDictionary); TextBox(HtmlHelper, String, Object, Object);

which are essentially the same. The last argument is a dictionary of extra HTML attributes, like style=”float: left;”. The good thing about accepting Object Is that you can call it this way:

Html.TextBox("email", new { style="float: left;" })

which is very handy for forms. The bad thing is that it is a pain in the ass to do that hocus pocus in C# using reflection. Thankfully ASP.NET MVC is open source. I downloaded the source and after 15 minutes I got it working nicely (and without manually using reflection). Use the source Luke!

In a recent episode of Hansel Minutes podcast someone argued what was the value of releasing the code of ASP.NET MVC at all. Well, this is the value. You help developers, you build a better developing community.

Without further ado, here’s the code:

public static class HtmlHelperExtensions { /// <summary> /// Returns a file input element by using the specified HTML helper and the name of the form field. /// </summary> /// <param name="htmlHelper">The HTML helper instance that this method extends.</param> /// <param name="name">The name of the form field and the <see cref="member">System.Web.Mvc.ViewDataDictionary</see> key that is used to look up the validation errors.</param> /// <returns>An input element that has its type attribute set to "file".</returns> public static string FileBox(this HtmlHelper htmlHelper, string name) { return htmlHelper.FileBox(name, (object)null); } /// <summary> /// Returns a file input element by using the specified HTML helper, the name of the form field, and the HTML attributes. /// </summary> /// <param name="htmlHelper">The HTML helper instance that this method extends.</param> /// <param name="name">The name of the form field and the <see cref="member">System.Web.Mvc.ViewDataDictionary</see> key that is used to look up the validation errors.</param> /// <param name="htmlAttributes">An object that contains the HTML attributes for the element. The attributes are retrieved through reflection by examining the properties of the object. The object is typically created by using object initializer syntax.</param> /// <returns>An input element that has its type attribute set to "file".</returns> public static string FileBox(this HtmlHelper htmlHelper, string name, object htmlAttributes) { return htmlHelper.FileBox(name, new RouteValueDictionary(htmlAttributes)); } /// <summary> /// Returns a file input element by using the specified HTML helper, the name of the form field, and the HTML attributes. /// </summary> /// <param name="htmlHelper">The HTML helper instance that this method extends.</param> /// <param name="name">The name of the form field and the <see cref="member">System.Web.Mvc.ViewDataDictionary</see> key that is used to look up the validation errors.</param> /// <param name="htmlAttributes">An object that contains the HTML attributes for the element. The attributes are retrieved through reflection by examining the properties of the object. The object is typically created by using object initializer syntax.</param> /// <returns>An input element that has its type attribute set to "file".</returns> public static string FileBox(this HtmlHelper htmlHelper, string name, IDictionary<String, Object> htmlAttributes) { var tagBuilder = new TagBuilder("input"); tagBuilder.MergeAttributes(htmlAttributes); tagBuilder.MergeAttribute("type", "file", true); tagBuilder.MergeAttribute("name", name, true); tagBuilder.GenerateId(name); ModelState modelState; if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState)) { if (modelState.Errors.Count > 0) { tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName); } } return tagBuilder.ToString(TagRenderMode.SelfClosing); } }

Reviewed by Daniel Magliola. Thank you!