Problem with DropDownListFor SelectedItem

RPM1984 picture RPM1984 · May 5, 2011 · Viewed 9.2k times · Source

This has totally puzzled me.

Here's my View:

@Html.DropDownListFor(model => model.ScoreDescription, 
                               Model.RatingOptions, 
                               "--", 
                               new { @id = clientId })

And the model:

public decimal? Score { get; set; }
public SelectList RatingOptions
{ 
    get
    {
        var options = new List<SelectListItem>();

        for (var i = 1; i <= 5; i++)
        {
            options.Add(new SelectListItem
            {
                Selected = Score.HasValue && Score.Value == Convert.ToDecimal(i),
                Text = ((decimal)i).ToRatingDescription(ScoreFactorType),
                Value = i.ToString()
            });
        }

        var selectList = new SelectList(options, "Value", "Text");
            // At this point, "options" has an item with "Selected" to true.
            // Also, the underlying "MultiSelectList" also has it.
            // Yet selectList.SelectedValue is null. WTF?
        return selectList;
    }
}

As the comments suggest, i can't get the selected value to happen.

Is it something to do with the fact i'm using a nullable decimal ? After that loop, options is correct in that it has exactly 1 item with select to true, so it seems i'm doing the right thing.

Now, if i use a different SelectList overload:

var selectedValue = Score.HasValue ? Score.Value.ToString("0") : string.Empty;
var selectList = new SelectList(options, "Value", "Text", selectedValue);

It works. Why? At first i thought it might be a LINQ-trick (e.g deferred execution), but i tried forcing a .ToList() and there is no difference.

It's like setting the Selected property as you create the SelectListItem has no effect, and you have you set it at the end using the SelectList ctor parameter.

Can anyone shed some light on this?

Answer

Darin Dimitrov picture Darin Dimitrov · May 5, 2011

If you look at the implementation of the SelectList class it never actually uses the fact that you are passing a SelectListItem. It works with an IEnumerable. So the Selected property of a SelectListItem is not used. Personally I prefer setting the selected value of a dropdown by setting the value of the corresponding property that you are binding the ddl to.

Example:

public int? Score { get; set; }
public SelectList RatingOptions
{ 
    get
    {
        var options = Enumerable.Range(1, 5).Select(i => new SelectListItem
        {
            Text = ((decimal)i).ToRatingDescription(ScoreFactorType),
            Value = ((decimal)i).ToString()
        });
        return new SelectList(options, "Value", "Text");
    }
}

and then in the controller action simply set the Score property to the necessary value and in the view use this Score property to bind to:

@Html.DropDownListFor(
    model => model.Score, 
    Model.RatingOptions, 
    "--", 
    new { @id = clientId }
)