ASP.NET with Knockout variable length list with combobox - how to bind? - asp.net-mvc

With the following ASP.NET models
public class User
{
public string Name { get; set; }
public LEmail LEmail { get; set; }
}
public class LEmail
{
public IList<CLabel> Labels;
public IList<CEmail> Emails;
}
public class CLabels
{
public IList<CLabel> Labels { get; set; }
}
public class CLabel
{
public string Name { get; set; }
}
public abstract class CEmail
{
public string SelectedLabel { get; set; }
public string Name { get; set; }
}
Filling it out with dummy data and sending to appropriate view as User object, I have the following knockout definitions in the view:
#using (Html.BeginForm("MyAction", "MyController", FormMethod.Post, new { id = "MyEditor" }))
{
#Html.EditorFor(m => #Model.LEmail)
<p>
<input type="submit" value="Save" data-bind="enable: Emails().length > 0" />
Cancel
</p>
<p data-bind="visible: saveFailed" class="error">A problem occurred saving the data.</p>
<div id="debug" style="clear: both">
<hr />
<h2>Debug:</h2>
<div data-bind="text: ko.toJSON(viewModel)"></div>
</div>
}
<script type="text/javascript">
$(function() {
ko.applyBindings(viewModel);
$("#profileEditorForm").validate({
submitHandler: function(form) {
if (viewModel.save())
window.location.href = "/";
return false;
}
});
});
var viewModel = {
Name: ko.observable("#Model.Name"),
Labels: ko.observableArray(#Html.Json(Model.LEmail.Labels) || []),
Emails: ko.observableArray(#Html.Json(Model.LEmail.Emails) || []),
addEmail: function() {
viewModel.Emails.push(#Html.Json(new CEmail()));
},
removeEmail: function(eml) {
viewModel.Emails.remove(eml);
},
saveFailed: ko.observable(false),
// Returns true if successful
save: function() {
var saveSuccess = false;
viewModel.saveFailed(false);
// Executed synchronously for simplicity
jQuery.ajax({
type: "POST",
url: "#Url.Action("MyAction", "MyController")",
data: ko.toJSON(viewModel),
dataType: "json",
contentType: "application/json",
success: function(returnedData) {
saveSuccess = returnedData.Success || false;
viewModel.saveFailed(!saveSuccess);
},
async: false
});
return saveSuccess;
}
};
</script>
And finally the editor template that is actually supposed to take care of variable length list that look like this:
#model MyDomain.ViewModels.LEmail
<table>
<tbody data-bind="template: { name: 'EmailsTemplate', foreach: Emails }" />
</table>
<button data-bind="click: addEmail">Add Email</button>
<script id="EmailsTemplate" type="text/html">
<tr>
<td>
#* PROBLEM IS HERE!! Labels won't show (they will it this code taken out of template) *#
<select data-bind="options: Labels"></select></td>
<td>
<input type="text" data-bind="value: Name, uniqueName: true" class="required" /></td>
<td>
Delete</td>
</tr>
</script>
Essentially I
cannot make it work in the EditorTemplate for combobox (or
dropdownlist). It won't attach to Labels no matter what I do. If I
take it outside the template somewhere else - it works as expected.
Also, based on selection to fill out the "SelectedValue" inside the Email - how to do that.
After everything is selected, (this must be simple) how to post it
all back without losing values on the way (its a super nested model
as you see).
Thank you very much in advance!

Labels is on your view model, not each email. Since the template is rendered within the context of a Knockout foreach binding, the binding context has changed to an email.
Here's how I'd write your view:
#model FiveW.ViewModels.LabeledEmail
<table>
<tbody data-bind="foreach: Emails">
<tr>
<td>
<select data-bind="options: $root.Labels, value: SelectedLabel"></select>
</td>
<td>
<input type="text" data-bind="value: Name, uniqueName: true" class="required" />
</td>
<td>
Delete
</td>
</tr>
</tbody>
</table>
<button data-bind="click: addEmail">Add Labeled Email</button>
The fix is in $root.Labels: we need to tell Knockout to use $root (your view model), since Labels is actually on your view model, and not on an individual email.
Also notice I didn't use an named template. This is preferable. Unless you are using the template in more than one place in your view, you should use anonymous, inline templates like I did above.

Related

How to create object that contains a list of object in a single form?

public class Basket
{
public int Id { get; set; }
public string Sharp { get; set; }
public string Material { get; set; }
public List<Fruit> Fruits { get; set; }
}
public class Fruit
{
public int Id { get; set; }
public string Color { get; set; }
public string Taste { get; set; }
}
With the above example, how could I create both Basket and Fruit in the same asp-form without using any JavaScript?
<form method="post" asp-controller="Basket" asp-action="Create">
<input asp-for="Material" />
<input asp-for="Sharp" />
#*I would like to also create custom amounts of new Fruit in this form.*#
<input type="submit" value="Submit" />
</form>
If my razor form is defined as the above example, how could I create custom amounts of Fruit and create Basket at the same form? It is possible to avoid using JavaScript in this case?
It is possible to avoid using JavaScript in this case?
Based on your scenario and current architecture what you need to do is, there should be a table where you would be adding your fruit object as it's a List<Fruit> Fruit kind of. As per your given code, your output should be as below:
So, I would say, Javascript would make it easier. If you would like to avoid javascript it wouldn't be impossible but would be costly and complex.
how could I create custom amounts of Fruit and create Basket at the
same form?
You could follow the below steps to achieve what you are trying to implement.
View:
#model DotNet6MVCWebApp.Models.Basket
<form method="post" asp-controller="Yonny" asp-action="Create">
<div class="form-group">
<label asp-for="Material" class="col-md-2 form-label"></label>
<input asp-for="Material" class="col-md-6 form-control" />
<span asp-validation-for="Material" class="form-span-error"></span>
</div>
<div class="form-group" style="padding-bottom:20px">
<label asp-for="Sharp" class="col-md-2 form-label"></label>
<input asp-for="Sharp" class="col-md-6 form-control" />
<span asp-validation-for="Sharp" class="form-span-error"></span>
</div>
#*I would like to also create custom amounts of new Fruit in this form.*#
<div style="padding-bottom:20px">
<button type="button" class="btn btn-primary" onclick="AddRow()">Add Fruit</button>
</div>
<div id="dataTable">
<table>
<thead>
<tr>
<th>Id</th>
<th>Color</th>
<th>Taste</th>
</tr>
</thead>
<tbody id="FruitList" data-count="0">
</tbody>
</table>
</div>
<input type="submit" class="btn btn-success" value="Submit" />
</form>
#section Scripts {
<script>
/*
. Hidding table on load
*/
document.getElementById('dataTable').style.display ='none';
function AddRow()
{
var countVal = parseInt($('#FruitList').attr('data-count'));
var html = '';
html += '<tr>';
html += '<td><input type="text" name="Fruits[' + countVal + '].Id" class="form-control"/></td>';
html += '<td><input type="text" name="Fruits[' + countVal + '].Color" class="form-control"/></td>';
html += '<td><input type="text" name="Fruits[' + countVal + '].Taste" class="form-control"/></td>';
html += '</tr>';
$('#FruitList').append(html);
countVal += 1;
$('#FruitList').attr('data-count', countVal);
/*
. Showing table when adding item into
*/
document.getElementById('dataTable').style.display ='block';
}
</script>
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
[Bind("Id,Material,Sharp,Fruits")] DotNet6MVCWebApp.Models.Basket basket)
{
if (ModelState.IsValid)
{
//Save Basket
_context.Add(basket);
await _context.SaveChangesAsync();
//Add Fruits List
foreach (var item in basket.Fruits)
{
_context.Add(item);
await _context.SaveChangesAsync();
}
return RedirectToAction(nameof(Create));
}
return View(basket);
}
Note:
If you somehow got null data while sending request to controller make sure your binding property that is Bind("Id,Material,Sharp,Fruits") are same as name="Fruits[' + countVal + '].Id" inside the javascript function
Output:

POSTing KnockoutJS model to MVC controller, List<T> in List<T> is empty

I have little experience with KnockoutJS so please bear with me.
I have a basic example that I want to get working so I can expand it to my project.
For this example you click the button and the AddSku method is called to return QuoteLine data with List.
However, as the diagram shows, BomDetails is empty:
Models:
public class QuoteViewModel
{
public int Id { get; set; }
public string QuoteName { get; set; }
public IList<QuoteLine> QuoteLines { get; set; }
}
public class QuoteLine
{
public string Description { get; set; }
public string Sku { get; set; }
public IList<BomDetail> BomDetails = new List<BomDetail>();
}
public class BomDetail
{
public string Name { get; set; }
}
Controller methods:
[HttpGet]
public ActionResult CreateQuote()
{
QuoteViewModel quote = new QuoteViewModel();
quote.Id = 10;
quote.QuoteName = "Test Create Quote";
quote.QuoteLines = new List<QuoteLine>();
return View(quote);
}
[HttpPost]
public ActionResult CreateQuote(QuoteViewModel viewModel)
{
if (ModelState.IsValid)
{
}
return RedirectToAction("CreateQuote");
}
[HttpGet]
public JsonResult AddSku()
{
QuoteLine line = new QuoteLine();
line.BomDetails = new List<BomDetail>();
line.Sku = "TestSku";
line.Description = "TestDescription";
line.BomDetails.Add(new BomDetail
{
Name = "BOM Detail 1"
});
line.BomDetails.Add(new BomDetail
{
Name = "BOM Detail 2",
});
return Json(line, JsonRequestBehavior.AllowGet);
}
The view:
#model EngineeringAssistantMVC.ViewModels.QuoteViewModel
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<div class="container-fluid">
<h2>Create Quote</h2>
#using (Html.BeginForm("CreateQuote", "Test", FormMethod.Post, new { #id = "createQuoteForm", #class = "form-horizontal", role = Model, enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.QuoteName)
<h3>Quote Lines</h3>
<table class="table master-detail-table" id="receiving-table">
<thead>
<tr>
<th>SKU</th>
<th>Description</th>
</tr>
</thead>
<tbody data-bind="foreach: QuoteLines">
<tr>
<td>
<input class='form-control' data-bind='value: $data.Sku, attr: { name: "QuoteLines[" + $index() + "].Sku" } ' type='text' readonly='readonly' />
</td>
<td>
<input class='form-control' data-bind='value: $data.Description, attr: { name: "QuoteLines[" + $index() + "].Description" } ' type='text' readonly='readonly' />
</td>
</tr>
<tr class="detail-row">
<td colspan="7">
<table class="table">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody data-bind="foreach: BomDetails">
<tr>
<td>
<input class='form-control' data-bind='value: $data.Name, attr: { name: "BomDetails[" + $index() + "].Name" } ' type='text' readonly='readonly' />
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h3>Add Sku from Db</h3>
<div class="row">
<div class="col-sm-2">
<input type="button" value="Add Sku" id="btnAddSku" class="btn btn-satcom-primary btn-wider" />
</div>
</div>
<h3>Submit</h3>
<div class="row">
<div class="col-sm-1">
<input type="submit" value="Submit" class="btn btn-satcom-primary btn-wider" id="btnSubmit" />
</div>
</div>
}
</div>
<script type="text/javascript">
$(function () {
quoteViewModel = new QuoteViewModel();
ko.applyBindings(quoteViewModel);
$('#btnAddSku').off().on('click', function () {
AddFromDb();
});
});
function QuoteViewModel() {
var self = this;
self.Id = ko.observable('#Model.Id');
self.QuoteName = ko.observable('#Model.QuoteName');
self.QuoteLines = ko.observableArray([]);
self.AddQuoteLine = function (sku, description, bomDetails) {
self.QuoteLines.push(new QuoteLineViewModel(sku, description, bomDetails));
}
}
function QuoteLineViewModel(sku, description, bomDetails) {
var self = this;
self.Sku = sku;
self.Description = description;
self.BomDetails = ko.observableArray([]);
$.each(bomDetails, function (index, item) {
self.BomDetails.push(new BomDetailViewModel(item.Name));
});
}
function BomDetailViewModel(name) {
var self = this;
self.Name = name;
}
function AddFromDb() {
$.ajax({
type: "GET",
url: '#Url.Action("AddSku", "Test")',
success: function (line) {
window.quoteViewModel.AddQuoteLine(line.Sku, line.Description, line.BomDetails);
}
});
}
I have tried so many things to get it populated but can't figure out where the problem lies, but I hope it is just something silly that I'm doing or not doing.
I have also tried using ko.mapping but I can't get that working either.
I managed to get this working so hopefully it will help somebody else in the future.
I removed the #Using (Html.BeginForm)
I changed the submit button to a normal button and added data-bind to a fucntion
<input type="button" value="Submit" class="btn btn-satcom-primary btn-wider" id="btnSubmit" data-bind="click:SaveToDatabase" />
The SaveToDatabase function:
self.SaveToDatabase = function () {
var dataToSend = ko.mapping.toJSON(self);
$.ajax({
type: "POST",
url: '#Url.Action("CreateQuote", "Test")',
contentType: 'application/json',
data: dataToSend,
success: function (data) {
},
error: function (err) {
console.log(err.responseText);
}
});
}
This correctly sends all the data to the controller.

fiil a list with values of a table

I'm new in learning asp.net MVC. I am writing because I am stubborn to a problem. Indeed, I have an application that should allow me to create an XML file that will be added to a database. At this point, I created my Model, and my view that allows me to create my XML tags.
I saw on this site that could add lines in my table via Javascript. What I have done just as you can see in the code.
I can not recover what is the value of each line that I can insert. Passing my view a list I created myself. I can recover both inputs I inserted in my controller.
My question is, there's another way to create a dynamic lines via javascript, then all the entries that the user has entered the recover and fill in my list? Then I know myself how I can play with my list. But I just want to recover all the different lines that my user has inserted. I am new in ASP.NET MVC. Any help , please
This is my code.
Model
public class XMLFile
{
public string TypeDoc { get; set; }
public string Type { get; set; }
public string Contenu { get; set; }
public string DocName { get; set; }
}
This is my controller :
public class XMLFileController : Controller
{
List<XMLFile> file = new List<XMLFile>();
[HttpGet]
public ActionResult Save()
{
file.AddRange( new XMLFile[] {
new XMLFile (){Type = "Titre", Contenu = "Chef de Service"},
new XMLFile (){Type = "Item", Contenu="Docteur Joel"}
});
return View(file);
}
[HttpPost]
public ActionResult Save(List<XMLFile> formCollection)
{
try
{
if (formCollection == null)
{
return Content("la liste est nulle");
}
else
{
return RedirectToAction("Create", "Layout");
}
}
catch
{
return View();
}
}
}
My view with a script for adding a new Row :
#using (Html.BeginForm("Save", "XMLFile", FormMethod.Post,new { #class = "form-horizontal", #role = "form", #id = "FormCreateXML" }))
{
<table class="table table-bordered" id="XMLFileTable">
<thead>
<tr>
<th>Type</th>
<th>Contenu</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
#for (int i = 0; i<Model.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(model=>model[i].Type, new {#class="form-control help-inline", #placeholder="type" })</td>
<td> #Html.TextBoxFor(model=>model[i].Contenu, new {#class="form-control help-inline", #placeholder="contenu" })</td>
<td> <input type="button" class="BtnPlus" value="+" /> </td>
<td> <input type="button" class="BtnMinus" value="-" /> </td>
</tr>
}
</tbody>
<tfoot>
<tr>
<td> <button type="submit" class="btn btn-success" >Save</button> </td>
</tr>
</tfoot>
</table>
}
</body>
<script type="text/javascript">
$(document).ready(function () {
function addRow() {
var html = '<tr>' +
'<td><input type="text" class="form-control" placeholder="type"></td>' +
'<td> <input type="text" class="form-control" placeholder="contenu"></td>' +
'<td> <input type="button" class="BtnPlus" value="+" /> </td>' +
'<td> <input type="button" class="BtnMinus" value="-" /></td>' +
'</tr>'
$(html).appendTo($("#XMLFileTable"))
};
function deleteRow() {
var par = $(this).parent().parent();
par.remove();
};
$("#XMLFileTable").on("click", ".BtnPlus", addRow);
$("#XMLFileTable").on("click", ".BtnMinus", deleteRow);
});
</script>

