tags:

views:

3311

answers:

4

I haver a simple radio button list on my page that I render with the following in my view:

<label for="gender">Gender</label>
<%= Html.RadioButton("gender", 1) %> Male
<%= Html.RadioButton("gender", 2) %> Female
<%= Html.ValidationMessage("gender") %>

Note that when the user initially sees this input, neither button is selected. The validation is there to force them to choose and not accept a default. Therefore, these two radio buttons are bound to a nullable int property in my model declared as:

public int? gender { get; set; }

So if they do not select a button, and submit the page, the gender property will be null indicating that they did not select. The following validation is called by the controller during the post:

if (!gender.HasValue)
    ModelState.AddModelError("gender", "gender required");

But, if the validation fails (they did not choose), then during the rendering phase, the following exception is thrown by the MVC framework:

System.NullReferenceException was unhandled by user code
  Message="Object reference not set to an instance of an object."

In searching for for a solution to this problem, I noted several had this problem. I am using ASP.NET MVC 1.0. I found the place in the code where this error is thrown using .NET Reflector.

The question is how to make this work correctly?

EDIT: to add stacktrace:

System.NullReferenceException was unhandled by user code
  Message="Object reference not set to an instance of an object."
  Source="System.Web.Mvc"
  StackTrace:
       at System.Web.Mvc.HtmlHelper.GetModelStateValue(String key, Type destinationType)
       at System.Web.Mvc.Html.InputExtensions.InputHelper(HtmlHelper htmlHelper, InputType inputType, String name, Object value, Boolean useViewData, Boolean isChecked, Boolean setId, Boolean isExplicitValue, IDictionary`2 htmlAttributes)
       at System.Web.Mvc.Html.InputExtensions.RadioButton(HtmlHelper htmlHelper, String name, Object value, Boolean isChecked, IDictionary`2 htmlAttributes)
       at System.Web.Mvc.Html.InputExtensions.RadioButton(HtmlHelper htmlHelper, String name, Object value, IDictionary`2 htmlAttributes)
       at System.Web.Mvc.Html.InputExtensions.RadioButton(HtmlHelper htmlHelper, String name, Object value)
       at ASP.views_vbs_register_aspx.__RenderregisterContent(HtmlTextWriter __w, Control parameterContainer) in c:\Users\David\Documents\BellevueProject\Bellevue\BellevueTeachers\Forms\Views\VBS\Register.aspx:line 42
+7  A: 

I just tried something that makes this work. The problem does not occur if I do not do the validation step but of course I need the validation. That gave me a clue for the solution.

The ValidationMessage HtmlHelper method takes a string argument that is the name of the property or model object being validated. I just changed that name to be "gender2" as follows:

<label for="gender">Gender</label>
<%= Html.RadioButton("gender", 1) %> Male
<%= Html.RadioButton("gender", 2) %> Female
<%= Html.ValidationMessage("gender2") %>

And I changed the validation code to refer to this new name (even though that property does not exist, it still works):

if (!gender.HasValue)
    ModelState.AddModelError("gender2", "gender required");

This works as desired.

I would have thought the other should have worked, but this is a simple workaround and I am documenting that here.

EDIT: By the way I tried changing the gender property to a string instead of a nullable int, and the same exact problem occurs.

The work around still seems to be in using a different key name for the Validation Message.

davcar
A: 

You might want to try changing gender to a string (M/F) instead of an int and see if that works.

If you absolutely must have it as an int, you could always translate on the back end.

private int? gender { get; set; }
public string displayGender
{
    get
    {
        return this.gender.HasValue
                 ? (this.gender.Value == 1 ? "M" : "F" )
                 : null;
    }
    set
    {
        this.gender = null;
        if (value == "M")
           this.gender = 1;
        else if (value == "F")
           this.gender = 2;
    }
}

<label for="gender">Gender</label>
<%= Html.RadioButton("displayGender", "M") %> Male
<%= Html.RadioButton("displayGender", "F") %> Female
<%= Html.ValidationMessage("displayGender") %>

Base on your comment, you may want to add:

<%= Html.RadioButton("displayGender",
                     string.Empty,
                     true, // this is the default
                     new { @style = "display: none;" } ) %>

This will ensure that displayGender gets posted back (there will always be a chosen radio) and I think the value will be string.Empty instead of a null reference. If this works, you may want to try switching back to the nullable int.

tvanfosson
Same thing happens with a string in the same spot. Even the string is null when no choice is made. And the error does not occur if I don't the validation.
davcar
Try keeping it a string and add a "hidden" radio that has value string.empty. I'll update my example. I think it doesn't expect that a radio button can have a null value so it doesn't check before trying to convert it.
tvanfosson
Ah, I see, interesting to have a hidden default radio button.Thanks for the idea.
davcar
+1  A: 

Try adding the following line of code after the ModelState.AddModelError():

ModelState.SetModelValue("gender", ValueProvider["gender"]);
+6  A: 

This is very similar, if not equal to the checkbox issue: The Html.Checkbox() helper method generates a hidden field with a false value. If this field were missing, the browser would not send any data for uncheked boxes.

A radiobutton, however, is supposed to have a value, and the possible values can be more than one. In this case it is not so easy to handle the non-selection case, which I guess is the reason it isn't.

The workaround for me was to add a hidden field as follows:

<%= Html.RadioButton("gender", 1) %> Male
<%= Html.RadioButton("gender", 2) %> Female
<%= Html.Hidden("gender", null) %>
Thomas Eyde