Ask your technical questions on forums or here :
ASP.NET or MVC | C# | Windows Phone
Microsoft Technology Journals by Abhimanyu K Vatsa
HOME ABOUT RAZOR BOOK SPEAKING MVC ASP.NET JQUERY VIDEOS EBOOK ARCHIVE

10 May 2013

Three ways to populate selected value in DropDownList on Edit or Update views - MVC

I have been asked this question many times that ‘how to populate/activate selected value/item (which I selected while inserting/adding new record) in Edit/Update mode’. I’m going to address few possible approaches here.

So, we are going to create this (look at the image) where I’m able to see the selected value on Edit page.


Let’s start with the best conventional way to do this:

First Approach

This approach is very-very easy and most suggested to this when you are working with code first to generate initial database and views using Entity Framework.

Always design conceptual model classes this way:

public class Student
{
    public int Id { get; set; }
    public string StudentName { get; set; }
       
    public int SubjectId { get; set; }
    public Subject Subject { get; set; }
}

public class Subject
{
    public int Id { get; set; }
    public string SubjectName { get; set; }

    public virtual ICollection<Student> Students { get; set; }
}

public class StudentContext : DbContext
{
    public StudentContext()
        : base("name=DDLinEditModeConnection")
    {
    }

    public DbSet<Student> Students { get; set; }
    public DbSet<Subject> Subjects { get; set; }
}

In above code, look at the highlighted part, Student class has StudentId as a field/property and Subject as a navigation property.

Please maintain the naming convention (like I used ‘StudentId’) so that by looking at the conceptual model EF generates the right view (with working DropDownList) for you and populates it with right data (subjects).

Once you have conceptual model classes designed by above suggested convention, you can go ahead and add CRUD views using Entity Framework to get fully functional DropDownList even in Edit/Update mode on the fly.


Second Approach

You can use this approach when you have database already or you are working with database first or database design has no such conceptual settings (relationships) like I discussed in above approach.

public class Student
{
    public int Id { get; set; }
    public string StudentName { get; set; }
    public string SubjectName { get; set; }
}

public class Subject
{
    public int Id { get; set; }
    public string SubjectName { get; set; }
}

public class StudentContext : DbContext
{
    public StudentContext()
        : base("name=DDLinEditModeConnection")
    {
    }

    public DbSet<Student> Students { get; set; }
    public DbSet<Subject> Subjects { get; set; }
}

By looking at the above model classes design anyone can say it has no relation or navigation properties, right. So, above model will generate following database for sure.


Now generate CRUD views using Entity Framework based on above models, you will face following Create and Edit pages.


I don’t have DropDownList up in above pages, even after adding few subjects.


When I opened Create and Edit views, I noticed following helper.

@Html.EditorFor(model => model.SubjectName)

This is just fine, the main problem is with model class designs. We failed to provide good model class convention to EF, now what to do?

What Next?

Let’s do workaround.

Step 1

Open Student controller and make following changes (changes are highlighted) in Create action.

private StudentContext db = new StudentContext();
public ActionResult Create()
{
    ViewBag.SubjectName = new SelectList(db.Subjects, "SubjectName", "SubjectName");

    return View();
}

In above code I just added a ViewBag.SubjectName that will contain the list of SubjectNames and we will bind this on the Create.cshtml view as given below.

<div class="editor-field">
    @*@Html.EditorFor(model => model.SubjectName)*@
    @Html.DropDownList("SubjectName")
    @Html.ValidationMessageFor(model => model.SubjectName)
</div>

Step 2

Open Student controller and make following changes (changes are highlighted) in Edit action.

private StudentContext db = new StudentContext();
public ActionResult Edit(int id = 0)
{
    string selected = (from sub in db.Students
                        where sub.Id == id
                        select sub.SubjectName).First();
    ViewBag.SubjectName = new SelectList(db.Subjects, "SubjectName", "SubjectName", selected);

    Student student = db.Students.Find(id);
    if (student == null)
    {
        return HttpNotFound();
    }
    return View(student);
}

In above code, I’m checking what the selected value was using LINQ query and then using that value inside SelectList. Now, DropDownList will look at the provided default selected value and displays it.

<div class="editor-field">
    @*@Html.EditorFor(model => model.SubjectName)*@
    @Html.DropDownList("SubjectName")
    @Html.ValidationMessageFor(model => model.SubjectName)
</div>

Step 3

Run the application, you will get same experience like we had in first approach. You will have working DDL everywhere.



Third Approach

This approach will be very similar to second one, here we will use Enum instead of Subject model class. This approach will be useful if you don’t want another database table to store list of subjects and to go simple.

Step 1

Design model class this way:

