I am trying to create an extension that renders a read-only drop down list. It appears that just slapping a readonly="true" attribute on the <select> element does not work, so I would like to render a disabled <select> element and a hidden input.
Here’s the code I have so far:
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> selectList,
object htmlAttributes,
bool isReadonly)
{
var values = new RouteValueDictionary(htmlAttributes);
if (isReadonly) values["disabled"] = "disabled";
var select = htmlHelper.DropDownListFor<TModel, TProperty>(expression, selectList, values);
var hiddenField = htmlHelper.HiddenFor<TModel, TProperty>(expression);
var bothFields = ?;
return bothFields;
}
What is the right way to render out multiple controls from a Razor extension?
Edited: Solution
Figured it out. Evidently you can just call ToString() on two elements and return that. As Jerad pointed out, the <select>‘s id must be modified so it’s not the same as the id of the hidden input. My final code looks like this:
public static MvcHtmlString DropDownListFor<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> selectList,
object htmlAttributes,
bool isReadonly)
{
if (isReadonly)
{
var values = new RouteValueDictionary(htmlAttributes);
values["disabled"] = "disabled";
values["id"] = (expression.Body as MemberExpression).Member.Name + "_Disabled";
var select = htmlHelper.DropDownListFor<TModel, TProperty>(expression, selectList, values);
var hiddenField = htmlHelper.HiddenFor<TModel, TProperty>(expression);
return new MvcHtmlString(select.ToString() + hiddenField.ToString());
}
else
{
return htmlHelper.DropDownListFor<TModel, TProperty>(expression, selectList, htmlAttributes);
}
}
I think the best approach would be to have an EditorTemplate that renders the two fields — from what I’ve seen w/ HtmlHelper extensions, extension methods typically return one HTML control.
For example, in your view:
And in your editor template:
Also, you’re going to end up with conflicting names on your form, no? Looks like both your hidden and select elements would be rendered with the same name.
If you really want to combine the two into one, I think you can simply concatenate the results of the two HTML elements, as these are just MvcHtmlString objects: