I have an MVC 4 App where i have a ViewModel which has a couple of collections. During a POST the collections are null but the other fields are populated. Code attached please help.
View Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BAQueryTool.Models
{
public class QueryDefinition
{
[Key]
public int ID { get; set; }
[Display(Name = "Query Name")]
[MaxLength(32)]
[Required(ErrorMessage = "Query name is required.")]
public string Name { get; set; }
[Display(Name = "Query String")]
[Required(ErrorMessage = "Query string is required.")]
[DataType(DataType.MultilineText)]
[MaxLength(8192)]
public string QueryString { get; set; }
[NotMapped]
[Display(Name = "Query String")]
[MaxLength(175)]
public string QueryStringShort
{
get
{
string s = string.Empty;
if (this.QueryString != null)
{
s = this.QueryString.Trim();
if (s.Length >= 172)
{
return this.QueryString.Substring(0, 172) + "...";
}
}
return s;
}
}
[Display(Name = "Query Parameters")]
public virtual ICollection<QueryParameter> QueryParameters { get; set; }
}
public class RunQueryDefinition
{
public QueryDefinition QueryDefinition { get; set; }
[Display(Name = "Run Query Parameters")]
public virtual ICollection<RunQueryParameter> RunQueryParameters { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BAQueryTool.Models
{
public enum ParameterDataType
{
Date, String
}
public class QueryParameter
{
[Key]
public int ID { get; set; }
public int QueryDefinitionID { get; set; }
[Display(Name = "Parameter Name")]
[Required(ErrorMessage = "Parameter name is required.")]
[MaxLength(32)]
public string Name { get; set; }
[Display(Name = "Parameter Type")]
[Required(ErrorMessage = "Parameter name is required.")]
public ParameterDataType DataType { get; set; }
}
public class RunQueryParameter : QueryParameter
{
[Display(Name = "Parameter Value")]
[Required(ErrorMessage = "Parameter name is required.")]
public string Value { get; set; }
}
}
Controller
` `// GET: /QueryDefinition/Run/5
public ActionResult Run(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
QueryDefinition querydefinition = db.QueryDefinitions.Find(id);
if (querydefinition == null)
{
return HttpNotFound();
}
RunQueryDefinition runQueryDefinition = new RunQueryDefinition();
runQueryDefinition.QueryDefinition = querydefinition;
runQueryDefinition.RunQueryParameters = new List<RunQueryParameter> { };
foreach (var param in querydefinition.QueryParameters)
{
RunQueryParameter rqp = new RunQueryParameter();
rqp.ID = param.ID;
rqp.QueryDefinitionID = param.QueryDefinitionID;
rqp.Name = param.Name;
rqp.DataType = param.DataType;
rqp.Value = Convert.ToString(DateTime.Now.ToShortDateString());
runQueryDefinition.RunQueryParameters.Add(rqp);
}
return View(runQueryDefinition);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Run(RunQueryDefinition parameterList)
{
DateTime BeginDate;
DateTime EndDate;
try
{
//QueryDefinition querydefinition = db.QueryDefinitions.Find(id);
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
}
catch (RetryLimitExceededException)
{
//Log the error
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View();
}
View
#model BAQueryTool.Models.RunQueryDefinition
#{
ViewBag.Title = "Run Query";
}
<h2>Run Query Definition '#Html.DisplayFor(model => model.QueryDefinition.Name)'</h2>
#using (Html.BeginForm("Run", "QueryDefinition", FormMethod.Post, null))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.RunQueryParameters)
</dt>
<dd>
<table class="table">
<tr>
<th>Parameter Name</th>
<th>Parameter Data Type</th>
<th>Parameter Value</th>
</tr>
#foreach (var item in Model.RunQueryParameters)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.DataType)
</td>
<td>
#Html.EditorFor(modelItem => item.Value)
</td>
</tr>
}
</table>
</dd>
</dl>
<div class="form-group">
<div class="col-md-offset-1 col-md-10">
<input type="submit" value="Run Query" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The main class is the QueryDefinition class. There is a Index page which lists out all the query definitions. When the user clicks on Run link the Run page is generated and populated with the QueryDefinitions for an Id. The QueryParameters Collection is also populated as well as the RunQueryParameters collection. When the user enters a value for the Column Value and clicks on Run Query a POST occurs. In Debug I noticed that the RunQueryDefinitions parameterList parameter was not fully populated, particularly the collections QueryParameters and RunQueryParameters.
Any suggestion ?
Do not use foreach loop, use for loop instead so that indexed values of collection can get posted:
#for(int i = 0; i < Model.RunQueryParameters.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(modelItem => Model.RunQueryParameters[i].Name)
</td>
<td>
#Html.DisplayFor(modelItem => Model.RunQueryParameters[i].DataType)
</td>
<td>
#Html.EditorFor(modelItem => Model.RunQueryParameters[i].Value)
</td>
</tr>
}
See how model binding works in this article.
Related
I am working on a small project and trying to display the fullname (which added by me on in ApplicationUser Model inhireted from IdentityUser) and below the Model:
public class ApplicationUser : IdentityUser
{
public int PersonnalNumber { get; set; }
public string FullName { get; set; }
}
here is SystemDetails Model have relationship with Application User:
public class SystemDetails
{
[Key]
public int Id { get; set; }
[Required]
[Display(Name = "System Name")]
public string SystemName { get; set; }
[Required]
[Display(Name = "Admin Name")]
public string SystemAdminId { get; set; }
public string ServerNames { get; set; }
[ForeignKey("SystemAdminId")]
public virtual ApplicationUser ApplicationUser { get; set; }
}
and here is the Index View:
#if (Model.Count() > 0)
{
<table class=" table table-striped border">
<tr class="table-secondary">
<th>
#Html.DisplayNameFor(m => m.SystemName)
</th>
<th>
#Html.DisplayNameFor(m => m.SystemAdminId)
</th>
<th></th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(m => item.SystemName)
</td>
<td>
#Html.DisplayFor(m => item.ApplicationUser.FullName)
</td>
<td>
<partial name="_TableButtonPartial" model="#item.Id" />
</td>
</tr>
}
Everything working fine except that FullName is not displayed on index page, I can display SystemAdminId which is the registered user Id but when i try to display the FullName using Applicationuser.FullName it is not displaying any thing! it is saved correctly on the database when I create record on SystemDetails Controller as below:
SystemAdminId
Here is create action:
[HttpPost,ActionName("Create")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreatePost()
{
List<ApplicationUser> userList = new List<ApplicationUser>();
if (!ModelState.IsValid)
{
return NotFound();
}
userList = await (from user in _db.ApplicationUser select user).ToListAsync();
ViewBag.usersList = userList;
_db.SystemDetails.Add(SystemDetails);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Appreciating any help
In controller , when querying the SystemDetails list you should load the related ApplicationUser :
using Microsoft.EntityFrameworkCore;
var result = _dbContext.SystemDetails.Include(f => f.ApplicationUser).ToList();
I'm trying to make my list to show data from another column instead of its ID, currently they are showing like so:
This is what I work out so far:
Controller:
public class CompanyAdmin
{
public string Id { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "Company Name")]
public string CompanyName { get; set; }
public virtual List<Certificate> Certificates { get; set; }
public class Certificate
{
[Key]
[Display(Name = "Certificate No.")]
public string CertNo { get; set; }
[Display(Name = "Company")]
public IEnumerable<SelectListItem> Companies { get; set; }
[Required]
public string Company { get; set; }
[Display(Name = "User")]
public IEnumerable<SelectListItem> UserList { get; set; }
[Required]
public string User { get; set; }
public string UploadedBy { get; set; }
[Display(Name = "Description")]
public string CertDescription { get; set; }
[Display(Name = "File Location")]
public string CertFileLocation { get; set; }
[Display(Name = "Last Modified")]
public DateTime LastModifiedDate { get; set; }
}
public class CertificateViewModel
{
[Display(Name = "Certificate No.")]
public string CertNo { get; set; }
public string User { get; set; }
[Display(Name = "User")]
public string UserName { get; set; }
public string Company { get; set; }
[Display(Name = "Company")]
public string CompanyName { get; set; }
[Display(Name = "Description")]
public string CertDescription { get; set; }
[Display(Name = "Last Modified")]
public DateTime LastModifiedDate { get; set; }
}
Here is my controller:
public ActionResult Index()
{
if (User.IsInRole("Admin"))
{
IEnumerable<CertificateViewModel> model = null;
model = (from c in db.CertificateDB
join u in db.Users on c.User equals u.Id
join d in db.CompanyDB on c.Company equals d.Id
select new CertificateViewModel
{
CertNo = c.CertNo,
UserName = u.UserName,
CompanyName = d.CompanyName,
CertDescription = c.CertDescription,
LastModifiedDate = c.LastModifiedDate
}
);
return View(model);
}
else
{
return RedirectToAction("Index", "Home");
}
}
and the view:
#model IEnumerable<IdentitySample.Models.CertificateViewModel>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CertNo)
</th>
<th>
#Html.DisplayNameFor(model => model.Company)
</th>
<th>
#Html.DisplayNameFor(model => model.User)
</th>
<th>
#Html.DisplayNameFor(model => model.CertDescription)
</th>
<th>
#Html.DisplayNameFor(model => model.LastModifiedDate)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.CertNo)
</td>
<td>
#Html.DisplayFor(modelItem => item.Company)
</td>
<td>
#Html.DisplayFor(modelItem => item.User)
</td>
<td>
#Html.DisplayFor(modelItem => item.CertDescription)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastModifiedDate)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.CertNo }) |
#Html.ActionLink("Details", "Details", new { id = item.CertNo }) |
#Html.ActionLink("Delete", "Delete", new { id = item.CertNo })
</td>
</tr>
}
</table>
My Context:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
static ApplicationDbContext()
{
// Set the database intializer which is run once during application start
// This seeds the database with admin user credentials and admin role
Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
}
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbEntityValidationException ex)
{
string errorMessages = string.Join("; ", ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.PropertyName + ": " + x.ErrorMessage));
throw new DbEntityValidationException(errorMessages);
}
}
public DbSet<CompanyAdmin> CompanyDB { get; set; }
public DbSet<Certificate> CertificateDB { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
But the results keep coming up empty for the 2 join columns, please see screenshot here
Any idea what I've done wrong?
In your query you populate property CompanyName, but in your view you render property Company. Same goes for User in view vs UserName in a query.
Also I recommend adding .ToList() just before you pass the model to your view - this will make sure data is extracted from DB into objects before it is passed to your view:
model = (from c in db.CertificateDB
join u in db.Users on c.User equals u.Id
join d in db.CompanyDB on c.Company equals d.Id
select new CertificateViewModel
{
CertNo = c.CertNo,
UserName = u.UserName,
CompanyName = d.CompanyName,
CertDescription = c.CertDescription,
LastModifiedDate = c.LastModifiedDate
}
).ToList();
You are setting UserName and CompanyName property for your CertificateViewModel in controller action:
UserName = u.UserName,
CompanyName = d.CompanyName,
while in View you are using Company and User property:
<td>
#Html.DisplayFor(modelItem => item.Company)
</td>
<td>
#Html.DisplayFor(modelItem => item.User)
</td>
So you either need to set the Company and User property of your view model in controller or use CompanyName and UserName for rendering in View, if you change the view, change it to use the correct properties :
<td>
#Html.DisplayFor(modelItem => item.CompanyName)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
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>
}
I've researched this a bit and haven't found an answer that quite deals with a similar situation or MVC3. In the ViewModel I'm using I have a Lists of a separate model (List<AgentId> which is a list of the AgentId model).
In the Create page for this controller, I need an input section for 5 items to be added to this list. However, before the page even load, I receive this error message:
There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'BankListAgentId[0].StateCode'.
Here is the ViewModel I am using:
public class BankListViewModel
{
public int ID { get; set; }
public string ContentTypeID1 { get; set; }
public string CreatedBy { get; set; }
public string MANonresBizNY { get; set; }
public string LastChangeOperator { get; set; }
public Nullable<System.DateTime> LastChangeDate { get; set; }
public List<BankListAgentId> BankListAgentId { get; set; }
public List<BankListStateCode> BankListStateCode { get; set; }
}
And here is the section of the view that's having the issues:
<fieldset>
<legend>Stat(s) Fixed</legend>
<table>
<th>State Code</th>
<th>Agent ID</th>
<th></th>
<tr>
<td>
#Html.DropDownListFor(model => model.BankListAgentId[0].StateCode,
(SelectList)ViewBag.StateCode, " ")
</td>
<td>
#Html.EditorFor(model => model.BankListAgentId[0].AgentId)
#Html.ValidationMessageFor(model => model.BankListAgentId[0].AgentId)
</td>
</tr>
<tr>
<td>
#Html.DropDownListFor(model => model.BankListAgentId[1].StateCode,
(SelectList)ViewBag.StateCode, " ")
</td>
<td>
#Html.EditorFor(model => model.BankListAgentId[1].AgentId)
#Html.ValidationMessageFor(model => model.BankListAgentId[1].AgentId)
</td>
<td id="plus2" class="more" onclick="MoreCompanies('3');">+</td>
</tr>
</table>
</fieldset>
I believe #Html.DropDownListFor() is expecting an IEnumerable<SelectListItem>, you can bind it the following way:
In your ViewModel:
public class BankListViewModel
{
public string StateCode { get; set; }
[Display(Name = "State Code")]
public IEnumerable<SelectListItem> BankListStateCode { get; set; }
// ... other properties here
}
In your Controller load the data:
[HttpGet]
public ActionResult Create()
{
var model = new BankListViewModel()
{
// load the values from a datasource of your choice, this one here is manual ...
BankListStateCode = new List<SelectListItem>
{
new SelectListItem
{
Selected = false,
Text ="Oh well...",
Value="1"
}
}
};
return View("Create", model);
}
And then in the View bind it:
#Html.LabelFor(model => model.BankListStateCode)
#Html.DropDownListFor(model => model.StateCode, Model.BankListStateCode)
I hope this helps. Let me know if you nee clarifications.
This error wound up being thrown because the ViewBag element I was using had the same name as one of the list item properties.
The solution was to change ViewBag.StateCode to ViewBag.StateCodeList.
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;
}
}