@Html.DropDownList fails to initialize value

2.1k Views Asked by At

I have a simple piece of code that tries to populate a list of 3 countries and select the 2nd item by default. For some reason, @Html.DropDownList is not initializing the 2nd item in the screen. Can someone point out whats wrong with this code ?

public class DemoController : Controller
{
    [HttpGet]
    public ActionResult Test()
    {
        List<SelectListItem> ddlCountries = new List<SelectListItem>();
        SelectListItem item1 = new SelectListItem { Selected = false, Text = "USA", Value = "1" };
        SelectListItem item2 = new SelectListItem { Selected = true, Text = "Ireland", Value = "2" };
        SelectListItem item3 = new SelectListItem { Selected = false, Text = "UK", Value = "3" };

        ddlCountries.Add(item1);
        ddlCountries.Add(item2);
        ddlCountries.Add(item3);

        ViewBag.ddlCountries = ddlCountries;
        return View();
    }

    [HttpPost]
    public string Test(string ddlCountries)
    {
        return "The selected Value was " + ddlCountries;
    }
}

The below is the output on the screen..

Screen Output

The quickwatch window shows that the "Selected" property is correctly set to value "2", yet the screen doesn't reflect this..

Quickwatch output

The code for the view is the following

@{
    ViewBag.Title = "Test";
}

<h2>Test</h2>

@using (Html.BeginForm("Test", "Demo", FormMethod.Post))
{ 

@Html.DropDownList("ddlCountries", (IEnumerable<SelectListItem>) ViewBag.ddlCountries)
    <br />
    <input type="submit" value="submit" />
}
1

There are 1 best solutions below

1
On

Try using a view model, it's much easier, safer and cleaner:

public class MyViewModel
{
    public string SelectedCountryId { get; set; }
    public IEnumerable<SelectListItem> Countries { get; set; }
}

and then in your controller:

public class DemoController : Controller
{
    [HttpGet]
    public ActionResult Test()
    {
        List<SelectListItem> ddlCountries = new List<SelectListItem>();
        SelectListItem item1 = new SelectListItem { Text = "USA", Value = "1" };
        SelectListItem item2 = new SelectListItem { Text = "Ireland", Value = "2" };
        SelectListItem item3 = new SelectListItem { Text = "UK", Value = "3" };

        ddlCountries.Add(item1);
        ddlCountries.Add(item2);
        ddlCountries.Add(item3);

        var model = new MyViewModel();
        model.Countries = ddlCountries;
        model.SelectedCountryId = "2"; // preseleted Ireland

        return View(model);
    }

    [HttpPost]
    public ActionResult Test(MyViewModel model)
    {
        return Content("The selected Value was " + model.SelectedCountryId);
    }
}

and finally in your strongly typed view:

@model MyViewModel
@{
    ViewBag.Title = "Test";
}

<h2>Test</h2>

@using (Html.BeginForm("Test", "Demo", FormMethod.Post))
{ 
    @Html.DropDownListFor(x => x.SelectedCountryId, Model.Countries)
    <br />
    <input type="submit" value="submit" />
}

and that's pretty much the correct approach. Forget about any ViewBag stuff. Just use a view model. The single source of information for the view must be the view model.

By the way the GET controller action could be simplified to:

[HttpGet]
public ActionResult Test()
{
    var model = new MyViewModel();
    model.Countries = new[]
    {
        new SelectListItem { Text = "USA", Value = "1" },
        new SelectListItem { Text = "Ireland", Value = "2" },
        new SelectListItem { Text = "UK", Value = "3" },
    };
    model.SelectedCountryId = "2"; // preseleted Ireland

    return View(model);
}

or the countries could be directly bound in your view model:

public class MyViewModel
{
    public string SelectedCountryId { get; set; }
    public IEnumerable<SelectListItem> Countries 
    {
        get
        {
            return new[]
            {
                new SelectListItem { Text = "USA", Value = "1" },
                new SelectListItem { Text = "Ireland", Value = "2" },
                new SelectListItem { Text = "UK", Value = "3" },
            };
        }
    }
}

and then your controller becomes even slimmer:

public class DemoController : Controller
{
    [HttpGet]
    public ActionResult Test()
    {
        var model = new MyViewModel();
        model.SelectedCountryId = "2"; // preseleted Ireland
        return View(model);
    }

    [HttpPost]
    public ActionResult Test(MyViewModel model)
    {
        return Content("The selected Value was " + model.SelectedCountryId);
    }
}