开发者

How to pass strongly typed results back to ActionResult?

I have a view that is strongly typed:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MPKwithMVC.Models.SmartFormViewModel>" %> 

Works great to generate the view, but when I post, I have an ActionResult defined:

[AcceptVerbs(HttpVerbs.Post)]  
public Ac开发者_开发问答tionResult Next(MPKwithMVC.Models.SmartFormViewModel model)   
{ .. }  

Which I would imagine get hit when my next button is clicked (it works if I change the argument to a FormsCollection). I instead get a message saying "No parameterless constructor defined for this object".

What am I doing wrong?

My SmartFormsViewModel is:

    [Serializable]
public class SmartFormViewModel
{
    public List<Question> Questions { get;  set; }
    public List<Answer> Answers { get;  set; }

    public SmartFormViewModel(List<Question> questions, List<Answer> answers)
    {
        this.Questions = questions;
        this.Answers = answers;
    }

    public SmartFormViewModel()
    {
    }
}

And here is the View:

    <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MPKwithMVC.Models.SmartFormViewModel>" %>
<%@ Import Namespace="MPKwithMVC.Models" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    SmartForms
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>
        Questionaire</h2>
    <% using (Html.BeginForm("Next", "SmartForms"))
       { %>
    <div style="float: left; margin-right: 2em;">
        <% Html.RenderPartial("NavigationPanel", Model); %>
    </div>
    <div>
        <table>
            <%
                foreach (Question question in (Model.Questions))
                { %>
            <tr>
                <td>
                    <div style="text-align: right; width: 20em;">
                        <%= Html.Encode(question.QuestionText)%>
                    </div>
                </td>
                <td>
                    <div style="float: left;">
                        <% if (question.QuestionType == 1)
                           { %>
                        <%= Html.TextBoxFor(model => model.Answers[(int)question.QuestionID - 1].AnswerValue) %>
                        <% } %>
                        <% if (question.QuestionType == 2)
                           { %>
                        <%= Html.RadioButton("yn" + question.QuestionID, "Yes", false)%>Yes
                        <%= Html.RadioButton("yn" + question.QuestionID, "No", true)%>No
                        <% } %>
                    </div>
                    <% if (question.Required == true)
                       { %>
                    <div style="color: Red; float: right; margin-left: 3px;">
                        *</div>
                    <% } %>
                </td>
            </tr>
            <%
                } %>
            <tr>
                <td>
                    <%
           if (ViewData["errorMsg"] != null)
                       {%>
                       <div style="color:Red;">
                        <%= Html.Encode(ViewData["errorMsg"].ToString()) %>
                       </div>
                    <% } %>
                </td>
                <td>
                    <div style="margin-top: 1em;">

                        <button name="button" value="next">Next</button>

                    </div>
                </td>
            </tr>
        </table>
    </div>
    <% } %>

</asp:Content>


Have a look at the input names that are being generated within your HTML. I think you have an issue with the naming of your controls thus the default model binding is failing since you mentioned that the using FormCollection works correctly. I am making this assumption since I don't know what your Questions and Answers classes look like

<%= Html.TextBoxFor(model => 
        model.Answers[(int)question.QuestionID - 1].AnswerValue) %>

Won't this render something similar to the following; which if I'm not mistaken will not bind to your model. The same applies for the RadioButtons.

<input type="text" name="Answers[0].AnswerValue" id="Answers_0__AnswerValue" value="somevalues"/>

The RadioButton helper should be

<%= Html.RadioButton("Questions[" + question.QuestionID + "].ID", "Yes", false)%> // you now get a list of questions

<input type="radio" name="Questions[1].ID" id="Questions_1__ID" value="No" checked="checked"/>

There are some ways you can try to resolve this:

  1. Correctly name your input controls so that they match you ViewModel. (This includes posting back all the required fields for you model - I think that that default values are used when the model binding occurs if its not posted)
  2. Create a custom model binder
  3. You may need to tell the Binder what the Prefix of the input fields are. ([Bind] attribute) to specifically include or exclude form fields.
  4. Create a new Model containing the values you expect to post back

I think that overall that your approach needs to slightly change. From the info provided, your Q&As are closely related. Depending on the question type your 'answer' is either boolean or freetext. At the moment your are not posting a List<Questions> back to the server. Answers, yes, but they are not I don't think that it is recognised as List<Answers>.

Haacked has a post which I think is related to your issue and this SO question further indicates that it may still applly to ASP-MVC-2.


As the error message suggest, your SmartFormViewModel class needs to contain a parameterless constructor.

This is a great example of what you would like to do

I would also suggest you use the strongly typed helpers from Html extensions to generate your form fields.

e.g.

Html.HiddenFor(x => x.SomeField) 
Html.TextBoxFor(x => x.SomeEditableField)


Make sure that your <form>'s action points to your Next method.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