Here is the relevant code. Mind you, I did this in Notepad++ instead of copying in my code for my project at work. If I misspelled a class name in it, assume it is not misspelled in my code. No compile errors.
Model:
public class MyViewModel
{
public int SelectedSomething { get; set; }
public IList<int> Somethings { get; set; }
}
Controller:
public class MyController
{
public ActionResult Index()
{
var viewModel = new MyViewModel();
viewModel.Somethings = Enumerable.Range(1, 12).ToList();
return View(viewModel);
}
}
View (Razor):
@Html.DropDownListFor(x => x.SelectedSomething, new SelectList(Model.Somethings, Model.SelectedSomething), "Select an Option")
This will “work”, but won’t set values on any of the rendered option elements in the select:
<select>
<option value="">Select an Option</option>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
<option>10</option>
<option>11</option>
<option>12</option>
</select>
I know of the SelectList constructor that takes in the property names to use to render the value and text in the option elements, but it is of no use (that I can see) when using a collection of a primitive type like an int.
Question:
I know I can change my view model’s IList<int> to IList<SelectListItem>, and create SelectListItem objects to represent each int, but that seems a bit ridiculous (as I think the constructor shouldn’t leave the values blank, and should default to using the text). Am I missing something obvious here, or am I going to have to move away from using primitive types here?
I solved this problem using extension methods, allowing you to write this in your views:
For an int collection you may need to use
n => n.ToString()as your name selector.The beauty of this approach is that you can use it for any kind of enumerable. In your example (with a list of integers), we simply specify lambdas that return the element itself, which will cause the text and value to be set to the same value in the rendered option list.
If you want to avoid having to import the namespace of the extensions class in your Web.config, declare a read-only property on your ViewModel that returns the SelectList, and then access that from your view.
The Web.config part would look something like this (note that you need to create a line for both the assembly and one for the namespace). Also note that your Views folder contains a separate Web.config that may override or extend the definitions in your root Web.config.
This is the implementation for the extension methods: