MVC Controllers get value of 'Checkboxlist' - asp.net-mvc

I'm probably a idiot here but I'm having problems getting the value of whether or not a checkbox is checked/selected or not. Here's what I've got so far:
In my Model:
public IEnumerable<SelectListItem> Insurers
{
get
{
var list = new List<SelectListItem>();
string zInsurersList = "Age UK,Be Wiser,Call Connection,Churchill,Sainsbury's,Direct Line,Hastings Direct,LV=,Nationwide,RIAS,Swinton";
string[] zInsurers = zInsurersList.Split(',');
foreach (string aInsurer in zInsurers)
{
list.Add(new SelectListItem() { Text = aInsurer, Value = aInsurer, Selected=false});
}
return list;
}
}
}
And my view:
#foreach (var insurer in #Model.Insurers)
{
var zInsurer = insurer.Text;
var zValue = insurer.Value;
<tr>
<td style="width: 120px; height: 35px;"><span id="#zInsurer">#zInsurer</span></td>
<td style="width: 40px; height: 35px;"><input id="#zInsurer" type="checkbox" name="#zInsurer"></td>
</tr>
}
So in my controller I'm trying to loop the list and get the value of whether or not the user has selected the option:
foreach (var item in model.Insurers)
{
//if (item.GetType() == typeof(CheckBox))
//string controlVal = ((SelectListItem)item).Selected.ToString();
zInsurers = zInsurers + item.Text + " " + ((SelectListItem)item).Selected.ToString() + "<br/>";
}
But the value always returns false.
Could someone spare a few mins to highlight my stupidity please?
Thanks,
Craig

There are a lot of ways to do it. I normally add String Array in model to collect selected values.
public string[] SelectedInsurers { get; set; }
<input type="checkbox" name="SelectedInsurers" value="#insurer.Value" />
Here is the sample code -
MyModel
public class MyModel
{
public string[] SelectedInsurers { get; set; }
public IEnumerable<SelectListItem> Insurers
{
get
{
var list = new List<SelectListItem>();
string zInsurersList = "Age UK,Be Wiser,Call Connection,Churchill,Sainsbury's,Direct Line,Hastings Direct,LV=,Nationwide,RIAS,Swinton";
string[] zInsurers = zInsurersList.Split(',');
foreach (string aInsurer in zInsurers)
{
list.Add(new SelectListItem { Text = aInsurer, Value = aInsurer, Selected = false });
}
return list;
}
}
}
Action Methods
public ActionResult Index()
{
return View(new MyModel());
}
[HttpPost]
public ActionResult Index(MyModel model)
{
return View();
}
View
#using (Html.BeginForm())
{
foreach (var insurer in #Model.Insurers)
{
<input type="checkbox" name="SelectedInsurers" value="#insurer.Value" /> #insurer.Text<br/>
}
<input type="submit" value="Post Back" />
}
Result

Firstly, your property Insurers should not be IEnumerable<SelectListItem> (tha'ts for binding a collection to a dropdownlist), and in any case, that kind of logic does not belong in a getter (and whats the point of creating a comma delimited string and then splitting it? - just create an array of strings in the first place!). Its not really clear exactly what you trying to do, but you should be creating a view model and doing it the MVC way and making use of its model binding features
View model
public class InsurerVM
{
public string Name { get; set; }
public bool IsSelected { get; set; }
}
Controller
public ActionResult Edit()
{
// This should be loaded from some data source
string[] insurers = new string[] { "Age UK", "Be Wiser", "Call Connection" };
List<InsurerVM> model = insurers.Select(i => new InsurerVM() { Name = i }).ToList();
return View(model);
}
View
#model List<InsurerVM>
#using(Html.BeginForm())
{
for (int i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(m => m[i].Name)
#Html.CheckBoxFor(m => m[i].IsSelected)
#Html.LabelFor(m => m.[i].IsSelected, Model[i].Name)
}
<input type="submit" value="Save" />
}
Post method
[HttpPost]
public ActionResult Edit(IEnumerable<InsurerVM> model)
{
// loop each item to get the insurer name and the value indicating if it has been selected
foreach(InsurerVM insurer in model)
{
....
}
}
In reality, Insurers would be an object with an ID and other properties so it can be identified and have a relationship with other entities.
As to why you code is not working. Your property does not have a setter so nothing that posted back could be bound anyway. All the method is doing is initializing your model then calling the getter which creates a new IEnumerable<SelectListItem> (identical to the one you sent to the view in the first place). Not that it would have mattered anyway, your checkboxes have name attributes name="Age_UK", name=Be_Wiser" etc which have absolutely no relationship to your model so cant be bound

That is because the modelbinding can't process your values.
You should look into model binding.
Try something like this:
#for (var countInsurer = 0; Model.Insurers.Count > countInsurer++)
{
var zInsurer = insurer.Text;
var zValue = insurer.Value;
<tr>
<td style="width: 120px; height: 35px;"><span id="#zInsurer">#zInsurer</span></td>
<td style="width: 40px; height: 35px;">#Html.CheckBoxFor(m=> Model.Insurers[countInsurer], new {name = zInsurer})</td>
</tr>
}

#for(int i = 0; i < Model.List.Count; i++)
{
#Html.CheckBoxFor(m => Model.List[i].IsChecked, htmlAttributes: new { #class = "control-label col-md-2" })
#Model.List[i].Name
#Html.HiddenFor(m => Model.List[i].ID)
#Html.HiddenFor(m => Model.List[i].Name)
<br />
}
in controller
StringBuilder sb = new StringBuilder();
foreach (var item in objDetail.List)
{
if (item.IsChecked)
{
sb.Append(item.Value + ",");
}
}
ViewBag.Loc = "Your preferred work locations are " + sb.ToString();

I Get Module And Right from Module and Rights Table How to Send All Data To RoleRight Table All Checkbox value
public class RoleRightModel
{
public ModuleModel _ModuleModel { get; set; }
public RightsModel _RightsModel { get; set; }
public RolesModel _RolesModel { get; set; }
public List<ModuleModel> _ModuleModelList { get; set; }
public List<RightsModel> _RightsModelList { get; set; }
public List<RolesModel> _RolesModelList { get; set; }
public List<RoleRightModel> _RoleRightModelList { get; set; }
public int RoleRightID { get; set; }
public int RoleID { get; set; }
public int ModuleID { get; set; }
public int FormMode { get; set; }
public int RightCode { get; set; }
public bool? RowInternal { get; set; }
public byte? IsAuthorised { get; set; }
public int? CreationID { get; set; }
public DateTime? CreationDate { get; set; }
public int? LastModificationID { get; set; }
public DateTime? LastModificationDate { get; set; }
public byte? RowStatus { get; set; }
public string RoleName { get; set; }
}
Razor
#foreach(var item in Model._ModuleModelList.Where(x => x.Level == 1))
{
<ul style="display: block;">
<li><i class="fa fa-plus"></i>
<label>
#if (item.Level == 1)
{
<input id="node-0-1" data-id="custom-1" type="checkbox" name="Module" value="#item.ModuleID"#(Model._ModuleModel.ModuleID)? "checked":"">
#item.ModuleName
}
</label>
#foreach (var lavel1 in Model._ModuleModelList.Where(x => x.ParentModuleID == item.ModuleID))
{
<ul>
<li><i class="fa fa-plus"></i>
<label>
<input id="node-0-1-1" data-id="custom-1-1" type="checkbox" name="Module" value="#lavel1.ModuleID"#(Model._ModuleModel.ModuleID)? "checked":"">
#lavel1.ModuleName
</label>
#foreach (var lavel2 in Model._ModuleModelList.Where(x => x.ParentModuleID == lavel1.ModuleID))
{
<ul>
<li><i class="fa fa-plus"></i>
<label>
<input id="node-0-1-1-1" data-id="custom-1-1-1" type="checkbox" name="Module" value="#lavel2.ModuleID"#(Model._ModuleModel.ModuleID)? "checked":"">
#lavel2.ModuleName
</label>
#foreach (var lavel3 in Model._RightsModelList.Where(x => x.ModuleId == lavel2.ModuleID))
{
<ul>
<li>
<label>
<input id="node-0-1-1-1-1" data-id="custom-1-1-1-1" type="checkbox" name="Right" value="#lavel3.RightID"#(Model._RightsModel.RightID)? "checked":"">
#lavel3.RightName
</label>
</li>
</ul>
}
</li>
</ul>
}
</li>
</ul>
}
</li>
</ul>
}

Related

ViewModel, View and PartialView Post to Controller

I am trying to do the following: I have two models, header and List(details), sent to a view by a view model. When loading the main view, a dropdown is displayed from a list in the ViewModel.header model previously loaded. When you click on that dropdown, a partial view is loaded with some values, filtered by the value of the ddl, of the ViewModel.List(details) for the user to complete the information. So far everything works fine, but when doing the Post, controller it receives the ViewModel.List(details) in null.
what am I doing wrong?
Header
public class StockTransactionsHeader
{
[Key]
public int TransactionHeaderID { get; set; }
public DateTime TransactionDate { get; set; }
public string TransactionDocument { get; set; }
public int CategoryID { get; set; }
[NotMapped]
public List<SelectList> CategoryCollection { get; set; }
public virtual List<StockTransactionsDetails> StockTransactionsDetails { get; set; }
}
Details
public class StockTransactionsDetails
{
[Key]
public int TransactionDetailID { get; set; }
public int TransactionHeaderID { get; set; }
public int ProductID { get; set; }
public decimal Qty { get; set; }
public decimal Amount { get; set; }
public decimal TransactionAmount { get; set; }
[NotMapped]
public string ProductDescription { get; set; }
public virtual StockTransactionsHeader StockTransactionsHeader { get; set; }
}
ViewModel
public class StockTransactionsViewModel
{
public StockTransactionsHeader StockTransactionsHeader { get; set; }
public List<StockTransactionsDetails> StockTransactionsDetails { get; set; }
}
Controller Create
public ActionResult Create()
{
var stockTransactions = new StockTransactionsViewModel();
stockTransactions.StockTransactionsHeader = GetHeaderCategories();
return View(stockTransactions);
}
GetHeaderCategories()
private StockTransactionsHeader GetHeaderCategories()
{
var header = new StockTransactionsHeader();
header.CategoryCollection = CommonServices.GetSelecList((int)DeliveryCommonHelper.ConfigurationType.Categoria);
return header;
}
MainView
#model DeliverySolutionCommon.ViewModels.StockTransactionsViewModel
#using (Html.BeginForm())
{
<div class="form-row">
<div id="partialView" class="table-responsive">
</div>
</div>
<div class="form-group">
<div class="col-md-2">
<input type="submit" value=" Procesar " class="btn btn-warning" />
</div>
</div>
}
Script to load partial view
<script>
$(document).ready(function () {
$("#Category").on("change", function () {
autoFiltro();
})
})
function autoFiltro() {
var url = "#Url.Action("GetProductsListByCategory", "StockTransactions")";
var id = $("#Category").val();
var data = { idCategory: id };
$.post(url, data).done(function (data) {
$("#partialView").html(data);
})
}
</script>
GetProductsListByCategory
[HttpPost]
public PartialViewResult GetProductsListByCategory(int idCategory)
{
var products = ProductsServices.GetProductsListByCategory(idCategory);
var stockTransactions = new StockTransactionsViewModel();
stockTransactions.StockTransactionsDetails = GetTransactionsDetails(products);
return PartialView("_createStockTransactions", stockTransactions);
}
GetTransactionsDetails
private List<StockTransactionsDetails> GetTransactionsDetails (List<Products> products)
{
var details = new List<StockTransactionsDetails>();
foreach (var item in products)
{
StockTransactionsDetails detail = new StockTransactionsDetails();
detail.ProductID = item.ProductID;
detail.ProductDescription = item.Description;
details.Add(detail);
}
return details;
}
PartialView
#model DeliverySolutionCommon.ViewModels.StockTransactionsViewModel
<table class="table table-sm table-bordered table-striped">
#foreach (var item in Model.StockTransactionsDetails)
{
<tr class="d-flex">
<td class="col-7">
#Html.DisplayFor(modelItem => item.ProductDescription)
</td>
<td class="col-1">
#Html.EditorFor(modelItem => item.Qty, new { htmlAttributes
= new { #class = "form-control" } })
</td>
<td class="col-2">
#Html.EditorFor(modelItem => item.Amount, new {
htmlAttributes = new { #class = "form-control" } })
</td>
<td class="col-2">
#Html.EditorFor(modelItem => item.TransactionAmount, new {
htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</table>
Aaaaand finally Create Post
[HttpPost]
public ActionResult Create(StockTransactionsViewModel stockTransactionsView)
{
// StockStransactionsView.StockTransactionsDetails = null
}
The problem is you are posting back a list and there is no indexing information in your HTML... MVC model binder does not know how to put the items in a list without the index info...
you can try something like this:
#for (int i = 0; i < Model.StockTransactionsDetails.Count, i++)
{
<tr class="d-flex">
<td class="col-7">
#Html.EditorFor(modelItem => Model[i].Amount, new {
htmlAttributes = new { #class = "form-control" } })
</td>
// more code...
This would add the indexing information to your HTML...
Alternatively you can use EditorTemplate... something like this:
// Note that EditorFor template would iterate the list item for you
#Html.EditorFor(m => m.Model.StockTransactionsDetails)
This tutorial might help

Error in my upload page after i changed my model [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
I added the code to show many checkboxes from my table (HairTags) and in my form CreationUpload.cshtml i got the following error :
An exception of type 'System.NullReferenceException' occurred in
App_Web eba142hb.dll but was not handled in user code Additional
information: Object reference not set to an instance of an object.
Object reference not set to an instance of an object.
<div class="col-md-12">
#for (int i = 0; i < Model.CreationHairTags.Count; i++)
{
#Html.CheckBoxFor(m => Model.CreationHairTags[i].IsChecked)
#Model.CreationHairTags[i].Text
#Html.HiddenFor(m => Model.CreationHairTags[i].Value)
#Html.HiddenFor(m => Model.CreationHairTags[i].Text)<br />
}
</div>
this is my model Creation.cs (in bold the added code)
namespace HairCollection3.Models
{
public class Creation
{
public string UserId { get; set; }
[Key]
public int CreationId { get; set; }
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ViewRes.ValidationStrings))]
[Display(Name = "Sex", ResourceType = typeof(ViewRes.Names))]
public string CreationSex { get; set; }
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ViewRes.ValidationStrings))]
[Display(Name = "CreationTitle", ResourceType = typeof(ViewRes.NamesCreation))]
[StringLength(2000)]
[AllowHtml]
public string CreationTitle { get; set; }
public string CreationPhotoBis { get; set; }
public string Creationtag { get; set; }
public virtual ICollection<CreationLike> CreationLikes { get; set; }
}
public class CreationLike
{
public int CreationId { get; set; }
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
[Key]
public int CreationLikeId { get; set; }
public virtual Creation ParentCreation { get; set; }
}
public class HairTag
{
[Key]
public int HairTagId { get; set; }
[Required]
public string HairTagTitle { get; set; }
[Required]
public string HairTagType { get; set; }
[Required]
public int HairTagOrder { get; set; }
}
***//CHECKBOXES
public class HairTagModel
{
[Key]
public int Value { get; set; }
public string Text { get; set; }
public bool IsChecked { get; set; }
}
public class HairTagList
{
private ApplicationDbContext creationdb = new ApplicationDbContext();
public HairTagList()
{
var HairTagList = creationdb.HairTags.ToList();
List<HairTagModel> obj = new List<HairTagModel>();
foreach (var tags in HairTagList)
{
obj.Add(new HairTagModel
{
Text = tags.HairTagTitle,
Value = tags.HairTagId,
IsChecked = false
});
}
this.CreationHairTags = obj;
}
public List<HairTagModel> CreationHairTags { get; set; }
//public List<HairTagModel> ListHairTags { get; set; }
}
public class CreationHairTagsModel
{
public Creation Creation { get; set; }
public List<HairTagModel> CreationHairTags { get; set; }
}
}***
My controller CreationController.cs
// GET: /Creation/CreationUpload
[Authorize]
public ActionResult CreationUpload()
{
CreationHairTagsModel creation = new CreationHairTagsModel();
return View(creation);
//return View();
}
// POST: /Creation/CreationUpload
// Afin de déjouer les attaques par sur-validation, activez les propriétés spécifiques que vous voulez lier. Pour
// plus de détails, voir http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult CreationUpload([Bind(Include = "CreationId,CreationSex,CreationTitle,CreationPhotoBis,CreationHairTags")] CreationHairTagsModel creation, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
// update each field manually
foreach (var file in files)
{
if (file != null)
{
if (file.ContentLength > 0)
{
....CODE UPLOAD HIDDEN....
//Avoid Script
var CreationTitletocheck = Regex.Replace(creation.Creation.CreationTitle, #"<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>", string.Empty);
CreationTitletocheck = Regex.Replace(CreationTitletocheck, #"(?></?\w+)(?>(?:[^>'""]+|'[^']*'|""[^""]*"")*)>", string.Empty);
creation.Creation.CreationTitle = CreationTitletocheck;
//Tags
StringBuilder sb = new StringBuilder();
foreach (var item in creation.CreationHairTags)
{
if (item.IsChecked)
{
sb.Append(item.Text + ",");
}
}
creation.Creation.Creationtag = sb.ToString();
creation.Creation.UserId = User.Identity.GetUserId();
db.Creations.Add(creation.Creation);
db.SaveChanges();
}
}
}
}
//UserId
return RedirectToAction("CreationList", "Creation", new { UserId = User.Identity.GetUserId() });
}
return View(creation);
}
My page of upload CreationUpload.cshtml
#model HairCollection3.Models.CreationHairTagsModel
#using Microsoft.AspNet.Identity
#{
ViewBag.Title = ViewRes.NamesCreation.CreationUploadTitle;
}
<div class="col-sm-12 col-md-12 chpagetop">
<h1>#ViewRes.Shared.PublishAPhoto</h1>
<hr />
#using (Html.BeginForm("CreationUpload", "Creation", FormMethod.Post, new { id = "CreationUpload", enctype = "multipart/form-data", onsubmit = "$('#creationloading').show(); $('#creationform').hide();" }))
{
#Html.AntiForgeryToken()
<div class="col-md-12" id="creationloading" style="display:none">
<div id="progress">
<p>#ViewRes.Shared.UploadPhotoProgress<strong>0%</strong></p>
<progress value="5" min="0" max="100"><span></span></progress>
</div>
</div>
<div class="col-md-12" id="creationform">
<div class="col-md-12">
#Html.ValidationMessageFor(m => m.Creation.CreationSex)
#Html.RadioButtonFor(m => m.Creation.CreationSex, "F", new { #checked = true }) #ViewRes.Shared.WomanHairstyle #Html.RadioButtonFor(m => m.Creation.CreationSex, "M") #ViewRes.Shared.ManHairstyle
</div>
<div class="col-md-12">
#Html.ValidationMessageFor(m => m.Creation.CreationTitle)
#Html.TextBoxFor(m => m.Creation.CreationTitle, new { #class = "inputplaceholderviolet wid100x100", placeholder = HttpUtility.HtmlDecode(Html.DisplayNameFor(m => m.Creation.CreationTitle).ToHtmlString()), onfocus = "this.placeholder = ''", onblur = "this.placeholder = '" + HttpUtility.HtmlDecode(Html.DisplayNameFor(m => m.Creation.CreationTitle).ToHtmlString()) + "'" })
</div>
<div class="col-md-12">
#for (int i = 0; i < Model.CreationHairTags.Count; i++)
{
#Html.CheckBoxFor(m => Model.CreationHairTags[i].IsChecked)
#Model.CreationHairTags[i].Text
#Html.HiddenFor(m => Model.CreationHairTags[i].Value)
#Html.HiddenFor(m => Model.CreationHairTags[i].Text)<br />
}
</div>
<div class="col-md-12" style="text-align: center">
<p style="display: inline-block">
<input type="file" accept="image/*" onchange="loadFile(event)" name="files" id="file1" translate="yes" data-val="true" data-val-required="A File is required." class="wid100x100" /><label for="file1"></label>
<img id="output" style="max-width:200px;"/>
</p>
</div>
<div class="col-sm-12 col-md-12 chpagetopdiv">
<button type="submit" title="#ViewRes.Shared.Publish"><span class="glyphicon glyphicon-share-alt"></span> #ViewRes.Shared.Publish</button>
</div>
</div>
}
</div>
What is wrong in my code please help and explain ?
Important: In C#, every collection must be initialized before being accessed
The error occurs when you are trying to access from the View to the collection CreationHairTags, which is not initialized. Replace your model to initialize collection in the class constructor:
public class CreationHairTagsModel
{
public Creation Creation { get; set; }
public List<HairTagModel> CreationHairTags { get; set; }
public CreationHairTagsModel()
{
CreationHairTags = new List<HairTagModel>();
}
}

