patterncsharpMinor
Implementing a generic DropDownList attribute and templating solution
Viewed 0 times
generictemplatingdropdownlistattributeandsolutionimplementing
Problem
My goal was to be able to render my view models using a standard
There were plenty of resources for creating custom attributes and custom editor templates. However I ran into a wall when creating the
Here is the reference I used to create the custom
Here is the problem I ran into with getting the current instance of the view model.
It turns out using reflection here to access the view model is not safe. Depending on how you iterate over the property metadata the view model can sometimes be null.
Here is my implementation using the resource referenced above. This is a simple version that only uses a
View Model:
DropDownListAttribute Implementation
Custom HTML Helper used to render elements that need access to the view model instance
``
Object.cshtml editor and display template. In order to do so I needed to be able to always call Html.Editor(propertyName) or Html.Display(propertyName) in order to render the HTML element.There were plenty of resources for creating custom attributes and custom editor templates. However I ran into a wall when creating the
DropDownListAttribute and template since I needed to obtain the values for the select list as well as set the selected item based on the current instance of the view model.Here is the reference I used to create the custom
ModelMetadataProvider and custom attributes.Here is the problem I ran into with getting the current instance of the view model.
It turns out using reflection here to access the view model is not safe. Depending on how you iterate over the property metadata the view model can sometimes be null.
Here is my implementation using the resource referenced above. This is a simple version that only uses a
List for the DropDownList. Using this example it would be trivial to use dictionaries and set the text/value properties:View Model:
public class PhoneNumberViewModel
{
// ... normal properties and attributes
[DropDownList("ContactTypes")] // ContactTypes
{
get
{
return new List()
{
"Home", "Work", "Mobile"
}
}
}
}DropDownListAttribute Implementation
public class DropDownListAttribute : MetadataAttribute
{
private string _items;
public DropDownListAttribute(string items)
{
_items = items;
}
public override void Process(System.Web.Mvc.ModelMetadata modelMetaData)
{
modelMetaData.AdditionalValues.Add("Items", _items);
modelMetaData.TemplateHint = TemplateHints.DropDownList; // Just a const string
}
}Custom HTML Helper used to render elements that need access to the view model instance
``
Solution
Why not just do this?
I didn't test the code to get the items. But I'm assuming that you had that part working.
I also authored my own HTML Helper Library if you want check out some other examples.
https://bitbucket.org/grcodemonkey/buildmvc
public class PhoneNumberViewModel
{
// ... normal properties and attributes
[DropDownList("ContactTypes")] // ContactTypes
{
get { return new[] { "Home", "Work", "Mobile" }.ToSelectList(); }
}
}
public static class ExtensionsForSelectListItem
{
public static IEnumerable ToSelectList(this IEnumerable items)
{
return items.Select(x => new SelectListItem { Value = x });
}
public static MvcHtmlString ContextualEditor(this HtmlHelper html, string expression)
{
var model = html.ViewData.Model;
var propertyMetadata = ModelMetadata.FromStringExpression(expression, html.ViewData);
switch (propertyMetadata.TemplateHint)
{
case TemplateHints.DropDownList:
var items = (IEnumerable)model.GetType().GetProperty((string)propertyMetadata.AdditionalValues["Items"]).GetValue(model, null);
return html.DropDownList(expression, items);
default:
return System.Web.Mvc.Html.EditorExtensions.Editor(html, expression);
}
}
}I didn't test the code to get the items. But I'm assuming that you had that part working.
I also authored my own HTML Helper Library if you want check out some other examples.
https://bitbucket.org/grcodemonkey/buildmvc
Code Snippets
public class PhoneNumberViewModel
{
// ... normal properties and attributes
[DropDownList("ContactTypes")] // <-- This is the important one!
public string ContactType { get; set; }
public static IEnumerable<SelectListItem> ContactTypes
{
get { return new[] { "Home", "Work", "Mobile" }.ToSelectList(); }
}
}
public static class ExtensionsForSelectListItem
{
public static IEnumerable<SelectListItem> ToSelectList(this IEnumerable<string> items)
{
return items.Select(x => new SelectListItem { Value = x });
}
public static MvcHtmlString ContextualEditor(this HtmlHelper html, string expression)
{
var model = html.ViewData.Model;
var propertyMetadata = ModelMetadata.FromStringExpression(expression, html.ViewData);
switch (propertyMetadata.TemplateHint)
{
case TemplateHints.DropDownList:
var items = (IEnumerable<SelectListItem>)model.GetType().GetProperty((string)propertyMetadata.AdditionalValues["Items"]).GetValue(model, null);
return html.DropDownList(expression, items);
default:
return System.Web.Mvc.Html.EditorExtensions.Editor(html, expression);
}
}
}Context
StackExchange Code Review Q#4899, answer score: 3
Revisions (0)
No revisions yet.