Model with collection - Html.ListBoxFor doesn't set selected items

ndd picture ndd · Dec 1, 2014 · Viewed 9.8k times · Source

This one is driving me crazy and before I loose my sane please help.

Summary of the issue: My model "Thread" has collection of "ForumMessage", each ForumMessage has one Multi select drop down list. All I want to do is set the selected value based on values coming from Database. I have gone thru many threads but wasn't able to find the solution.

In case if you are aware of any such question please let me know and I will go thru them.

Below are my models

public class Thread
{
    public List<ForumMessage> Messages { get; set; }
    //Master list coming from DB
    public List<Classifications> AllClassifications { get; set; }
    public string Subject { get; set; }
    public int[] ThreadSelectedClassifications { get; set; }
}

public class ForumMessage
{
    public string MessageName { get; set; }
    public int[] SelectedClassifications { get; set; }
}

public class Classifications
{
    public int ID { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
}

Below is my controller

public ActionResult Index()
{
    var thread = new Thread();
    thread.Messages = new List<ForumMessage>();

    thread.AllClassifications = new List<Classifications>() 
    { 
        new Classifications { ID = 1, Title = "One"   , Description = "One"   }, 
        new Classifications { ID = 2, Title = "Two"   , Description = "Two"   }, 
        new Classifications { ID = 3, Title = "Three" , Description = "Three" }, 
        new Classifications { ID = 4, Title = "Four"  , Description = "Four"  }, 
        new Classifications { ID = 5, Title = "Five"  , Description = "Five"  } 
    };
    thread.ThreadSelectedClassifications = new int[] { 2, 4 };
    for (int i = 0; i < 5; i++)
    {
        var post = new ForumMessage();
        post.SelectedClassifications = new int[] { 2, 4 };
        post.MessageName = i.ToString();
        thread.Messages.Add(post);
    }

    return View(thread);    

Below is my view

@model MultiSelectDemo.Controllers.Thread
@foreach (var item in Model.Messages)
{
    <div class="row">
        <div class="col-md-4">
            @*@Doesn't set the items selected *@
            @Html.ListBoxFor(m => item.SelectedClassifications, new SelectList(Model.AllClassifications, "ID", "Title"));
        </div>

        <div class="col-md-4">
            @* Doesn't set the items selected  *@
            @Html.ListBoxFor(m => item.SelectedClassifications, new SelectList(Model.AllClassifications, "ID", "Title", item.SelectedClassifications));
        </div>
        <div class="col-md-4">
            @* Doesn't set the items selected  *@
            @Html.DropDownListFor(m => item.SelectedClassifications, new SelectList(Model.AllClassifications, "ID", "Title", item.SelectedClassifications), new { @multiple = "multiple" })
        </div>
    </div>
}
<hr />
<div class="row">
    <div class="col-md-12">
        This works.
        @Html.ListBoxFor(m => m.ThreadSelectedClassifications, new MultiSelectList(Model.AllClassifications, "ID", "Title"))
    </div>
</div>

Sample Output}

Answer

Chris Pratt picture Chris Pratt · Dec 1, 2014

SelectList only supports a single selected value, so in your code examples you're telling it to set the selected value as the option which has a value of the entire list of selected values. Since no such option exists, nothing is selected.

MultiSelectList on the other hand, allows passing an enumerable of selected values. If you use the right class, you'll be okay.

@Html.ListBoxFor(m => item.SelectedClassifications, new MultiSelectList(Model.AllClassifications, "ID", "Title", item.SelectedClassifications))