1

I have the following controller and view. It is listing the values correctly using GET. When I click on the button it causes a POST. However the value received in the controller is NULL. How do we correct it?

HIGHLIGHTED CODE

    [HttpPost]
    public ActionResult CastVote(IEnumerable<Program> theProgramList)

GET Image

enter image description here

CODE

 public enum RatingEnum { Poor = 0, Neutral, Good, Excellent };

public class Program
{
    public int ProgramID { get; set; }
    public string ProgramName { get; set; }
    public RatingEnum RatingID { get; set; }
    public string ProgramCategory { get; set; }
}

CONTROLLER

namespace MyProgramRatingApp.Controllers
{
public class ProgramController : Controller
{




    List<Program> programList = new List<Program>()
                          {
                            new Program
                            {
                                ProgramID = 1,ProgramName = "Program1",
                                ProgramCategory = "A"
                            },
                            new Program
                            {
                                ProgramID = 2,ProgramName = "Program2",
                                ProgramCategory = "B"
                            },
                            new Program
                            {
                                ProgramID = 3,ProgramName = "Program3",
                                ProgramCategory = "A"
                            }

                          };




    // GET: /Program/
    public ActionResult CastVote()
    {
        ViewBag.RatingEnum = GetRstingSelectList();
        return View(programList);
    }


    // POST: /StoreManager/Create
    [HttpPost]
    public ActionResult CastVote(IEnumerable<Program> theProgramList)
    {
        if (ModelState.IsValid)
        {
            //Save the book in DB first and then redirectToAction.


            return RedirectToAction("CastVote");
        }

        return View(theProgramList);
    }


    public static SelectList GetRstingSelectList()
    {
        Array values = Enum.GetValues(typeof(RatingEnum));
        List<System.Web.UI.WebControls.ListItem> items = new List<System.Web.UI.WebControls.ListItem>(values.Length);

        foreach (var i in values)
        {
            items.Add(new System.Web.UI.WebControls.ListItem
                                    {
                                        Text = Enum.GetName(typeof(RatingEnum), i),
                                        Value = ((int)i).ToString()
                                    }
                       );
        }

        return new SelectList(items);
    }


    }
}

VIEW

@model IEnumerable<MyProgramRatingApp.Program>

@{
ViewBag.Title = "CastVote";
}

<h2>CastVote</h2>

@using (Html.BeginForm())
{
<table>
    <tr>
        <th style="border:1px solid Teal; background-color:Gray">
            ProgramName
        </th>
        <th style="border:1px solid Teal; background-color:Gray">
            RatingID
        </th>
        <th style="border:1px solid Teal; background-color:Gray">
            ProgramCategory
        </th>
        <th style="border:1px solid Teal; background-color:Gray"></th>
    </tr>

@foreach (var item in Model)
{
    <tr>
        <td style="border:1px solid Teal">
            @Html.DisplayFor(modelItem => item.ProgramName)
        </td>
        <td style="border:1px solid Teal">
            @Html.DisplayFor(modelItem => item.RatingID)
        </td>
        <td style="border:1px solid Teal">
            @Html.DisplayFor(modelItem => item.ProgramCategory)
        </td>
        <td style="border:1px solid Teal">
            @Html.DropDownListFor(model => item.RatingID, (SelectList)ViewBag.RatingEnum, String.Empty)
        </td>

    </tr>
}

</table>

<p>
    <input type="submit" value="Cast Vote" />
</p>

}

READING:

  1. dropdownlist set selected value in MVC3 Razor

  2. ASP.NET MVC 3 - Partial vs Display Template vs Editor Template

  3. IEnumerable property with MVC3 EditorTemplate

  4. ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries

  5. Model Binding To A List


Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418
  • DisplayFor is probably generating ProgramName (in the name attr) for the complete list which cannot be bound back to the model. check the names in the html I think they need to be ProgramName[0], ProgramName[1] etc.. – Daniel Little Feb 20 '12 at 06:30
  • Since I am a newbie in MVC, I didn't quite understand your point. Could you please provide a code demo? Also, I the number of programs is dynamic (from database) in real scenario. – LCJ Feb 20 '12 at 06:33
  • 1
    to check what values are actually being posted accept a FormCollection parameter in your post action. – nathan gonzalez Feb 20 '12 at 06:37
  • When I used FormCollection, there is only one string in allKeys. It is "item.RatingID" – LCJ Feb 20 '12 at 06:40

3 Answers3

4

Replace the foreach loop in your view with a call to an editor template:

@model IEnumerable<MyProgramRatingApp.Program>

@{
    ViewBag.Title = "CastVote";
}

<h2>CastVote</h2>

@using (Html.BeginForm())
{
    <table>
        <tr>
            <th style="border:1px solid Teal; background-color:Gray">
                ProgramName
            </th>
            <th style="border:1px solid Teal; background-color:Gray">
                RatingID
            </th>
            <th style="border:1px solid Teal; background-color:Gray">
                ProgramCategory
            </th>
            <th style="border:1px solid Teal; background-color:Gray"></th>
        </tr>

        @Html.EditorForModel()

    </table>

    <p>
        <input type="submit" value="Cast Vote" />
    </p>
}

and then define the editor template which will be automatically rendered for each element in the model (~/Views/Shared/EditorTemplates/Program.cshtml):

@model MyProgramRatingApp.Program
<tr>
    <td style="border:1px solid Teal">
        @Html.EditorFor(x => x.ProgramName)
    </td>
    <td style="border:1px solid Teal">
        @Html.EditorFor(x => x.RatingID)
    </td>
    <td style="border:1px solid Teal">
        @Html.EditorFor(x => x.ProgramCategory)
    </td>
    <td style="border:1px solid Teal">
        @Html.DropDownListFor(
             x => x.RatingID, 
             (SelectList)ViewBag.RatingEnum, 
             String.Empty
        )
    </td>
</tr>

Notice that I have used @Html.EditorFor in the editor template instead of @Html.DisplayFor in order to generate input fields. If you don't do that you won't get any values back in the controller because your form doesn't contain any input elements. If you don't want to show input fields you could use hidden inputs:

@model MyProgramRatingApp.Program
<tr>
    <td style="border:1px solid Teal">
        @Html.DisplayFor(x => x.ProgramName)
        @Html.HiddenFor(x => x.ProgramName)
    </td>
    <td style="border:1px solid Teal">
        @Html.DisplayFor(x => x.RatingID)
        @Html.HiddenFor(x => x.RatingID)
    </td>
    <td style="border:1px solid Teal">
        @Html.DisplayFor(x => x.ProgramCategory)
        @Html.HiddenFor(x => x.ProgramCategory)
    </td>
    <td style="border:1px solid Teal">
        @Html.DropDownListFor(
             x => x.RatingID, 
             (SelectList)ViewBag.RatingEnum, 
             String.Empty
        )
    </td>
</tr>

The editor template will generate correct names for the input fields so that the model binder correctly binds the values.

You may also take a look at the following article to better understand the wire format that is expected for collections.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • @Lijo, that's most probably because you made some mistake in the naming of your editor template. Make sure that it is located in `~/Views/Shared/EditorTemplates/` and that it is called `Program.cshtml`. If it is not showing it means that you didn't place the template at the correct place. Double check once again. – Darin Dimitrov Feb 20 '12 at 08:43
  • 1
    @Lijo, I made a mistake in my code. You should use `@Html.EditorFor` instead of `@Html.DisplayFor` in the template. I have updated my answer to fix that. A DisplayFor simply generates a label. You need to use an input field if you want to preserve the value when posting to the controller. – Darin Dimitrov Feb 20 '12 at 08:55
  • @Lijo, you have 2 inputs for it. One `@Html.EditorFor(x => x.RatingID)` and one `@Html.DropDownListFor`. You must decide which one of the two to use in order to bind this value. Then remove the other. – Darin Dimitrov Feb 20 '12 at 09:03
  • @Lijo, I don't see any input field for `ProgramID` in your template which might explain why it is always zero. Did you add one like `@Html.EditorFor(x => x.ProgramID)`? – Darin Dimitrov Feb 20 '12 at 09:28
  • ProgramId reached controller when I used @Html.HiddenFor(x => x.ProgramID). However RatingID is still not reaching. @Html.DropDownListFor(x => x.RatingID, (SelectList)ViewBag.RatingEnum, String.Empty). How do we make it working? – LCJ Feb 20 '12 at 10:13
  • 1
    @Lijo, you seem to be using an enum type to bind the dropdown list to: `ViewBag.RatingEnum`. This obviously cannot work with your `x => x.RatingID` because your `RatingID` property is an integer. Maybe you could try declaring it as a `public RatingEnum ProgramID { get; set; }` – Darin Dimitrov Feb 20 '12 at 11:06
  • Thank you Darin. I am astonished at the care that you give for each post. I am going through one of your replies in the following. I am learning about viewmodel now to solve this. http://stackoverflow.com/questions/4579598/c-sharp-mvc-3-using-selectlist-with-selected-value-in-view. Also I will try with RatingEnum as you suggested. – LCJ Feb 20 '12 at 12:48
  • Thanks. Changing the RatingID in Program class did the trick. Hopefully the final question on this post. We made the ProgramName as EditorFor to get the value in controller. Now it is coming as textbox. We need to get the value in controller still it should be a simple label. How do we do that? – LCJ Feb 21 '12 at 05:34
  • 1
    @Lijo, bu using a `Html.DisplayFor(x => x.ProgramName)` and by placing an additional hidden field in order to pass the value `Html.HiddenFor(x => x.ProgramName)`. I have already explained this in my answer and even shown an example. – Darin Dimitrov Feb 21 '12 at 06:51
