views:

114

answers:

0

Once again I'm confronted with a "This shouldn't be this ?*!# hard" situation.

Problem: I want to use a form in MVC for creation of an object. One of the elements of the object is a set of limited choices - a perfect candidate for a drop down list.

But if I use a SelectList in my model, and a drop down list in my View, and then try to post the Model back to my Create method, I get the error "Missing Method Exception:No Parameterless constructor for this object". Exploring the MVC source code, it appears that in order to bind to a model, the Binder has to be able to create it first, and it can't create a SelectList because there is no default constructor for it.

Here's the simplified code: For the model:

 public class DemoCreateViewModel
{
    public SelectList Choice { get; set; }

}

For the controller:

      //
    // GET: /Demo/Create

    public ActionResult Create()
    {
        DemoCreateViewModel data = new DemoCreateViewModel();
        data.Choice = new SelectList(new string[] { "Choice1", "Choice2", "Choice3" });
        ViewData.Model = data;
        return View();
    }

    //
    // POST: /Demo/Create

    [HttpPost]
    public ActionResult Create(DemoCreateViewModel form)
    {
        try
        {
            // TODO: Add insert logic here

            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }

And for the View:

      <fieldset>
        <legend>Fields</legend>
        <%= Html.LabelFor(model => model.Choice) %>
        <%= Html.DropDownListFor(model => model.Choice, Model.Choice) %>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>

Now, I know I can MAKE this work by dropping back 10 yards and punting: bypass model binding and drop back to the FormCollection and validate and bind all the fields myself, but there's got to be a simpler way. I mean, this is about as simple a requirement as it gets. Is there a way to make this work within the MVC ModelBinding architecture? If so, what is it? And if not, how come?

Edit: Well, I have egg on my face, but maybe this will help someone else. I did some more experimenting and found a simple solution that seems to work.

Provide a simple value (string or integer, depending on what your select list value type is), and name that as the model element that you bind to. Then provide a second element as the select list of choices, and name it something else. So my model became:

 public class DemoCreateViewModel
{
    public string Choice { get; set; }
    public SelectList Choices { get; set; }

}

And then the DropDownListFor statement in the View becomes:

            <%= Html.DropDownListFor(model => model.Choice, Model.Choices) %>

When I do this, the submit button correctly binds the choice made in the form to the string Choice, and submits the model back to the second Create method.