How to Write a Simple Html.Dropdownlistfor()

How to write a simple Html.DropDownListFor()?

See this MSDN article and an example usage here on Stack Overflow.

Let's say that you have the following Linq/POCO class:

public class Color
{
public int ColorId { get; set; }
public string Name { get; set; }
}

And let's say that you have the following model:

public class PageModel 
{
public int MyColorId { get; set; }
}

And, finally, let's say that you have the following list of colors. They could come from a Linq query, from a static list, etc.:

public static IEnumerable<Color> Colors = new List<Color> { 
new Color {
ColorId = 1,
Name = "Red"
},
new Color {
ColorId = 2,
Name = "Blue"
}
};

In your view, you can create a drop down list like so:

<%= Html.DropDownListFor(n => n.MyColorId, 
new SelectList(Colors, "ColorId", "Name")) %>

MVC3 DropDownListFor - a simple example?

You should do like this:

@Html.DropDownListFor(m => m.ContribType, 
new SelectList(Model.ContribTypeOptions,
"ContribId", "Value"))

Where:

m => m.ContribType

is a property where the result value will be.

Create simple Html.DropDownList or DropDownListFor

Start by adding to your ViewModel the following 2 properties:

  1. SelectedClientId: which stores the selected value
  2. ClientItems: stores the collection of SelectListItems which populates your drop down.

E.G.

public class ClientViewModel
{
public List<Client> Clients;

public int SelectedClientId { get; set; } // from point 1 above
public IEnumerable<SelectListItem> ClientItems // point 2 above
{
get { return new SelectList(Clients, "Id", "Name");}
}
}

Then on your View index.cshtml you would add the following:

@model ClientViewModel

@Html.DropDownListFor(m => m.SelectedClientId, Model.ClientItems)

@Html.DropDownListFor Basic Usage


Part 1: Explaining Lambda magic

You're not alone. Lambdas + MVC make coding wicked easy, but they also make it seem rather magical (due to the abstraction).

Looking at the first param of the DropDownListFor Method [...] Do I understand correctly that the "m" represents the instance of the model class that was passed into the View?

Yes.

HTML Helpers + Lambdas in Razor are simple and magical because they are taking a shortcut and not actually revealing to you what's going on.

First, let's take a look at @Html. We know @ begins our Razor syntax and then Html is magic. Actually, Html is an HtmlHelper object that is automatically initialised and given to you. More specifically it's an HtmlHelper<TModel>. The Razor HTML editor is smart and knows ahead of time what type TModel will be based on what you've said this view's model type is. That's why you have so much intellisense.

When you type

@model IEnumerable<BusinessLayer.Employee>

The editor reads that and makes an assumption now that the HTML Helper will be HtmlHelper<IEnumerable<BusinessLayer.Employee>>. When it comes time to compile the page, the HTML helper will in fact be that type (of course).

So what about DropDownListFor or any other extension method?

Looking at DropDownListFor we can see that it's actually DropDownListFor<TModel, TProperty>. We now understand that TModel is our view's model type. DropDownListFor knows about this because the method is extending HtmlHelper<TModel>. Therefore DropDownListFor knows that the incoming type is whatever type is defined at the top of the view. What about TProperty?

TProperty is the return type. That's the type that you specify when you select your property. The view gives HtmlHelper the model type, who then gives DropDownListFor the model type and now that you're in a lambda expression: (x => x.Property) you select the property which then selects the type for you; in this case a string (Gender).

Part 2: Your problem

It's hard to answer your question without knowing exactly what you're trying to achieve. Do you have a collection of Employees and you want to display multiple gender dropdown boxes for each and then save them?

When dealing with collections it's a little more tricky, since MVC doesn't handle them well out of the box; it's usually best to write your own solutions according to what you need done.

I don't really like the idea of a single view getting a collection of things on a page. Consider this piece of logic:

Your way:

  • Get collection of Employees
  • Give Employees to a view
  • Have view create a single SelectListItem
  • Loop through and display the SelectList for each Employee

My way

  • Get collection of Employees
  • Give employees to a view
  • Have the view loop through and pass each Employee to another view (partial)
  • Each partial deals with a single Employee

With MVC do try to break things down into small, logical parts. Views should be stupid; meaning they shouldn't really do any logic or thinking. Things become easier this way and it makes sure that your views don't grow into monsters.

Example solution walk-through

Try this:

Create a new PartialView called _EmployeeGender.cshtml in the same folder as the view you're using.

Use this code

_EmployeeGender.cshtml

@model BusinessLayer.Employee

<tr>
<td>
@{

var listItems = new List<SelectListItem>()
{
new SelectListItem {Text = "Male", Value = "M"},
new SelectListItem {Text = "Female", Value = "F"}
};

@Html.DropDownListFor(m => m.Gender, listItems, string.Empty)
}

</td>
</tr>

