I'm using EF4 CTP5 and am having trouble saving records back to the database. I have Contact and ContactType entities. As the post title states, I have set up a many-to-many navigation property between the tables.
The problem is with validating the ContactType values. ModelState.IsValid is false because it's unable to convert the values passed back from the form (a string array of ContactType id's into ContactType objects.
POCO's
public partial class Contact
{
public Contact()
{
this.ContactTypes = new HashSet<ContactType>();
}
// Primitive properties
public int ContactId { get; set; }
public string ContactName { get; set; }
// Navigation properties
public virtual ICollection<ContactType> ContactTypes { get; set; }
}
public partial class ContactType
{
public ContactType()
{
this.Contacts = new HashSet<Contact>();
}
// Primitive properties
public int ContactTypeId { get; set; }
public string Name { get; set; }
// Navigation properties
public virtual ICollection<Contact> Contacts { get; set; }
}
Controller
//
// GET: /Contact/Edit/5
public virtual ActionResult Edit(int id)
{
Contact contact = context.Contacts.Include(c => c.ContactTypes).Single(x => x.ContactId == id);
ViewData["ContactTypesAll"] = GetTypesList();
return View(contact);
}
//
// POST: /Contact/Edit/5
[HttpPost]
public virtual ActionResult Edit(Contact contact)
{
if (ModelState.IsValid)
{
context.Entry(contact).State = EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
ViewData["ContactTypesAll"] = GetTypesList();
return View(contact);
}
View
<div class="field-block">
#Html.LabelFor(model => model.ContactId)
#Html.EditorFor(model => model.ContactId, new { fieldName = "ContactId" })
#Html.ValidationMessageFor(model => model.ContactId)
</div>
<div class="field-block">
#Html.LabelFor(model => model.OrganizationNameInternal)
#Html.EditorFor(model => model.OrganizationNameInternal)
#Html.ValidationMessageFor(model => model.OrganizationNameInternal)
</div>
<div class="field-block">
#Html.LabelFor(model => model.ContactTypes)
#Html.ListBoxFor(modelContactType,
new MultiSelectList((IEnumerable<TDAMISObjects.ContactType>)ViewData["ContactTypesAll"],
"ContactTypeId",
"Name",
Model.ContactTypes))
#Html.ValidationMessageFor(model => model.ContactTypes)
</div>
ModelState error
ModelState.Values.ElementAt(2).Value
{System.Web.Mvc.ValueProviderResult}
AttemptedValue: "5"
Culture: {en-US}
RawValue: {string[1]}
ModelState.Values.ElementAt(2).Errors[0]
{System.Web.Mvc.ModelError}
ErrorMessage: ""
Exception: {"The parameter conversion from type 'System.String' to type 'ProjectObjects.ContactType' failed because no type converter can convert between these types."}
So it seems pretty clear what the problem is, but I can't seem to find the solution. I have tried to manually convert the ContactType id's into ContactType objects, and adding them to the Contact object passed into the Edit function (called 'contact'):
contact.ContactTypes.Clear();
string[] ids = this.HttpContext.Request.Form["ContactTypes"].Split(',');
for(int i = 0; i< ids.Length; i++)
{
int x = Convert.ToInt32(ids[i]);
ContactType selectedType = context.ContactTypes.Single(t => t.ContactTypeId == x);
contact.ContactTypes.Add(selectedType);
}
but the error persists. I've also tried calling
context.ChangeTracker.DetectChanges();
but that did not do the trick. I also manually set the ValueProviderResult for the value that will not validate, using
ModelState.SetModelValue("ContactTypes", val);
Which also did not work. I feel like I'm missing something basic here. Any ideas?
Thanks, Steve
Well after more work on this, I found a work around. Basically, I had to ignore the validation errors, then manually remove existing ContactTypes, then add in ones selected by the user. I did try to build a custom validator for the Contact.ContactTypes propery, but a ContactType object was always passed in to that method; I never saw the array of strings. Admittedly, that was the first custom validator I have built, so perhaps I was missing something.
Anyway, here's the Edit method I ended up with:
//
// POST: /Contact/Edit/5
[HttpPost]
public virtual ActionResult Edit(Contact contact)
{
// clear up ModelState.IsValid for contact type
ModelState.Remove("ContactTypes");
if(ModelState.IsValid)
{
// remove all contact types for contact
Contact dummy = context.Contacts.Single(c => c.ContactId == contact.ContactId);
if(dummy.ContactTypes.Count > 0)
{
dummy.ContactTypes.Clear();
context.Entry(dummy).State = EntityState.Modified;
context.SaveChanges();
}
context.Detach(dummy);
context.Contacts.Attach(contact);
// add currently selected contact types, then save
string[] ids = this.HttpContext.Request.Form["ContactTypes"].Split(',');
for(int i = 0; i< ids.Length; i++)
{
int x = Convert.ToInt32(ids[i]);
ContactType selectedType = context.ContactTypes.Single(t => t.ContactTypeId == x);
contact.ContactTypes.Add(selectedType);
}
context.Entry(contact).State = EntityState.Modified;
context.SaveChanges();
ViewBag.Message = "Save was successful.";
}
ViewData["ContactTypes"] = contact.ContactTypes.Select(t => t.ContactTypeId);
ViewData["ContactTypesAll"] = GetTypesList();
return View(contact);
}
I had to add a Detach method to my DBContext class as well (in CTP 5 this is not exposed):
public void Detach(object entity)
{
((System.Data.Entity.Infrastructure.IObjectContextAdapter)this).ObjectContext.Detach(entity);
}
Related
I have a model with a list of child objects. I have created a custom validation attribute, implemented IValidatableObject on the model and i get an error message as expected. The problem is that once the property has an error in the modelstate, i can't get the updated value to post back to the server. They get cleared out some time between hitting the submit button and receiving the model in the controller.
if i call ModelState.Clear() in the controller action, i don't get any messages but the new values post as expected. The model is however picking up on the custom attribute because ModelState.IsValid == false
I'm thinking the best way to handle this is to call ModelState.Clear() on the client somehow after $(ready) so i get the validation messages but can also have the changed values post to the server. Is this possible or is there a better way to do this?
Parent Model
public class PayrollPlanModel : IMapFrom<Data.PayrollPlan>
{
public int? PayrollPlanId { get; set; }
[Required]
public string Name { get; set; }
public List<PlanOptionFormModel> Options { get; set; }
}
Model List property on parent with custom attribute
public class PlanOptionFormModel : IValidatableObject
{
public int PlanOptionValueId { get; set; }
public int PayrollPlanId { get; set; }
public string PlanName { get; set; }
public int PlanOptionId { get; set; }
public string Description { get; set; }
[UIHint("_Money")]
[RequiredIf("Selected", true)]
public decimal? Value { get; set; }
public bool Selected { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Selected && !Value.HasValue)
{
yield return new ValidationResult("Add a value.");
}
}
}
Custom Attribute (Shamelessly stolen from here)
public class RequiredIfAttribute : ValidationAttribute
{
RequiredAttribute _innerAttribute = new RequiredAttribute();
public string _dependentProperty { get; set; }
public object _targetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this._dependentProperty = dependentProperty;
this._targetValue = targetValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectType.GetProperty(_dependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if ((dependentValue == null && _targetValue == null) || (dependentValue.Equals(_targetValue)))
{
if (!_innerAttribute.IsValid(value))
{
string name = validationContext.DisplayName;
return new ValidationResult(ErrorMessage = name + " Is required.");
}
}
return ValidationResult.Success;
}
else
{
return new ValidationResult(FormatErrorMessage(_dependentProperty));
}
}
}
Page snippet
for (int i = 0; i < Model.Options.Count; i++)
{
<div class="row">
<div class="col-md-3">
#Html.HiddenFor(m => Model.Options[i].PlanOptionValueId)
#Html.HiddenFor(m => Model.Options[i].PayrollPlanId)
#Html.HiddenFor(m => Model.Options[i].PlanOptionId)
#Html.HiddenFor(m => Model.Options[i].Description)
</div>
<div class="col-md-1 text-right">
#Html.CheckBoxFor(m => Model.Options[i].Selected, new { #data_textbox = "optionValue_" + i.ToString(), #class = "form-control modelOptionSelector" })
</div>
<div class="col-md-2 text-right">
<h4>#Model.Options[i].Description</h4>
</div>
<div class="col-md-1">
#Html.EditorFor(m => Model.Options[i].Value, Model.Options[i].Selected ? new { HtmlAttributes = new { id = "optionValue_" + i.ToString(), #class = "planOptionValueEditor" } } : (object)new { HtmlAttributes = new { disabled = "disabled", id = "optionValue_" + i.ToString(), #class = "planOptionValueEditor" } })
#Html.ValidationMessageFor(m => Model.Options[i].Value)
</div>
</div>
}
<br />
Editor Template
#model decimal?
#{
var defaultHtmlAttributesObject = new { };
var htmlAttributesObject = ViewData["htmlAttributes"] ?? new { };
var htmlAttributes = Html.MergeHtmlAttributes(htmlAttributesObject, defaultHtmlAttributesObject);
string attemptedValue = "";
ModelState modelStateForValue = Html.ViewData.ModelState[Html.IdForModel().ToString()];
if (modelStateForValue != null)
{
attemptedValue = modelStateForValue.Value.AttemptedValue;
}
}
#(Html.Kendo().CurrencyTextBoxFor(m => m)
.HtmlAttributes(htmlAttributes)
.Format("c")
.Spinners(false)
)
Controller
[HttpPost]
public ActionResult EditPlan(PayrollPlanModel model)
{
if(ModelState.IsValid)
{
}
else
{
}
return View(model);
}
It makes no sense to attempt to attempt to clear ModelState errors from the client. ModelState is only set within the controller method (by the DefaultModelBinder) when you make a request to the method. In any case, your issues are not related to ModelState being valid or invalid in the controller method.
There are a number of changes you need to make to your code:
You should delete your EditorTemplate for decimal? It means that any property of that type is going to use that template. Instead replace your
#Html.EditorFor(m => Model.Options[i].Value, ...)
with
#(Html.Kendo().CurrencyTextBoxFor(m => m.Options[i].Value)....
in the main view.
If you really do want to use a template, then make it a named template (which is called using #Html.EditorFor(m => Model.Options[i].Value, "yourTemplateName"), but in any case, you need to remove the code relating to attemptedValue and modelStateForValue (including the if block) - the EditorFor() methods will always correctly use values from ModelState if they exist.
Next, your RequiredIfAttribute does not implement IClientValidatable so you will not get client side validation. You could use the foolproof library, or if you want to write your own, refer this answer for the full implementation of a RequiredIfAttribute, including the scripts for client side validation.
Next, you need to delete the IValidatableObject implementation (the Validate() method) from your model. That is just repeating the validation that the [RequiredIf] attribute is doing, and you should avoid mixing ValidationAttribute's with IValidatableObject (refer The Complete Guide To Validation In ASP.NET MVC 3 - Part 2 for more detailed information).
Finally, the Kendo().CurrencyTextBoxFor() method hides the input and renders its own html. By default, hidden inputs are not validated, so you need to reconfigure the validator. In the main view, add the following script (after the jquery-{version}.js, jquery.validate.js and jquery.validate.unobtrusive.js scripts
<script>
$.validator.setDefaults({
ignore: []
});
.... // other scripts as required
<script>
I am new to MVC. I am using a DropDownListFor to populate a number of Customer fields when a Company is selected. I am using the following code for the DropDownListFor:
#Html.DropDownListFor(model => model.CustomerId, new SelectList(ViewBag.Customers, "CustomerId", "Company"), "---Select one---", new { htmlAttributes = new { #class = "company" } });
#Html.HiddenFor(model => model.Company)
This code retrieves the Customer data:
[HttpPost]
public ActionResult GetCustomer(int custId)
{
var data = db.Customers.Find(custId);
return Json(data);
}
The relevant fields in the ViewModel (from the Customer table) are:
public int CustomerId { get; set; }
public string Company { get; set; }
The code in the Create method that creates the ViewBag:
public ActionResult Create()
{
QuoteViewModel qvm = new QuoteViewModel();
qvm.QuoteDetail = new List<QuoteDetail>();
var customers = db.Customers.ToList();
ViewBag.Customers = customers;
return View(qvm);
}
And here is the code for the POST:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(QuoteViewModel qvm)
{
if (ModelState.IsValid)
{
Quote quote1 = new Quote();
quote1.CustomerId = qvm.CustomerId;
...
db.Quotes.Add(quote1);
customer.CustomerId = qvm.CustomerId;
...
db.Entry(customer).State = EntityState.Modified;
bool saveFailed;
do
{
saveFailed = false;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
var objContext = ((IObjectContextAdapter)db).ObjectContext;
// Get failed entry
var entry = ex.Entries.Single();
// Now call refresh on ObjectContext
objContext.Refresh(RefreshMode.ClientWins, entry.Entity);
}
} while (saveFailed);
return RedirectToAction("Index");
}
return View(qvm);
}
The population of the fields works fine, but when I attempt to Create the view model I get an error "Value cannot be null" on the DropDownListFor. I have researched others having this issue but cannot find an answer that applies to this case. Any help would be much appreciated.
The error is because in the POST method you return the view (ModelState is invalid), but have not set the value of ViewBag.Customers as you did in the GET method, so it null and you cannot create a SelectList from a null collection.
Your need assign ViewBag.Customers as you did in the GET method before your return View(qvm); statement.
As a side note, since you using a view model, that view model should contain a property (say) public IEnumerable<SelectListItem> CustomerList { get; set; } and you set that in the controller methods, and in the view
#Html.DropDownListFor(model => model.CustomerId, Model.CustomerList, "---Select one---", new { #class = "company" });
Are you making a full page POST request when a Company is selected?
If you are, you need to fill ViewBag.Customers because of ViewBag's lifetime.
http://www.dotnettricks.com/learn/mvc/viewdata-vs-viewbag-vs-tempdata-vs-session
I have two dropdown lists and two textboxes
Search By: ByHtml.DropDownList("Search1", "Please Select...")
Html.TextBox("searchString1")
Search By: Html.DropDownList("Search2", "Please Select...")
#Html.TextBox("searchString2")
<input type="submit" value="Filter" />
When I make my selection from whichever DDL and type text into the textbox and hit filter my search returns, however after the search the text remains in the textbox, is there a way of clearing it after the search so that the textbox is empty again? I tried
ModelState.Remove("");
but it didn't work.
A sample from My controller code is
public class MainController : Controller
{
private DBEntities db = new DBEntities();
// GET: /Main/
public ActionResult Index(string searchString1, string searchString2, string Search1, string Search2)
{
//Create a Dropdown list
var SearchOptionList = new List<string>();
SearchOptionList.Add("LandLord");
SearchOptionList.Add("Postcode");
SearchOptionList.Add("Street Address");
ViewBag.Search1 = new SelectList(SearchOptionList);
ViewBag.Search2 = new SelectList(SearchOptionList);
var mylist = from m in "mydatabase" select m;
//This statement runs if the user selects a parameter from Search2 and leaves Search1 empty
if (String.IsNullOrEmpty(Search1) && !String.IsNullOrEmpty(Search2))
{
if (Search2 == "Postcode")
{
mylist = mylist.Where(s => s.Postcode.Contains(searchString2));
}
if (Search2 == "LandLord")
{
mylist = mylist.Where(s => s.Name.Contains(searchString2));
}
if (Search2 == "Street Address")
{
mylist = mylist.Where(s => s.StreetAddress.Contains(searchString2));
}
}
return View(mylist.ToList());
}
Your should have a view model containing properties searchString1 and searchString2 and the select lists
public class SearchVM
{
public string searchString1 { get; set; }
public string searchString2 { get; set; }
public SelectList SearchList1 { get; set; }
public SelectList SearchList2 { get; set; }
}
Controller
public ActionResult Search()
{
SearchVM model = new SearchVM();
model.SearchList1 = new SelctList(...);
model.SearchList2 = new SelctList(...);
return View(model);
}
View
#model SearchVM
#using(Html.BeginForm())
{
....
#Html.DropDownListFor(m => m.searchString1, Model.SearchList1, "--Please select--")
#Html.DropDownListFor(m => m.searchString2, Model.SearchList2, "--Please select--")
....
}
Post
[HttpPost]
public ActionResult Search(SearchVM model)
{
// to clear all modelstate and reset values
ModelState.Clear();
model.searchString1 = null;
model.searchString2 = null;
// or to clear just one property and reset it
ModelState.Remove("searchString1");
model.searchString1 = null;
// repopulate select lists if your returning the view
return View(model);
}
At the end of my public ActionResult Index method but before return View() I placed the following code which worked perfectly
ModelState.Remove("searchString1");
ModelState.Remove("searchString2");
ModelState.Remove("Search1");
ModelState.Remove("Search2");
I know is an old question, but I fall in the same issue. So I put my solution.
View:
#Html.TextBox("Search", null, new { #autofocus = "autofocus" })
Controller:
ViewBag.Search= null;
ModelState.Remove("Search");
return View(list.ToList());
Hope to help someone
Issuer has as a related field KeyEntity. My code works, but I think that there must be a better way to update the related field other than loading entities from database.
There should be something wrong in ViewModeling and templating... Thanks in advance.
OpViewModel
public class OpViewModel
{
public Connect.Models.Issuer Issuer { get; set; }
public IEnumerable<SelectListItem> KeyEntities { get; set; }
public OpViewModel() { }
public OpViewModel(Connect.Models.Issuer issuer,
IEnumerable<Jose.Models.KeyEntity> key_entities)
{
this.Issuer = issuer;
this.KeyEntities = key_entities.Select(k =>
new SelectListItem()
{
Selected = (k.Id == issuer.KeyEntity.Id),
Text = k.Id.ToString(),
Value = k.Id.ToString()
});
}
}
Op/Edit.chtml(Razor)
#model AdConnect.Models.OpViewModel
#using (Html.BeginForm()) {
<div class="editor-field">
#Html.DropDownListFor(model => model.Issuer.KeyEntity.Id,
Model.KeyEntities)
</div>
}
OpController
[HttpPost]
public ActionResult Edit(Models.OpViewModel obj)
{
if (ModelState.IsValid)
{
// This works for most data fields of Issuer, but KeyEntity's Id
// (dbo.Issuers.KeyEntity_Id) is not updated.
//
// ctx.Entry(obj.Issuer).State = System.Data.EntityState.Modified
// So load Issuer and KeyEntity from the database and update.
var issuer = ctx.Issuers.Find(obj.Issuer.Id);
if (obj.Issuer.KeyEntity != null)
{
issuer.KeyEntity =
ctx.KeyEntities.Find(obj.Issuer.KeyEntity.Id);
}
ctx.Entry(issuer).CurrentValues.SetValues(obj.Issuer);
ctx.SaveChanges();
return RedirectToAction("Index");
}
return View(obj);
}
I think you are missing a bit around how navigation properties work. EF can automatically populate them so you aren't doing it in the constructor of the object.
Have a read of my article on nav properties here.
Also in terms of your controller update code the normal path for an update like this is: Load, Modify, Save.
ie
var issuer = ctx.Issuers.Include(i=>i.KeyEntity).SingleOrDefault(i=>i.Id == obj.Issuer.Id);
issuer.Whatever = obj.Issuer.Whatever; //note prehaps you want something like CopyMatchinProperties here
ctx.SaveChanges();
I tried searching and didn't find anything that fixed my problem. I have a DropDownList on a Razor view that will not show the the item that I have marked as Selected in the SelectList. Here is the controller code that populates the list:
var statuses = new SelectList(db.OrderStatuses, "ID", "Name", order.Status.ID.ToString());
ViewBag.Statuses = statuses;
return View(vm);
Here is the View code:
<div class="display-label">
Order Status</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.StatusID, (SelectList)ViewBag.Statuses)
#Html.ValidationMessageFor(model => model.StatusID)
</div>
I walk through it and even in the view it has the correct SelectedValue however the DDL always shows the first item in the list regardless of the selected value. Can anyone point out what I am doing wrong to get the DDL to default to the SelectValue?
The last argument of the SelectList constructor (in which you hope to be able to pass the selected value id) is ignored because the DropDownListFor helper uses the lambda expression you passed as first argument and uses the value of the specific property.
So here's the ugly way to do that:
Model:
public class MyModel
{
public int StatusID { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
// TODO: obviously this comes from your DB,
// but I hate showing code on SO that people are
// not able to compile and play with because it has
// gazzilion of external dependencies
var statuses = new SelectList(
new[]
{
new { ID = 1, Name = "status 1" },
new { ID = 2, Name = "status 2" },
new { ID = 3, Name = "status 3" },
new { ID = 4, Name = "status 4" },
},
"ID",
"Name"
);
ViewBag.Statuses = statuses;
var model = new MyModel();
model.StatusID = 3; // preselect the element with ID=3 in the list
return View(model);
}
}
View:
#model MyModel
...
#Html.DropDownListFor(model => model.StatusID, (SelectList)ViewBag.Statuses)
and here's the correct way, using real view model:
Model
public class MyModel
{
public int StatusID { get; set; }
public IEnumerable<SelectListItem> Statuses { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
// TODO: obviously this comes from your DB,
// but I hate showing code on SO that people are
// not able to compile and play with because it has
// gazzilion of external dependencies
var statuses = new SelectList(
new[]
{
new { ID = 1, Name = "status 1" },
new { ID = 2, Name = "status 2" },
new { ID = 3, Name = "status 3" },
new { ID = 4, Name = "status 4" },
},
"ID",
"Name"
);
var model = new MyModel();
model.Statuses = statuses;
model.StatusID = 3; // preselect the element with ID=3 in the list
return View(model);
}
}
View:
#model MyModel
...
#Html.DropDownListFor(model => model.StatusID, Model.Statuses)
Make Sure that your return Selection Value is a String and not and int when you declare it in your model.
Example:
public class MyModel
{
public string StatusID { get; set; }
}
Create a view model for each view. Doing it this way you will only include what is needed on the screen. As I don't know where you are using this code, let us assume that you have a Create view to add a new order.
Create a new view model for your Create view:
public class OrderCreateViewModel
{
// Include other properties if needed, these are just for demo purposes
// This is the unique identifier of your order status,
// i.e. foreign key in your order table
public int OrderStatusId { get; set; }
// This is a list of all your order statuses populated from your order status table
public IEnumerable<OrderStatus> OrderStatuses { get; set; }
}
Order status class:
public class OrderStatus
{
public int Id { get; set; }
public string Name { get; set; }
}
In your Create view you would have the following:
#model MyProject.ViewModels.OrderCreateViewModel
#using (Html.BeginForm())
{
<table>
<tr>
<td><b>Order Status:</b></td>
<td>
#Html.DropDownListFor(x => x.OrderStatusId,
new SelectList(Model.OrderStatuses, "Id", "Name", Model.OrderStatusId),
"-- Select --"
)
#Html.ValidationMessageFor(x => x.OrderStatusId)
</td>
</tr>
</table>
<!-- Add other HTML controls if required and your submit button -->
}
Your Create action methods:
public ActionResult Create()
{
OrderCreateViewModel viewModel = new OrderCreateViewModel
{
// Here you do database call to populate your dropdown
OrderStatuses = orderStatusService.GetAllOrderStatuses()
};
return View(viewModel);
}
[HttpPost]
public ActionResult Create(OrderCreateViewModel viewModel)
{
// Check that viewModel is not null
if (!ModelState.IsValid)
{
viewModel.OrderStatuses = orderStatusService.GetAllOrderStatuses();
return View(viewModel);
}
// Mapping
// Insert order into database
// Return the view where you need to be
}
This will persist your selections when you click the submit button and is redirected back to the create view for error handling.
I hope this helps.
For me, the issue was caused by big css padding numbers ( top & bottom padding inside the dropdown field). Basically, the item was being shown but not visible because it was way down. I FIXED it by making my padding numbers smaller.
I leave this in case it helps someone else. I had a very similar problem and none of the answers helped.
I had a property in my ViewData with the same name as the selector for the lambda expression, basically as if you would've had ViewData["StatusId"] set to something.
After I changed the name of the anonymous property in the ViewData the DropDownList helper worked as expected.
Weird though.
My solution was this...
Where the current selected item is the ProjectManagerID.
View:
#Html.DropDownList("ProjectManagerID", Model.DropDownListProjectManager, new { #class = "form-control" })
Model:
public class ClsDropDownCollection
{
public List<SelectListItem> DropDownListProjectManager { get; set; }
public Guid ProjectManagerID { get; set; }
}
Generate dropdown:
public List<SelectListItem> ProjectManagerDropdown()
{
List<SelectListItem> dropDown = new List<SelectListItem>();
SelectListItem listItem = new SelectListItem();
List<ClsProjectManager> tempList = bc.GetAllProductManagers();
foreach (ClsProjectManager item in tempList)
{
listItem = new SelectListItem();
listItem.Text = item.ProjectManagerName;
listItem.Value = item.ProjectManagerID.ToString();
dropDown.Add(listItem);
}
return dropDown;
}
Please find sample code below.
public class Temp
{
public int id { get; set; }
public string valueString { get; set; }
}
Controller
public ActionResult Index()
{
// Assuming here that you have written a method which will return the list of Temp objects.
List<Temp> temps = GetList();
var tempData = new SelectList(temps, "id", "valueString",3);
ViewBag.Statuses = tempData;
return View();
}
View
#Html.DropDownListFor(model => model.id, (SelectList)ViewBag.Statuses)
#Html.ValidationMessageFor(model => model.id)