Bruce Patin
Bruce Patin

Reputation: 355

How to use CheckBox in View _CreateOrEdit.cshtml for an integer or character database field

MVC 3, EntityFramework 4.1, Database First, Razor customization:

I have an old database that sometimes uses Int16 or Char types for a field that must appear as a CheckBox in the MVC _CreateOrEdit.cshtml View. If it is an Int, 1=true and 0=false. If it is a Char, "Y"=true and "N"=false. This is too much for the Entity Framework to convert automatically. For the Details View, I can use:

@Html.CheckBox("SampleChkInt", Model.SampleChkInt==1?true:false)

But this won't work in place of EditorFor in the _CreateOrEdit.cshtml View. How to do this? I was thinking of a custom HtmlHelper, but the examples I've found don't show me how to tell EntityFramework to update the database properly. There are still other such customizations that I might like to do, where the MVC View does not match the database cleanly enough for EntityFramework to do an update. Answering this question would be a good example. I am working on a sample project, using the following automatically generated (so I can't make changes to it) model class:

namespace AaWeb.Models
{
    using System;
    using System.Collections.Generic;

    public partial class Sample
    {
        public int SampleId { get; set; }
        public Nullable<bool> SampleChkBit { get; set; }
        public Nullable<short> SampleChkInt { get; set; }
        public Nullable<System.DateTime> SampleDate { get; set; }
        public string SampleHtml { get; set; }
        public Nullable<int> SampleInt { get; set; }
        public Nullable<short> SampleYesNo { get; set; }
        public string Title { get; set; }
        public byte[] ConcurrencyToken { get; set; }
    }
}

Upvotes: 2

Views: 9252

Answers (3)

Bruce Patin
Bruce Patin

Reputation: 355

Here is the way to go from checkbox to database, without the special code in the controller:

// The following statement added to the Application_Start method of Global.asax.cs is what makes this class apply to a specific entity:
// ModelBinders.Binders.Add(typeof(AaWeb.Models.Sample), new AaWeb.Models.SampleBinder());

// There are two ways to do this, choose one:
// 1. Declare a class that extends IModelBinder, and supply all values of the entity (a big bother).
// 2. Declare a class extending DefaultModelBinder, and check for and supply only the exceptions (much better).

// This must supply all values of the entity:
//public class SampleBinder : IModelBinder 
//{
//    public object BindModel(ControllerContext cc, ModelBindingContext mbc)
//    {
//        Sample samp = new Sample();
//        samp.SampleId = System.Convert.ToInt32(cc.HttpContext.Request.Form["SampleId"]);
//        // Continue to specify all of the rest of the values of the Sample entity from the form, as done in the above statement.
//        // ...
//        return samp;
//    }
//}

// This must check the property names and supply appropriate values from the FormCollection.
// The base.BindProperty must be executed at the end, to make sure everything not specified is take care of.
public class SampleBinder : DefaultModelBinder
{
    protected override void BindProperty( ControllerContext cc, ModelBindingContext mbc, System.ComponentModel.PropertyDescriptor pd)
    {
        if (pd.Name == "SampleChkInt")
        {
            // This converts the "true" or "false" of a checkbox to an integer 1 or 0 for the database.
            pd.SetValue(mbc.Model, (Nullable<Int16>)(cc.HttpContext.Request.Form["SampleChkIntBool"].Substring(0, 4) == "true" ? 1 : 0));

            // To do the same in the reverse direction, from database to view, use pd.GetValue(Sample object).

            return;
        }

        // Need the following to get all of the values not specified in this BindProperty method:
        base.BindProperty(cc, mbc, pd);
    } 
}

Upvotes: 0

Bruce Patin
Bruce Patin

Reputation: 355

I figured it out. Do not need a model binder or Html Helper extension:

In _CreateOrEdit.cshtml, I made up a new name SampleChkIntBool for the checkbox, and set it according to the value of the model SampleChkInt:

@Html.CheckBox("SampleChkIntBool", Model == null ? false : ( Model.SampleChkInt == 1 ? true : false ), new { @value = "true" })

Then, in the [HttpPost] Create and Edit methods of the Sample.Controller, I use Request["SampleChkIntBool"] to get the value of SampleChkIntBool and use it to set the model SampleChkInt before saving:

string value = Request["SampleChkIntBool"];
// @Html.CheckBox always generates a hidden field of same name and value false after checkbox,
// so that something is always returned, even if the checkbox is not checked.
// Because of this, the returned string is "true,false" if checked, and I only look at the first value.
if (value.Substring(0, 4) == "true") { sample.SampleChkInt = 1; } else { sample.SampleChkInt = 0; }

Upvotes: 1

Adam Tuliper
Adam Tuliper

Reputation: 30152

I believe a custom model binder would be in order here to handle the various mappings to your model.

ASP.NET MVC Model Binder for Generic Type

etc etc

Upvotes: 0

Related Questions