tags:

views:

3788

answers:

8

I have two checkboxes in my MVC app which are both boolean / bit fields. "NotifyEmail" & "NotifySMS".

Whenever I post back to the server and an error occurs, the state of the checkbox is gone event though I set attempted values.

+2  A: 

MVC does not have ViewState like WebForms does - this means that you now have the responsibility of maintaining those values.

This will require that you store whether or not the checkbox was checked and then applying that setting to the checkbox again before the page is rendered to the browser in your View.

Andrew Hare
A: 

Adam,

Assuming you understand that MVC does not include server functionality to handle Asp.Net post-backs, If you need to send a message back to the server that informs your application that the check box was checked, then it's probably best to do that with Javascript and an Ajax request. .Net MVC includes a the JQuery Javascript library so doing this may be easier then you think. Here's an SO post that kind of covers how to use checkboxes correctly in MVC.

Otherwise, realize that MVC doesn't support post-back, and like Andrew said above, doesn't support Asp.Net view-state either. However, you could go back to the old school way of view-state and use a regular HTML hidden input element with a little bit of javascript to maintain your state.

That all being said, you might want to take a minute to read this article. A lot of MVC frameworks assume that you will be using the Post -> Redirect -> Get pattern for handling user input in your web forms. If you continue to use your current pattern of post-back and view state, you may run into more problems in the future like the one you're currently trying to solve.

matt_dev
The answer in the referenced SO post is fine if all you have are checkboxes. Not all forms are like that
Thomas Eyde
A: 

How are you specifying your checkbox HTML? Binding will require a hidden input element in addition to the checkbox input element. Html.Checkbox will handle this for you, or you can study how it does it and do it yourself.

Tim Scott
A: 

Using an HTML Helper such as Html.CheckBox will persist the state automatically on POST.

Andrei Rinea
It gets a whole lot more complicated than this and isn't always the case when you have dynamic checkboxes in a list, rather than just a single/simple checkbox.
Aaron
+3  A: 

Update: This is fixed in RC2.

This question together with this one,

Html.Checkbox does not preserve its state in ASP.net MVC,

addresses a very undocumented feature of the ASPNET.MVC RC1. I have been searching around for hours to find a good answer, but there are very few to find.

There is a bug, apparently, which prohibit checkboxes and radiobuttons to maintain their state from ModelState. As we also know by now, is that these two controls are handled specially by the Html helpers.

The best I managed to come up with, was to build my own ViewBinder:

From a very simple view:

<h2>Keep checkbox value between posts</h2>

<% using (Html.BeginForm("update", "checkbox")) {%>

<p><%= Html.CheckBox("a") %></p>
<p><%= Html.CheckBox("b") %></p>
<p><%= Html.TextBox("dummy") %></p>

<input type="submit" value="update" />

<% } %>

Associated with an equally simple controller:

public class CheckboxController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Update()
    {
        var binder = new ViewBinder(ViewData, ValueProvider);
        binder.UpdateBooleanValues("a", "b");
        binder.UpdateMissingValues();

        return View("Index");
    }
}

And a simple class to make it all work:

internal class ViewBinder
{
    private readonly IDictionary<string, ValueProviderResult> valueProvider;
    private readonly ViewDataDictionary viewData;

    public ViewBinder(ViewDataDictionary viewData, IDictionary<string, ValueProviderResult> valueProvider)
    {
        this.valueProvider = valueProvider;
        this.viewData = viewData;
    }

    public void UpdateMissingValues()
    {
        foreach (var key in valueProvider.Keys)
        {
            if (ValueIsMissing(key)) UpdateValue(key);
        }
    }

    public void UpdateBooleanValues(params string[] names)
    {
        foreach (var name in names)
        {
            UpdateValue(name, BooleanValueFor(name));
        }
    }

    private bool BooleanValueFor(string name)
    {
        return valueProvider[name].AttemptedValue != "false";
    }

    private bool ValueIsMissing(string key)
    {
        return viewData.ContainsKey(key) == false;
    }

    private void UpdateValue(string key)
    {
        var value = valueProvider[key].AttemptedValue;
        UpdateValue(key, value);
    }

    private void UpdateValue(string key, object value)
    {
        viewData[key] = value;
    }
}
Thomas Eyde
+1  A: 

Why not something as simple as this?

<%=Html.CheckBox("AgreeToRules", (Request.Form["AgreeToRules"] ?? string.Empty).Contains("true"))%>
Chris
A: 

Andrei Rinea is partially right. But I have done - is use the helpers and pass back into the page the previous values into the DataClass (accessed by Model. etc). It works well.

Joe