1

this code work for me:

  1. Controller: [HttpPost] public ActionResult CastVote(Ilist theProgramList)

2 View:

@model IList<Program>
@using (Html.BeginForm())
{
<table>
    <tr>
        <th style="border:1px solid Teal; background-color:Gray">
            ProgramName
        </th>
        <th style="border:1px solid Teal; background-color:Gray">
            ProgramCategory
        </th>
        <th style="border:1px solid Teal; background-color:Gray">  </th>
    </tr>

@for(var i=0,j=mode.Count;i<j;i++)
{
  <tr>
     <td>@model[i].ProgrameName</td>
     <td><input type="text" name="RaingID[@i]" value="@model[i].RatingID"</td>


      <td>
        @Html.DropDownListFor(x => x.Rating, 
                      (SelectList)ViewBag.RatingEnum, new {@name="ProgramCategory[i] "})
     </td>
  </tr>
}
<p>
    <input type="submit" value="Cast Vote" />
</p>

</table>

}

Alex Nguyen
  • 1,032
  • 12
  • 27
0

Thanks to Darin. I have accepted his answer. The complete solution is posted for the benefit of others reading this.

Note: EditorTemplate helped to avoid foreach loop and helped in proper object binding.

Note: The EditorTemplate should be placed in proper folder and the filename should be based on convention.

//Model

namespace MyProgramRatingApp
{
public enum RatingEnum { Poor = 0, Neutral, Good, Excellent };

public class Program
{
    public int ProgramID { get; set; }
    public string ProgramName { get; set; }
    public RatingEnum Rating { get; set; }
    public string ProgramCategory { get; set; }
}
}

//Controller

using System;
using System.Collections.Generic;
using System.Web.Mvc;

namespace MyProgramRatingApp.Controllers
{

public class ProgramController : Controller
{


    List<Program> programList = new List<Program>()
                          {
                            new Program
                            {
                                ProgramID = 1,ProgramName = "Program1",
                                ProgramCategory = "A"
                            },
                            new Program
                            {
                                ProgramID = 2,ProgramName = "Program2",
                                ProgramCategory = "B"
                            },
                            new Program
                            {
                                ProgramID = 3,ProgramName = "Program3",
                                ProgramCategory = "A"
                            }

                          };




    // GET: /Program/
    public ActionResult CastVote()
    {
        ViewBag.RatingEnum = GetRstingSelectList();
        return View(programList);
    }


    // POST: /StoreManager/Create
    [HttpPost]
    public ActionResult CastVote(IEnumerable<Program> theProgramList)
    {
        if (ModelState.IsValid)
        {
            //Save the book in DB first and then redirectToAction.
            return RedirectToAction("CastVote");
        }

        return View(theProgramList);
    }


    public static SelectList GetRstingSelectList()
    {
        Array values = Enum.GetValues(typeof(RatingEnum));
        List<System.Web.UI.WebControls.ListItem> items = new List<System.Web.UI.WebControls.ListItem>(values.Length);

        foreach (var i in values)
        {
            items.Add(new System.Web.UI.WebControls.ListItem
                                    {
                                        Text = Enum.GetName(typeof(RatingEnum), i),
                                        Value = ((int)i).ToString()
                                    }
                       );
        }

        return new SelectList(items);
    }


 }
}

View(CastVote.cshtml)

@model IEnumerable<MyProgramRatingApp.Program>

@{
ViewBag.Title = "CastVote";
}

<h2>CastVote</h2>

@using (Html.BeginForm())
{
<table>
    <tr>
        <th style="border:1px solid Teal; background-color:Gray">
            ProgramName
        </th>
        <th style="border:1px solid Teal; background-color:Gray">
            ProgramCategory
        </th>
        <th style="border:1px solid Teal; background-color:Gray">  </th>
    </tr>

    @Html.EditorForModel()

</table>

<p>
    <input type="submit" value="Cast Vote" />
</p>
}

EditorTemplate(Program.cshtml)

@model MyProgramRatingApp.Program
<tr>
<td style="border:1px solid Teal">
    @Html.DisplayFor(x => x.ProgramName)
    @Html.HiddenFor(x => x.ProgramName)
</td>
<td style="border:1px solid Teal">
    @Html.EditorFor(x => x.ProgramCategory)
</td>
<td style="border:1px solid Teal">
    @Html.DropDownListFor(x => x.Rating, (SelectList)ViewBag.RatingEnum, String.Empty)
</td>
<td>
    @Html.HiddenFor(x => x.ProgramID)
</td>

</tr>
LCJ
  • 22,196
  • 67
  • 260
  • 418