Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Automatic Dropdown Lists for Razor Views

0.00/5 (No votes)
21 Jan 2018 1  
Using a metadata attribute and editor template for DropDown lists in Razor

Introduction

This article shows how we can apply a DropDownList metadata attribute to a View Model property, and have a DropdDownList rendered by the EditorFor HTML helper.

Background

I'm a big fan of runtime scaffolding in ASP.NET MVC. This is where you use e.g. the EditorFor HTML Helper to decide how to render an HTML element for a property of your view model. For example, if a property is marked with the DataType attribute and that indicates the property's DataType is DateTime, then it will automatically render a date picker. For integer properties, EditorFor renders an UpDown spinner control. If EditorFor works smoothly for all your properties, you can use a single EditorForModel element to render your whole view model into the view correctly.

Using the Code

Using this system involves new code and changed code in several areas of your MVC application. It is not just a library or plug in, although I have plans for version 2 to use Unity at these points where I modify the code.

public class DemoModel
{
    [DropDownList("LanguageSelect")]
    public int? LanguageId { get; set; }
    public SelectList LanguageSelect { get; set; } 
} 

Now when I use the Razor markup @EditorFor(m => m.LanguageId), I get a drop-down populated from the LanguageSelect list. I get this because the DropDownListAttrbute class attaches the select list name to the LanguageId model:

public class DropDownListAttribute : UIHintAttribute, IMetadataAware
{
    public DropDownListAttribute(string selectListName) : 
              base(KnownUiHints.DropDown, KnownPresentationLayers.Mvc, selectListName)
    {
        SelectListName = selectListName;
    }
    public string SelectListName { get; set; }
    public void OnMetadataCreated(ModelMetadata metadata)
    {
        var listProp = metadata.ContainerType.GetProperty(SelectListName);
        metadata.AdditionalValues[KnowMetadataKeys.SelectListName] = SelectListName;
    }
} 

All my view models derive from ViewModel, which offers a SelectListDictionary property:

private IDictionary<string,> _selectListdictionary;
public virtual IDictionary<string,> SelectListDictionary
{
    get
    {
        if (_selectListdictionary == null)
        {
            var props = GetType().GetProperties().Where(p => p.PropertyType == typeof(SelectList));
            _selectListdictionary = props.ToDictionary
                    (prop => prop.Name, prop => (SelectList)prop.GetValue(this, null));
        }
        return _selectListdictionary;
    }
}

In my base controller, I override the View method to pull the entire select list dictionary from the view model, and insert it into the view's viewdata, making it available for the editor template:

protected override ViewResult View(string viewName, string masterName, object model)
{
    var result = base.View(viewName, masterName, model);
    if ((model is ViewModel) && (!ViewData.ContainsKey(KnowMetadataKeys.ViewDataSelectLists)))
    {
        var vm = (ViewModel)model;
        result.ViewData.Add(KnowMetadataKeys.ViewDataSelectLists, vm.SelectListDictionary);
    }
    return result;
}

Said editor template:

@using Erisia.Constants
@{
    var list = (SelectList)ViewData.ModelMetadata.AdditionalValues
        [ViewData.ModelMetadata.AdditionalValues[KnowMetadataKeys.SelectListName].ToString()];
    var listWithSelected = new SelectList(list.Items, list.DataValueField, list.DataTextField, Model);
}
@Html.DropDownListFor(m => Model, listWithSelected, " - select - ")

Remarks

There are probably some better ways of doing this, and populating the select list inside the view model is probably not too kosher, but this little 'framework' has saved me so much time on so many projects, and I've just received a glowing compliment for it on Code Review beta. It's been way too long since I wrote an article and I thought sharing this would make a good one.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here