I\'ve spent the majority of the past week knee deep in the new templating functionality baked into MVC2. I had a hard time trying to get a DropDownList template working. The b
This is my approach from this post in Code Project:
One Editor Template for all DropDownLists in ASP.Net MVC
Perfect. This is what I'm looking for. Thanks!
But your example model is simple model. How about a complex viewmodel like
public class MaintainServicePackageViewModel
{
public IEnumerable<ServicePackageWithOwnerName> ServicePackageWithOwnerName { get; set; }
public ServicePackageWithOwnerName CurrentServicePackage { get; set; }
public IEnumerable<ServiceWithPackageName> ServiceInPackage { get; set; }
}
public class ServicePackageWithOwnerName : ServicePackage
{
[UIHint("DropDownList")]
[DropDownList(DropDownListTargetProperty = "Owner")]
[DisplayNameLocalized(typeof(Resources.Globalization), "OwnerName")]
public IEnumerable<SelectListItem> OwnerName { get; set; }
}
The OwnerName is set to a dropdownlist, but it is not a direct element of the viewmodel instead it's a child element of ServicePackageWithOwnerName which is the element of the viewmodel. In such condition, there's no way to set the OwnerName value in the controller, how to fix this? Appreciate!
Regards
Jack
I think I found a solution to make it work when using Html.EditorForModel(); When using EditorForModel(), MVC uses Object.ascx to loop through all properties of the model and calls the corresponding template for each property in the model. ASP.Net MVC out of the box has Object.ascx in code, but you can create your own Object.ascx. Just create an EditorTemplates subfolder in your Shared View folder. Create an Object.ascx file there. (read this post for more information: http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-3-default-templates.html)
Here's my Object.ascx:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%@ Import Namespace="WebAppSolutions.Helpers" %>
<% if (ViewData.TemplateInfo.TemplateDepth > 1) { %>
<%= ViewData.ModelMetadata.SimpleDisplayText%>
<% }
else { %>
<% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm))) { %>
<% var htmlFieldName = Html.HtmlFieldNameFor(prop.PropertyName);%>
<% if (prop.HideSurroundingHtml) { %>
<%= Html.Editor(htmlFieldName)%>
<% }
else { %>
<div id="<%= htmlFieldName %>Container" class="editor-field">
<% if (!String.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString())) { %>
<%= Html.Label(prop.PropertyName, Html.HtmlDisplayName(prop.PropertyName), prop.IsRequired)%>
<% } %>
<%= Html.Editor(prop.PropertyName, "", htmlFieldName)%>
<%= Html.ValidationMessage(prop.PropertyName, "*") %>
</div>
<% } %>
<% } %>
<% } %>
I have some custome code in my WebAppSolutions.Helpers for HtmlFieldNameFor and HtmlDisplayName. These helpers retrieve data from attributes applied to properties in the view model.
public static String HtmlFieldNameFor<TModel>(this HtmlHelper<TModel> html, String propertyName)
{
ModelMetadata modelMetaData = GetModelMetaData(html, propertyName);
return GetHtmlFieldName(modelMetaData, propertyName);
}
public static String HtmlDisplayName<TModel>(this HtmlHelper<TModel> html, String propertyName)
{
ModelMetadata modelMetaData = GetModelMetaData(html, propertyName);
return modelMetaData.DisplayName ?? propertyName;
}
private static ModelMetadata GetModelMetaData<TModel>(HtmlHelper<TModel> html, String propertyName)
{
ModelMetadata modelMetaData = ModelMetadata.FromStringExpression(propertyName, html.ViewData);
return modelMetaData;
}
private static String GetHtmlFieldName(ModelMetadata modelMetaData, string defaultHtmlFieldName)
{
PropertyExtendedMetaDataAttribute propertyExtendedMetaDataAttribute = GetPropertyExtendedMetaDataAttribute(modelMetaData);
return propertyExtendedMetaDataAttribute.HtmlFieldName ?? defaultHtmlFieldName;
}
The key to getting this to work using EditorModelFor() is this (should be line 20 or so in Object.ascx above):
<%= Html.Editor(prop.PropertyName, "", htmlFieldName)%>
prop.PropertyName is the property in the ViewModel containing the list of data that will become the DropDownList. htmlFieldName is the name of the property that's hidden that the DropDownList property is replacing. Make sense?
I hope this helps you.