Your original view

@model IEnumerable<BusinessLayer.Employee>
@{
ViewBag.Title = "Employees";
}

<h2>Employees</h2>

<table>
@foreach (var employee in Model)
{
Html.RenderPartial("_EmployeeGender", employee);
}
</table>

Results

Let's have a look at our generated HTML now:

<table>
<tr>
<td>
<select id="Gender" name="Gender"><option value=""></option>
<option selected="selected" value="M">Male</option>
<option value="F">Female</option>
</select>

</td>
</tr><tr>
<td>
<select id="Gender" name="Gender"><option value=""></option>
<option value="M">Male</option>
<option selected="selected" value="F">Female</option>
</select>

</td>
</tr></table>

We can see that there is now a blank selection and that our dropdown boxes are automatically selected with the Employee's values (I create one male and one female employee).

Do Note that the id and name HTML attributes are the same. If you want to submit a form with these values then it will require more work. But this is a reasonable starting point for you.

MVC DropDownListfor() Basics

You have at least two options:

1.) Add a list, array, or any other collection type of cities to your model

2.) Add a SelectList property to your model

Option 1 can be something as simple as an array of strings, or can be, say, an IEnumerable of City objects. You would then need to transform this property to a collection of SelectListItem objects in the view as part of the DropDownList binding.

Option 2 has the advantage of being capable of direct binding to the DropDownList, but requires that you construct the list within the action method.

Then end result is the same, it's just a matter of how pedantic you want to be about SoC.

For example (assuming you add a property called Cities):

@Html.DropDownListFor(m=>m.City, Model.Cities.Select(city => new SelectListItem()
{
Text = city,
Value = city,
Selected = city == Model.City
})

EDIT:

To answer your comment, I have to make some assumptions. I will assume you have a model called EmployeeModel. This model has a property, City, that is a plain string. So, this is a partial of your model, as I assume it to be:

public class EmployeeModel
{
public string City { get; set; }

// ... other properties ...
}

So, if you need to add a property for binding to your dropdown, you would do one of the following:

public class EmployeeModel
{
public string City { get; set; }

public IEnumerable<string> Cities { get; set; }

// ... other properties ...
}

or

public class EmployeeModel
{
public string City { get; set; }

public SelectList Cities { get; set; }

// ... other properties ...
}

This new property will contain the list of cities that you allow your user(s) to pick from.

If you choose the first option, you load the IEnumerable from your datastore, and then use the first example above in your view, which uses LINQ to project each string in the Cities property into a new SelectListItem object.

If you go with the second option, you build a SelectList in the action prior to passing the model to the view. This isn't terribly difficult, as the class provides a constructor that takes an IEnumerable (your list of cities) and the "selected value," which will be the City property (see http://msdn.microsoft.com/en-us/library/dd460123%28v=vs.108%29.aspx). Your code would look something like:

model.Cities = new SelectList(GetCities(), model.City);

This, of course, assumes you have a helper method (GetCities()) to load your cities from wherever they are stored. Your view then would have something like this:

@Html.DropDownListFor(m=>m.City, model.Cities)

The view engine then uses these SelectListItems to build the <select> element and it's <option> elements.

Adding a class to HTML.DropDownListFor

Four of the DropDownListFor overloads have HTML Attributes as their last parameter. This one is probably the easiest. Modifying your example, it becomes:

@Html.DropDownListFor(model => model.Business, 
new SelectList(
new List<Object>{
new { ... },
new { ... },
new { ... }
},
"value",
"text",
2),
new {
@class = "myclass"
})

How to choose displayed text on Html.DropDownListFor

Ok, you need to actually pass a list of possible values to the dropdown list, for example like this:

@Html.DropDownListFor(model => model.ArtystaID, new SelectList(Model.Artysci, "ArtystaID", "Nazwisko", 0))

It says: DropDownList setting models ArtystaID field, which is populated by models Artysci field, which is supposed to contain items that have key under ArtystaID field and display text under Nazwisko field (so, your Artysta class).

Now, your class doesn't have Artysci field, so you have to create it:

public partial class Artysta
{
public Artysci()
{
this.SpektaklArtysta = new HashSet<SpektaklArtysta>();
}

[Key]
public int ArtystaID { get; set; }

public string Imie { get; set; }

public string Nazwisko { get; set; }

public List<Artysta> Artysci = ArtistRepository.GetAllArtists();
}

Or you can pass it directly in DropDownList:

@Html.DropDownListFor(model => model.ArtystaID, new SelectList(ArtistRepository.GetAllArtists(), "ArtystaID", "Nazwisko", 0))

Oh, and just a personal note: I know it's probably not your choice, unless you are a lead/sole developer, but please use English names for your classes, variables etc., it will be much easier if in the future someone else will have to work on your code, and they might not speak Polish ;)



Related Topics



Leave a reply



Submit