Having returned to this problem after a few months I've added my current best answer below.
In the original question I was still looking for a simple way to achieve a generic DropDown but the title was more closely tied to the specific error I was then facing.
I've amended the title to reflect the answer more closely. Hopefully this might help someone.
Original Question:
Generic Editor Template for DropDownListFor throws Cannot convert type error
I'm trying to create a generic template for a drop down list using ideas lifted from the follwing post:
Move Html DropDownListFor into Editor Template
I've created a DropDownHelper class:
public class DDLOptions<T>
{
public T Value { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
I've amended the Controller from this:
public ActionResult Create()
{
var model = new FilmEditViewModel();
FilmDropDownViewModel films = new FilmDropDownViewModel
{
Items = _repo.GetSelect(),
};
model.filmName = films;
return View(model);
}
...to this:
public ActionResult Create()
{
var model = new FilmEditViewModel();
DDLOptions<FilmDropDownViewModel> films
= new DDLOptions<FilmDropDownViewModel>
{
Items = _repo.GetSelect()
};
model.filmName = films;
return View(model);
}
This is throwing the following error:
Cannot implicitly convert type
'BootstrapSupport.DDLOptions<FilmStore.ViewModels.FilmDropDownViewModel>'
to 'FilmStore.ViewModels.FilmDropDownViewModel'
I'm also having difficulty working out how to amend the Editor Template to work with the modified DDLOptions
class.
There is a way to create a generic DropDown which I've mangled together from a few pointers on StackOverflow and this article on CodeProject. Comments on whether this follows best practice would be appreciated.
I use both an AddList and an EditList to allow for a selected item on the EditList and some jQuery based on html class attributes. The generic EditList is created as follows:
Models
I have a viewmodel for any DropDown that fits with the generic pattern and then a ViewModel for the entity I'm returning. Annotations are held in a validation file.
DropDown ViewModel
public class DropDownViewModel
{
public IEnumerable<SelectListItem> Items { get; set; }
}
Entity ViewModel
public partial class OrganisationEditViewModel
{
public int entityID { get; set; }
public string entityName { get; set; }
public DropDownViewModel entityTypeID { get; set; }
public string notes { get; set; }
}
Validation
[MetadataTypeAttribute(typeof(OrganisationEditViewModelMetaData))]
public partial class OrganisationEditViewModel
{
}
public class OrganisationEditViewModelMetaData
{
[Key]
[ScaffoldColumn(false)]
[HiddenInput(DisplayValue = false)]
public int entityID { get; set; }
[Required]
[Display(Name = "Organisation")]
public string entityName { get; set; }
[Required]
[Display(Name = "Entity Type")]
[UIHint("_dropDownEdit")]
public DropDownViewModel entityTypeID { get; set; }
[Display(Name = "Notes")]
public string notes { get; set; }
}
Editor Template
The UIHint annotation on the ViewModel points to an Editor Template. I'm using chosen.js on my lookups, hence the html attributes.
@model WhatWorks.ViewModels.DropDownViewModel
@Html.DropDownList("", Model.Items, new { @class = "chosen chosenLookup" })
Controller
The controller queries the current entity to get the selected string for the EditList. The DropDown is called from the GetEditList<O>
function in the repository. The ViewModel is then mapped via Automapper (GetUpdate(id)
).
public ActionResult Edit(int id = 0)
{
var query = _repo.GetByID(id);
string selected = query.tEntityType.entityTypeID.ToString();
DropDownViewModel entityType = new DropDownViewModel
{
Items = _repo.GetEditList<tEntityType>("entityType", "entityTypeID", selected)
};
OrganisationEditViewModel a = GetUpdate(id);
a.entityTypeID = entityType;
if (a == null)
{
return HttpNotFound();
}
return View(a);
}
Repository
The generic DropDown repository method gets an IEnumerable
set of data from the calling type <O>
(tEntityType
in this case). The return
method converts everything into strings and checks for the selected item.
//generic Edit dropdown
public IEnumerable<SelectListItem> GetEditList<O>(string text, string value, string selected)
where O : class
{
IEnumerable<O> result = context.Set<O>();
var query = from e in result
select new
{
Value = e.GetType().GetProperty(value).GetValue(e, null),
Text = e.GetType().GetProperty(text).GetValue(e, null)
};
return query.AsEnumerable()
.Select(s => new SelectListItem
{
Value = s.Value.ToString(),
Text = s.Text.ToString(),
Selected = (selected == s.Value.ToString() ? true : false)
});
}
View
Finally, the view renders the DropDown via a standard Html.Editor
which picks up the Editor Template from the UIHint
annotation on the validation file.