submit form as object parameter

So I have this form on my Index.cshtml view (Some data removed to shorten length),
I want to be able to submit it to the action "/Estimate/Index" defined as,
public ActionResult CreateEstimate(Estimate Est);
It creates the object just fine by serializing the data and submitting, my problem is that sub-object data is not inserted (and I understand that it doesn't cause it doesn't know how), I was curious if there was a way to tell it how to correctly create the objects in a simple/automated manner.
I've tried giving the NetUplift a different name,
Ex. name="NetUplift.Value" but this results in an internal server error (code 500).
Form in razor view - Index.cshtml
<form id="form-create-estimate">
<table id="table-create-estimate" class="table">
<tbody>
<tr>
<td class="td-header">ID</td>
<td id="td-id"><input name="ID" type="text" value="" /></td>
</tr>
<tr>
<td class="td-header">Name</td>
<td id="td-name"><input name="Name" type="text" value="" /></td>
</tr>
<tr>
<td class="td-header">Job Difficulty</td>
<td id="td-jobdifficulty"><input name="JobDifficulty" type="text" value="" /></td>
</tr>
<tr>
<td class="td-header">Type</td>
<td id="td-type"><input name="Type" type="text" value="" /></td>
</tr>
<tr>
<td class="td-header Unit">Net Uplift</td>
<td id="td-netuplift">
<input name="NetUplift" class="Unit" title="in PSF" type="number" value="" />
#*<input name="NetUplift.DataType" type="hidden" value="System.Int32" />
<input name="NetUplift.UnitOfMeasure" type="hidden" value="psf" />*#
</td>
</tr>
<tr>
<td class="td-header Unit">Joist Spacing</td>
<td id="td-joistspacing"><input name="JoistSpacing" class="FeetInch" title="in Feet' Inch''" type="text" value="" /></td>
</tr>
</tr>
<tr>
<td class="td-header">Drawing Location</td>
<td id="td-drawinglocation"><input name="DrawingLocation" type="text" value="" /></td>
</tr>
<tr>
<td><input id="button-submit-estimate" type="button" value="Create" /></td>
<td><input id="button-cancel-estimate" type="button" value="Cancel" /></td>
</tr>
</tbody>
</table>
</form>
Ajax Submit Script
// Do an Ajax Post to Submit the newly created Estimate.
function CreateEstimate() {
// Serialize the Form.
var Estimate = $("#form-create-estimate").serialize();
// Send the Serialized Form to the Server to be processed and returned
// as an updated page to asynchronously update this page.
// I guess instead of returning this entire page we could make the action
// return just a table row with the Estimate's Data, then append it to the table
// of estimates. It'll be saved to the list of estimates too so there won't be
// a chance to the lists on the Client and list on the server to be different.
$.ajax({
url: "/Estimate/CreateEstimate/",
datatype: "json",
data: Estimate,
contenttype: "application/json",
success: function (Data, Success) {
if (Data.Estimate !== undefined) {
// Create a Html Table Row from the Estimate.
// Append the Row to the Table.
// Hide the Dialog.
} else {
alert("No Error Occured; however, the Creation of the Estimate was unsuccessful. Please Try Again.");
}
},
error: function (xhr, ajaxOptions, thrownError) {
alert("An Error Occured. \n" + thrownError + "\n" + xhr.status);
}
});
}
Estimate Class
public class Estimate
{
public Int32 ID { get; set; }
public String JobDifficulty { get; set; }
public String Type { get; set; }
public String Name { get; set; }
public Unit NetUplift { get; set; }
public Unit JoistSpacing { get; set; }
public String DrawingLocation { get; set; }
}
Unit Class
public class Unit
{
public String Value { get; set; }
public Type DataType { get; set; }
public String UnitOfMeasure { get; set; }
public Unit(Type DataType, String Value, String UnitOfMeasure)
{
this.Value = Value;
this.DataType = DataType;
this.UnitOfMeasure = UnitOfMeasure;
}
}
Currently the action CreateEstimate(Estimate Est) recieves the data submitted except for sub-object values such as Unit (Estimate.NetUplift). How do I tell it to map NetUplift to Estimate.NetUplift.Value, and the two commented out input fields to NetUplift.DataType/NetUplift.UnitOfMeasure?
Is there a way to have the server just know that it should be mapped that way or do I have to do it before sending the form to the server?
You could try this:
$.fn.serializeToObject = function () {
var o = {};
var a = this.serializeArray();
$.each(a, function () {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
var estimate = $("#form-create-estimate").serializeToObject();

Binding check box values to list in the model mvc

My problem is I have to create a layout like the following Using MVC to assign rights to the user.
Now there is no problem in creating the check boxes I'll create it using the list of users.
but while submitting the form i should submit it to the list like below.
public class UserRightsViewModel
{
public UserRightsViewModel()
{
_screenrights = new List<ScreenRight>();
}
public String Id { get; set; }// Role Name
List<ScreenRight> _screenrights;
public List<ScreenRight> ScreenRights { get { return _screenrights; } set { _screenrights = value; } }
}
definition for screenRight is below
public class ScreenRight
{
public String UserName { get; set; }
public Boolean Select{ get; set; }
public Boolean Add{ get; set; }
public Boolean Edit{ get; set; }
,,,
}
Now while submitting the form how can i post it to the controller in the right format.
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new UserRightsViewModel
{
// Obviously those could come from some data source
ScreenRights = new[]
{
new ScreenRight { UserName = "Robert", Select = true, Add = false, Edit = false },
new ScreenRight { UserName = "John", Select = true, Add = true, Edit = false },
new ScreenRight { UserName = "Mike", Select = true, Add = true, Edit = false },
new ScreenRight { UserName = "Allan", Select = true, Add = true, Edit = true },
new ScreenRight { UserName = "Richard", Select = false, Add = false, Edit = false },
}.ToList()
};
return View(model);
}
[HttpPost]
public ActionResult Index(UserRightsViewModel model)
{
// The view model will be correctly populated here
// TODO: do some processing with them and redirect or
// render the same view passing it the view model
...
}
}
View:
#model UserRightsViewModel
#using (Html.BeginForm())
{
<table>
<thead>
<tr>
<th>User Id</th>
<th>Select</th>
<th>Add</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.ScreenRights.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(x => x.ScreenRights[i].UserName)
#Html.HiddenFor(x => x.ScreenRights[i].UserName)
</td>
<td>
#Html.CheckBoxFor(x => x.ScreenRights[i].Select)
</td>
<td>
#Html.CheckBoxFor(x => x.ScreenRights[i].Add)
</td>
<td>
#Html.CheckBoxFor(x => x.ScreenRights[i].Edit)
</td>
</tr>
}
</tbody>
</table>
<button type="submit">OK</button>
}
Further reading: Model Binding To a List.
To work backwards, your eventual HTML should look like this (ignoring your table)
<input type="hidden" value="0" name="ScreenRights[0].Select"/>
<input type="checkbox" name="ScreenRights[0].Select"/>
<input type="hidden" value="0" name="ScreenRights[0].Add"/>
<input type="checkbox" name="ScreenRights[0].Add"/>
<input type="hidden" value="0" name="ScreenRights[0].Edit"/>
<input type="checkbox" name="ScreenRights[0].Edit"/>
<input type="hidden" value="0" name="ScreenRights[1].Select"/>
<input type="checkbox" name="ScreenRights[1].Select"/>
<input type="hidden" value="0" name="ScreenRights[1].Add"/>
<input type="checkbox" name="ScreenRights[1].Add"/>
<input type="hidden" value="0" name="ScreenRights[1].Edit"/>
<input type="checkbox" name="ScreenRights[1].Edit"/>
The idea is that the index order shows up in the [i] array portion of the property name, and then chain into the next property. It should bind up in the same order as your i's. The other key here is the check boxes only bind up with CHECKED attribute, so value has no meaning in terms of the binder. That's why you have the hidden input in front. The binder will always assign false, and then override to true if the checkbox is checked. You can verify this resulting html with Html.CheckBoxFor on a simple model.
In terms of getting your HTML to look like that, you can do it manually, or you can make use of the built-in framework.
I'm pretty sure you can just do this in a for Loop (i as the iterator)
#Html.CheckBoxFor(m => m.ScreenRights[i].Select)
#Html.CheckBoxFor(m => m.ScreenRights[i].Add)
#Html.CheckBoxFor(m => m.ScreenRights[i].Add)

Resources