public class Student
{
    public int Id { get; set; }
    public string StudentName { get; set; }
    public string SubjectName { get; set; }
}

//public class Subject
//{
//    public int Id { get; set; }
//    public string SubjectName { get; set; }
//}

public enum SubjectEnum
{
    Computer_Science, Mathematics, Chemistry, Physics, Biology
}

public class StudentContext : DbContext
{
    public StudentContext()
        : base("name=DDLinEditModeConnection")
    {
    }

    public DbSet<Student> Students { get; set; }
    //public DbSet<Subject> Subjects { get; set; }
}

In above code, you can see I commented out Subject class and its DbSet and used a new SubjectEnum to store the list of subjects. Don’t worry about the underscore (_) signed I used with subject name, I’ll replace it with white space in controller action.

Step 2

Once you have above model class, generate the CRUD views and make following changes in Create action method:

public ActionResult Create()
{
    var subjects =  from SubjectEnum s in Enum.GetValues(typeof(SubjectEnum))
                    select new { Name = s.ToString().Replace('_', ' ') };
    ViewBag.SubjectName = new SelectList(subjects, "Name", "Name");

    return View();
}

In above controller action, I used LINQ query to query the Enum and replaced the underscore (_) character with white space.

Now, on the Create.cshtml view page, use following:

<div class="editor-field">
    @*@Html.EditorFor(model => model.SubjectName)*@
    @Html.DropDownList("SubjectName", "")
    @Html.ValidationMessageFor(model => model.SubjectName)
</div>

Step 3

Make following changes in Edit action method:

public ActionResult Edit(int id = 0)
{
    var subjects = from SubjectEnum s in Enum.GetValues(typeof(SubjectEnum))
                    select new { Name = s.ToString().Replace('_', ' ') };
    string selected = (from sub in db.Students
                        where sub.Id == id
                        select sub.SubjectName).First();
    ViewBag.SubjectName = new SelectList(subjects, "Name", "Name", selected);

    Student student = db.Students.Find(id);
    if (student == null)
    {
        return HttpNotFound();
    }
    return View(student);
}

One thing new here, to populating the previous selection as a selected value in DDL use following on Edit.cshtml view page:

<div class="editor-field">
    @*@Html.EditorFor(model => model.SubjectName)*@
    @Html.DropDownList("SubjectName", "")
    @Html.ValidationMessageFor(model => model.SubjectName)
</div>

Step 4

Run the application, you will get same experience like in first and second approach. You will have working DDL everywhere.

You can also do this by taking advantage of Razor extension methods.

Hope this helps.

Comment using Google Services (9 comments):

  1. Thank you for great article. It was very helped me.

    ReplyDelete
  2. When using ddl like this @Html.DropDownList("SubjectName", "") in view cant store to db.
    when using in this syntax @Html.DropDownListFor(m => m.Color, new SelectList(ViewBag.Colors, "Value", "Text"), "") storing value to db but when enum with "_" symbol also doesnt work. why?

    ReplyDelete
  3. First method is not working, it creates an editFor for the subjectId. I'm following all the name conventions and I'm using visual studio 2013 and MVC 4.

    ReplyDelete
  4. Reading the second method, since it mirrors what I have already in an application, and I'm noticing that you don't specify in the Create() how to access the data for the ViewBag. What am I specifically missing here?

    ReplyDelete
  5. Excellent it solved my purpose

    ReplyDelete
  6. Excellent solutions... Thank you

    ReplyDelete
  7. Hi, i've followed the second approach but it is not working. what's the problem with my codes? any suggestions thanks :)

    here's my code
    Model:
    public class JobRequestList
    {
    public int ID { get; set; }
    public string Status { get; set; }
    }

    public class StatusList
    {
    public int ID { get; set; }
    public string Status { get; set; }
    public string statusDes { get; set; }
    }

    controller:
    public ActionResult Edit(int? id = 0)
    {
    if (id == null)
    {
    return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    string selected = (from sub in db.RequestModels
    where sub.ID == id
    select sub.Status).First();
    ViewBag.Status = new SelectList(db.StatusList, "Status", "statusDes", selected);
    JobRequestList jobRequestList = db.RequestModels.Find(id);

    if (jobRequestList == null)
    {
    return HttpNotFound();
    }
    return View(jobRequestList);
    }

    View :
    @Html.LabelFor(model => model.Status, htmlAttributes: new { @class = "control-label col-md-3" })
    @Html.DropDownList("Status", (IEnumerable)ViewBag.Status, new { @class = "form-control" })
    @Html.ValidationMessageFor(model => model.Status, "", new { @class = "text-danger" })

    ReplyDelete