IEnumerable Property can't return the value

I have ViewModel
public class Magazine_ViewModel
{
public int MagId { get; set; }
public string MagNo { get; set; }
public IEnumerable<Titles_ViewModel> Titles { get; set; }
}
public class Titles_ViewModel
{
public int TitleId { get; set; }
public string TitleName { get; set; }
public int pos { get; set; }
}
in Controller i do like this
var viewModel = new Magazine_ViewModel
{
Titles = numberTitles
.Select(c => new Titles_ViewModel
{
TitleId = c.TitleId,
TitleName = c.Title.TitleText,
pos=c.position
}).ToList()
};
viewModel.MagNo = magazine.MagNo.ToString();
viewModel.MagId = magazine.Id;
when i check viewModel.Titles has n records which is right.
now in the view part
<div class="editor-label">
#Html.LabelFor(model => model.MagNo)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MagNo)
</div>
<div class="editor-field">
#Html.ListBox("wholeTitles")
<input type="button" name="add" id="add" value="add" onclick="addValue()" /><input type="button" name="remove" id="remove" value="remove" onclick="removeValue()"/>
#Html.ListBoxFor(model => model.Titles, new SelectList(Model.Titles, "TitleId", "TitleName"))
</div>
<p>
<input type="submit" value="Save" />
</p>
I add new Titles from first ListBox(wholeTitles) to second ListBox(Titles). Now the submit button is press and i want to add the new Titles to the database.
This is what i do in Post Action
[HttpPost]
public ActionResult EditTitle(Magazine_ViewModel magazineViewModel)
{
if (ModelState.IsValid)
{
var magazineTitles = new Magazine
{
Id = magazineViewModel.MagId,
NumberTitles = new List<NumberTitle>()
};
foreach (var numberTitle in magazineViewModel.Titles)
{
NumberTitle newNumberTitle = new NumberTitle();
newNumberTitle.MagazineId = magazineViewModel.MagId;
newNumberTitle.TitleId = numberTitle.TitleId;
newNumberTitle.position = 0;
unitOfWork.NumberTitleRepository.Insert(newNumberTitle);
}
}
return View();
}
but magazineViewModel.Titles shows null, I don't know what should I check to see why it is null value.
Your values not get posted back to the server. Use below in your View. This should post the titles back to the Server within the same ViewModel
for (var i = 0; i < Model.Titles.Count(); i++)
{
#Html.HiddenFor(m => m.Titles[i].TitleId)
#Html.HiddenFor(m => m.Titles[i].TitleName)
}

How do I bind checkboxes to the List<int> property of a view model?

I've been reading the various posts on view models and check boxes, but my brain is starting to lock up and I need a little push in the right direction.
Here's my simplified view model. I have checkboxes that need to populate the lists with their values. I don't think this can happen automagically. I'm not sure how to bridge the gap between an array of string values and a List correctly. Suggestions?
public int AlertId { get; set; }
public List<int> UserChannelIds { get; set; }
public List<int> SharedChannelIds { get; set; }
public List<int> SelectedDays { get; set; }
Have your View Model like this to represent the CheckBox item
public class ChannelViewModel
{
public string Name { set;get;}
public int Id { set;get;}
public bool IsSelected { set;get;}
}
Now your main ViewModel will be like this
public class AlertViewModel
{
public int AlertId { get; set; }
public List<ChannelViewModel> UserChannelIds { get; set; }
//Other Properties also her
public AlertViewModel()
{
UserChannelIds=new List<ChannelViewModel>();
}
}
Now in your GET Action, you will fill the values of the ViewModel and sent it to the view.
public ActionResult AddAlert()
{
var vm = new ChannelViewModel();
//The below code is hardcoded for demo. you mat replace with DB data.
vm.UserChannelIds.Add(new ChannelViewModel{ Name = "Test1" , Id=1});
vm.UserChannelIds.Add(new ChannelViewModel{ Name = "Test2", Id=2 });
return View(vm);
}
Now Let's create an EditorTemplate. Go to Views/YourControllerName and Crete a Folder called "EditorTemplates" and Create a new View there with the same name as of the Property Name(ChannelViewModel.cshtml)
Add this code ro your new editor template.
#model ChannelViewModel
<p>
<b>#Model.Name</b> :
#Html.CheckBoxFor(x => x.IsSelected) <br />
#Html.HiddenFor(x=>x.Id)
</p>
Now in your Main View, Call your Editor template using the EditorFor Html Helper method.
#model AlertViewModel
<h2>AddTag</h2>
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(m => m.AlertId)
#Html.TextBoxFor(m => m.AlertId)
</div>
<div>
#Html.EditorFor(m=>m.UserChannelIds)
</div>
<input type="submit" value="Submit" />
}
Now when You Post the Form, Your Model will have the UserChannelIds Collection where the Selected Checkboxes will be having a True value for the IsSelected Property.
[HttpPost]
public ActionResult AddAlert(AlertViewModel model)
{
if(ModelState.IsValid)
{
//Check for model.UserChannelIds collection and Each items
// IsSelected property value.
//Save and Redirect(PRG pattern)
}
return View(model);
}
Part of My View Model:
public List<int> UserChannelIds { get; set; }
public List<int> SharedChannelIds { get; set; }
public List<int> Weekdays { get; set; }
public MyViewModel()
{
UserChannelIds = new List<int>();
SharedChannelIds = new List<int>();
Weekdays = new List<int>();
}
I used partial views to display my reusable checkboxes (I didn't know about editor templates at this point):
#using AlertsProcessor
#using WngAlertingPortal.Code
#model List<int>
#{
var sChannels = new List<uv_SharedChannels>();
Utility.LoadSharedChannels(sChannels);
}
<p><strong>Shared Channels:</strong></p>
<ul class="channel-list">
#{
foreach (var c in sChannels)
{
string chk = (Model.Contains(c.SharedChannelId)) ? "checked=\"checked\"" : "";
<li><input type="checkbox" name="SharedChannelIds" value="#c.SharedChannelId" #chk /> #c.Description (#c.Channel)</li>
}
}
All three checkbox partial views are similar to each other. The values of the checkboxes are integers, so by lining up my view model List names with the checkbox names, the binding works.
Because I am working in int values, I don't feel like I need the extra class to represent the checkboxes. Only checked checkboxes get sent, so I don't need to verify they are checked; I just want the sent values. By initializing the List in the constructor, I should be avoiding null exceptions.
Is this better, worse or just as good as the other solution? Is the other solution (involving an extra class) best practice?
The following articles were helpful to me:
http://forums.asp.net/t/1779915.aspx/1?Checkbox+in+MVC3
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Binding list with view model
This site handles it very nicely
https://www.exceptionnotfound.net/simple-checkboxlist-in-asp-net-mvc/
public class AddMovieVM
{
[DisplayName("Title: ")]
public string Title { get; set; }
public List<CheckBoxListItem> Genres { get; set; }
public AddMovieVM()
{
Genres = new List<CheckBoxListItem>();
}
}
public class MembershipViewData
{
public MembershipViewData()
{
GroupedRoles = new List<GroupedRoles>();
RolesToPurchase = new List<uint>();
}
public IList<GroupedRoles> GroupedRoles { get; set; }
public IList<uint> RolesToPurchase { get; set; }
}
//view
#model VCNRS.Web.MVC.Models.MembershipViewData
#{
ViewBag.Title = "MembershipViewData";
Layout = "~/Views/Shared/_Layout.cshtml";
int i = 0;
}
#using (Html.BeginForm("Membership", "Account", FormMethod.Post, new { id = "membershipForm" }))
{
<div class="dyndata" style="clear: left;">
<table width="100%" cellpadding="0" cellspacing="0" class="table-view list-view">
foreach (var kvp2 in Model.GroupedRoles)
{
string checkBoxId = "RolesToPurchase" + kvp2.RoleType;
<tr>
<td width="240px">
<label class="checkbox-label" for="#checkBoxId">
<input type="checkbox" class="checkbox" name="RolesToPurchase[#i]"
id="#checkBoxId" value="#kvp2.RoleType" />
#kvp2.Key
</label>
</td>
</tr>
i++;
}
<tr style="background-color: #ededed; height: 15px;">
<td colspan="5" style="text-align: right; vertical-align: bottom;">
#Html.SubmitButton(Resources.MyStrings.Views_Account_Next)
</td>
</tr>
</table>
</div>
}
//Post Action
[HttpPost]
public ActionResult Membership(MembershipViewData viewData)
{
..........................
}
}

ASP.net MVC 3 handling multiple checkboxes in each row of my table

I'm building an admin interface for a word guessing game so admin users are allowed to setup the game by selecting the words that will appear in the game and then select which letters in the words will be encoded.
MainGameTable Edit page.....
#model GameServer.ViewModels.GameTableModel
#section Javascript
{
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
}
#{
ViewBag.Title = "GameTableEdit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>GameTableEdit</h2>
#using (Html.BeginForm("GameTableEdit", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>GameTable</legend>
#Html.HiddenFor(model => model.GameTableId)
#Html.HiddenFor(model => model.GameTableNumber)
<div class="editor-label">
Table #: #Html.DisplayFor(model => model.GameTableNumber)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SubjectId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SubjectId, new SelectList(Model.Subjects, "Key", "Value"))
#Html.ValidationMessageFor(model => model.SubjectId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ComplexityId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ComplexityId, new SelectList(Model.Complexities, "Key", "Value"))
#Html.ValidationMessageFor(model => model.ComplexityId)
</div>
<button type="submit" name="button" value="GetWords">Get Words</button>
#Html.Partial("GameMatrix/_LineWordsTable", Model)
<p>
<button type="submit" name="button" value="Save">Save</button>
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Partial Page_for each of the words in the table
#model GameServer.ViewModels.GameTableModel
#if (Model.SelectedLinewords.Count != null && Model.SelectedLinewords.Count > 0)
{
<table>
<tr>
<th>
Select Word
</th>
<th>
LineWord
</th>
<th>
Characters to Display
</th>
</tr>
#Html.EditorFor(x => x.SelectedLinewords)
</table>
}
The Editor Template for each row:
#model GameServer.ViewModels.SelectedLineWord
<tr>
<td>
#Html.CheckBoxFor(x => x.isSelected)
</td>
<td>
#Html.DisplayFor(x => x.LineWord)
</td>
<td>
#Html.HiddenFor(x=>x.LineWordId)
#Html.HiddenFor(x=>x.LineWord)
#{ char[] lineword = Model.LineWord.ToCharArray(); }
#for (int i = 0; i < Model.LineWord.Length; i++)
{
<input type="checkbox" name="DisplayCharPosition" value="#i" /> #lineword[i]
}
</td>
</tr>
Here is my ViewModel
public class SelectedLineWord
{
[Required]
public Guid LineWordId { get; set; }
[Required]
public String LineWord { get; set; }
public int[] DisplayCharPosition { get; set; }
[Required]
public bool isSelected { get; set; }
public SelectedLineWord()
{
}
public SelectedLineWord(Guid linewordid, String word, String displaycharposition)
{
LineWordId = linewordid;
LineWord = word;
String[] pos = displaycharposition.Split(',');
DisplayCharPosition = new int[word.Length];
for (int i = 0; i < word.Length; i++)
{
DisplayCharPosition[i] = 0;
}
for (int i = 0; i < pos.Length; i++)
{
DisplayCharPosition[Int32.Parse(pos[i])] = 1;
}
}
public SelectedLineWord(Guid linewordid, String word, bool issel)
{
LineWordId = linewordid;
LineWord = word;
isSelected = issel;
}
}
public class GameTableModel
{
[Required]
public Guid GameTableId { get; set; }
[Required]
public Guid GameMatrixId { get; set; }
[Required]
[Display(Name = "Table Subject")]
public int SubjectId { get; set; }
[Required]
[Display(Name = "Minimum Complexity")]
public int ComplexityId { get; set; }
[Required]
public int GameTableNumber { get; set; }
[Required]
[Display(Name = "Include a Bonus table")]
public bool IsBonus { get; set; }
[Display(Name = "Table Subject")]
public Dictionary<int, string> Subjects;
[Display(Name = "Minimum Complexity")]
public Dictionary<int, int> Complexities;
public List<GameTableLine> GameTableLines { get; set; }
public List<SelectedLineWord> SelectedLinewords { get; set; }
public GameTableModel ()
{
try
{
//get a connection to the database
var data = new GameServerDataModelDataContext();
//Fetch the subjects reference data
var subjects = from c in data.Subjects orderby c.Subject1 select new { c.SubjectId, c.Subject1};
Subjects = new Dictionary<int, string>();
foreach (var subject in subjects)
{
Subjects.Add(subject.SubjectId, subject.Subject1);
}
//Fetch the complexities questions
Table<Complexity> dComplexities = data.GetTable<Complexity>();
Complexities = new Dictionary<int, int> { { 0, 0 } };
foreach (var complexity in dComplexities)
{
if (complexity.Complexity1 != null)
Complexities.Add(complexity.ComplexityId, (int)complexity.Complexity1);
}
}
catch (Exception ex)
{
//[TODO: Complete the exception handeling code.]
}
}
}
My problem is when I hit the save button the model passed to the controller has everything populated correctly but returns null for the check boxes that where selected for the DisplayCharPosition. What i was expecting was an int[] populated with the index of the character selected for display.
Could someone please help me understand what I'm doing wrong?
I've manage to solve this (but i'm still open to suggestions for a better way to do it).
What I did was I changed the following line <input type="checkbox" name="DisplayCharPosition" value="#i" /> #lineword[i] to #Html.CheckBoxFor(x => x.DisplayCharPosition[i]) #lineword[i] and my model type to public bool[] DisplayCharPosition { get; set; } so that i store an array of bools. Now the bool array has a true/false value for each char that the user wants to display in the game table. I then store this in the database as a comma delimited string (e.g. 1,4,6 - which i later split so i know that char in pos 1, 4 and 6 must be displayed and the rest encoded).
I also changed my model as follows:
public class SelectedLineWord
{
[Required]
public Guid LineWordId { get; set; }
[Required]
public String LineWord { get; set; }
public bool[] DisplayCharPosition { get; set; }
[Required]
public bool isSelected { get; set; }
public SelectedLineWord()
{
}
public SelectedLineWord(Guid linewordid, String word, String displaycharposition)
{
LineWordId = linewordid;
LineWord = word;
String[] pos = displaycharposition.Split(',');
DisplayCharPosition = new bool[word.Length];
//set all to false
for (int i = 0; i < word.Length; i++)
{
DisplayCharPosition[i] = false;
}
//now only set the ones that were already in the db.
for (int i = 0; i < pos.Length; i++)
{
DisplayCharPosition[Int32.Parse(pos[i])] = true;
}
}

Resources