I am quite confused with how to effectively use the Html.DropDownList helper for ASP.NET MVC.
Background: I have a 5-page form, which saves data to the form each time "Next" is clicked. Users may navigate back and forth between sections, so previous sections will already be pre-populated with previously-entered data.
This works for TextBoxes. But not DropDownLists. I have tried a load of different methods, including:
How to add static list of items in MVC Html.DropDownList()
Setting selected item to DropdownList in MVC Application?
I have a ViewModel such taht I have got my lists and my Model (a LINQ-to-SQL generated class) as properties. eg:
public class ConsultantRegistrationFormViewModel
{
public IConsultantRegistration ConsultantRegistration { get; private set; }
public SelectList Titles { get; private set; }
public SelectList Countries { get; private set; }
public SelectList Currencies { get; private set; }
public int CurrentSection { get; private set; }
private ConsultantRegistrationFormViewModel(IConsultantRegistration consultantRegistration)
{
ConsultantRegistration = consultantRegistration;
CurrentSection = 1;
Titles = new SelectList(new string[] { "Mr", "Mrs", "Miss", "Ms", "Dr", "Sir" });
Countries = new SelectList(countries.Select(q => q.Name));
Currencies = new SelectList(currencies,"CurrencyCode","FriendlyForm");
}
}
My Controller's Edit Action on GET looks like:
public class ConsultantRegistrationController : Controller
{
public IConsultantRegistrationRepository ConsultantRegistrationRepository { get; private set; }
public ICountryRepository CountryRepository { get; private set; }
public IEnumerable<ICountry> Countries { get; private set; }
public ConsultantRegistrationController()
{
ConsultantRegistrationRepository = RepositoryFactory.CreateConsultantRegistrationRepository();
CountryRepository = RepositoryFactory.CreateCountryRepository();
Countries = CountryRepository.GetCountries().ToArray();
}
public ActionResult Edit(Guid id, int sectionIndex)
{
IConsultantRegistration consultantRegistration = ConsultantRegistrationRepository.GetConsultantRegistration(id);
SelectList bankBranchCountriesSelectList = new SelectList(Countries, "BankBranchCountry", "CountryName", consultantRegistration.BankBranchCountry);
ViewData["bankBranchCountrySelectList"] = bankBranchCountriesSelectList;
return View(new ConsultantRegistrationFormViewModel(consultantRegistration,sectionIndex, Countries,Currencies));
}
}
With my View doing:
<%: Html.DropDownList("ConsultantRegistration.BankBranchCountry",ViewData["bankBranchCountrySelectList"] as SelectList) %>
This gives me the error:
DataBinding:
'IWW.ArrowPay.ConsultantRegistration.Data.Country'
does not contain a property with the
name 'BankBranchCountry'.
Which it does, have a look at the schema of this property:
public interface IConsultantRegistration
{
Guid ID { get; set; }
[DisplayName("Branch Country")]
string BankBranchCountry { get; set; }
}
(My LINQ-to-SQL type ConsultantRegistration implemented IConsultantRegistration)
It seems that it is trying to bind to the wrong type, though?
If I use this in my view (and use my Controller's Countries property):
<%: Html.DropDownList("ConsultantRegistration.BankBranchCountry ",Model.Countries,"(select a Country)") %>
I get the saved value fine, but my model doesn't update on POST.
And if I use this in my view:
<%: Html.DropDownListFor(model=>model.ConsultantRegistration.BankBranchCountry ",Model.Countries,"(select a Country)") %>
I get the list, and it POSTs the selected value back, but does not pre-select the currently selected item in my model on the view.
So I have a bit of the solution all over the place, but not all in one place.
Hope you can help fill in my ignorance.
Ok, I solved it. Proper hacky, but it gets the job done.
I'm using the ViewData in my view:
<%: Html.DropDownList("bankBranchCountrySelectList", ViewData["bankBranchCountrySelectList"] as SelectList)%>
With the following in my controller:
public ActionResult Edit(Guid id, int sectionIndex)
{
IConsultantRegistration consultantRegistration = ConsultantRegistrationRepository.GetConsultantRegistration(id);
ViewData["bankBranchCountrySelectList"] = Countries.Select(q => new SelectListItem() { Text = q.Name, Value = q.Name, Selected = (q.Name.Trim().Equals(consultantRegistration.BankBranchCountry, StringComparison.InvariantCultureIgnoreCase)) }); // bankBranchCountriesSelectList;
return View(new ConsultantRegistrationFormViewModel(consultantRegistration,sectionIndex, Countries,Currencies));
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, int sectionIndex, FormCollection formValues)
{
IConsultantRegistration consultantRegistration = ConsultantRegistrationRepository.GetConsultantRegistration(id);
UpdateModel(consultantRegistration);
ViewData["bankBranchCountrySelectList"] = Countries.Select(q => new SelectListItem() { Text = q.Name, Value = q.Name, Selected = (q.Name.Trim().Equals(consultantRegistration.BankBranchCountry, StringComparison.InvariantCultureIgnoreCase)) });
IEnumerable<RuleViolation> ruleViolations = consultantRegistration.GetRuleViolations(sectionIndex);
if (ruleViolations.Count() == 0)
{
// ...
}
else
{
ModelState.AddRuleViolations(ruleViolations);
return View(new ConsultantRegistrationFormViewModel(consultantRegistration, sectionIndex, Countries, Currencies));
}
}
Not ideal and breaks clean coding. No idea why it works, but that seems to be what MVC is all about with "convention over configuration".
This article was written with Multi-Select Lists in mind, but the principle applies equally to a single-selection drop down list:
http://www.stevefenton.co.uk/Content/Blog/Date/201002/Blog/How-To-Handle-Multiple-Select-Lists-In-ASP-NET-MVC/
Related
I have an MVC 5 site, I would like to use a strongly typed DropDownListFor with a ViewModel - not with ViewBag.
I have found various articles on this - but they all seem to have huge holes - for example this one doesnt cover editing, and I do not understand how or when "SelectedFlavourId" should be used.
http://odetocode.com/blogs/scott/archive/2013/03/11/dropdownlistfor-with-asp-net-mvc.aspx
I have several requirements.
When editing the story I would like a drop down list of all places to
be displayed - with the associated place (if any) - selected.
I want to use the strongly typed DropDownListFOR (as opposed to
DropDownList).
I would like to use a ViewModel not the ViewBag.
I want to add a "No Associated Place" which will be
selected if PlaceId is null.
I want to add a css class = "form-control" to the DropDownListFor.
The below is as far as I have got after a day of frustration.
A story can be optionally associated with a PlaceId. A blank placeId is also valid. A place can also be associated with more than one story.
Models
public class Place
{
public Guid Id { get; set; }
public string PlaceName { get; set; }
}
public class Story
{
public Guid Id { get; set; }
public Guid? PlaceId { get; set; }
public string StoryName { get; set; }
}
public class StoryPlaceDropdown
{
public Story story { get; set; }
public Guid SelectedStoryId;
public IEnumerable<Place> places;
public IEnumerable<SelectListItem> placeItems
{
get
{
return new SelectList(places, "Id", "PlaceName");
}
}
}
Controller
public ActionResult Edit(Guid Id)
{
var spd = new StoryPlaceDropdown();
spd.places = PlaceRepo.SelectAll();
spd.story = StoryRepo.SelectStory(Id);
spd.selectedStoryID = apd.story.Id;
// Return view
return View(spd);
}
[HttpPost]
public ActionResult Edit(StoryPlaceDropdown spd)
{
// Never gets this far
spd.Places = PlaceRepo.SelectAll();
return View();
}
In View
#Html.DropDownListFor(m => m.SelectedStoryId, Model.PlaceItems)
This populates the DropDownList fine. However it does not select the correct item in edit view. Also when I submit the form I get this error:
Object reference not set to an instance of an object. on this line in the view #Html.DropDownListFor(m => m.SelectedStoryId, Model.PlaceItems)
How can I get this all working? Thanks.
I solved this - I had stupidly forgotten the { get; set; } accessors on the ViewModel, doh!
You can resolved this by these three steps:
Step 1:Create viewmodel
public class StoryPlaceDropdown
{
Required]
[Display(Name = "SelectedStory")]
public int SelectedStoryId { get; set; }
}
Step 2:After this on controller you can write:
public ActionResult Edit(Guid Id)
{
var spd = new StoryPlaceDropdown();
ViewBag.PlaceItems= PlaceRepo.SelectAll();
spd.story = StoryRepo.SelectStory(Id);
spd.selectedStoryID = apd.story.Id;
return View(spd);
}
Step 3: And on view you can write
<div class="col-sm-6">
#Html.DropDownListFor(m => m.SelectedStoryId, new SelectList(#ViewBag.PlaceItems, "Id", "PlaceName"), "---Select---", new { #class = "form-control select-sm" })
</div>
In ASP.NET MVC 5 Please could someone help me with how I determine the value that has been selected from a Select List (implemented as a #Html.DropDownListFor) in a View so I can pass it back to the Controller Method in order to select a modified list of data please? I have spent a few days browsing and trying things to no avail:
Here is what I have. I have a parent Model:
public class ParentEndorsementViewModel
{
// Child Models:
public List<VW_CMA_PRODUCT_ENDORSEMENT> VW_CMA_PRODUCT_ENDORSEMENTS { get; set; }
// This property will hold all available Products for selection:
public IEnumerable<SelectListItem> Product_DropDownList { get; set; }
}
And the relevant “child” model is:
public class Product_DropDownList
{
private Product_DropDownList(int id, string name)
{
Id = id;
Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
}
The View has:
#model App_Endorsement.Models.ParentEndorsementViewModel
….
#using (Html.BeginForm("Manage_Endorsement", "Endorsement", new { ParentEndorsementViewModel = #Model }, FormMethod.Post, null))
{
#*#Html.AntiForgeryToken()*#
#Html.DropDownListFor(m => m.Product_DropDownList, new SelectList(Model.Product_DropDownList, "Value", "Text", Model.Product_DropDownList), "Show All Products")
<input type = "submit" value = "Send" />
}
The Controller method starts:
[HttpPost]
//[ValidateAntiForgeryToken]
public IActionResult Manage_Endorsement(ParentEndorsementViewModel In_ParentEndorsementViewModel)
{
var ParentEndorsementViewModel = In_ParentEndorsementViewModel;
As I say I want to retrieve the Selected Value that the User has chosen from within the Product_DropDownList but when the model (ParentEndorsementViewModel) arrives in the Controller it is empty.
I think you have overcomplicated this somewhat. Your views main model needs both a list to populate the control and a variable to contain the selected value.
For example your model:
public class ParentEndorsementViewModel
{
public int SelectedId { get; set; }
public List<SelectListItem> ProductList { get; set; }
public ParentEndorsementViewModel() {
// Do this just in case you forget to instantiate the list in your first call to the view.
ProductList = New List<SelectListItem>();
}
}
On your view:
#Html.DropDownListFor(x => x.SelectedId, Model.ProductList)
When the user sends this data to back to your controller via a form POST for example, the model binder should marry up to the view model. The value selected by your user is contained in SelectedId variable.
[HttpPost]
public ActionResult MyPostBack (ParentEndorsementViewModel model) {
var myselectedid = model.SelectedId;
//Do whatever
}
You need another property on your ParentEndorsementViewModel, which is what will hold the selected value from the drop down list. Currently your code is attempting to set the selected value to the property that contains the available items.
e.g. in the view model
public class ParentEndorsementViewModel
{
// Child Models:
public List<VW_CMA_PRODUCT_ENDORSEMENT> VW_CMA_PRODUCT_ENDORSEMENTS { get; set; }
// This property will hold all available Products for selection:
public IEnumerable<SelectListItem> Product_DropDownList { get; set; }
// NEW PROPERTY
public int SelectedProductId { get; set; }
}
In the view:
#model App_Endorsement.Models.ParentEndorsementViewModel
#using (Html.BeginForm("Manage_Endorsement", "Endorsement", new { ParentEndorsementViewModel = #Model }, FormMethod.Post, null))
{
#*#Html.AntiForgeryToken()*#
#* CHANGE THE TARGET PROPERTY HERE *#
#Html.DropDownListFor(m => m.SelectedProductId, new SelectList(Model.Product_DropDownList, "Value", "Text", Model.Product_DropDownList), "Show All Products")
<input type = "submit" value = "Send" />
}
In the Controller:
[HttpPost]
//[ValidateAntiForgeryToken]
public IActionResult Manage_Endorsement(ParentEndorsementViewModel In_ParentEndorsementViewModel)
{
var ParentEndorsementViewModel = In_ParentEndorsementViewModel;
var selectedProductId = ParentEndorsementViewModel.SelectedProductId;
I've created an MVC project using entity framework code first. My model is simply a form that gathers information.
public class Application
{
public int Id { get; set; }
public string FirstName { get; set; }
public string MiddleInitial { get; set; }
public string LastName { get; set; }
public int SSN { get; set; }
public DateTime DOB { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State {get; set; }
public string Zip { get; set; }
public int HomePhone { get; set; }
public int BusinessPhone { get; set; }
public int MobilePhone { get; set; }
}
My goal is to create a drop down list with all of the states, but I'm finding this to be very difficult given that I've already created the database via scaffolding with views and controllers. Is there a simple way to do this and tie it in with the existing database? I've searched for almost the entire day with no luck. An overview/explanation of what to include for the model/controller/view would be amazing!
Update: I've created a new model named "State" with properties "Id" and "StateName" and have created some states in the database. In my "Application" controller inside the Create action method I have:
Controller
public ActionResult Create()
{
ApplicationDbContext db = new ApplicationDbContext();
this.ViewData["Id"] = new SelectList(db.States.ToList(), "Id", "StateName");
return View();
}
View
#Html.DropDownList("Id")
Now the problem is I'm getting this error " There is no ViewData item of type 'IEnumerable' that has the key 'Id'." Would really appreciate help!
Its quite simple. Add an IEnumerable<SelectListItem> property to your model(Here I suggest you make a ViewModel that can have the exact same properties as Application with the below code included as a property). Then you just need to build the list and send it to your view
public IEnumerable<SelectListItem> States{ get; set; }
I will assume you want to retrieve the State values from the db. Here is how you will do it:
private IEnumerable<SelectListItem> GetAllStates()
{
IEnumerable<SelectListItem> list = from s in db.Applications
select new SelectListItem
{
Selected = false,
Text = s.State,
Value = s.State
};
return list;
}
Or this...
private IEnumerable<SelectListItem> GetAllStates()
{
IEnumerable<SelectListItem> list = db.Applications.Select(s => new SelectListItem
{
Selected = false,
Text = s.State,
Value = s.State
});
return list;
}
Then do something like this in your action:
var app = new Application
{
States = GetAllStates()
};
return View(app);
Then finally, use Razor on the view to display the Dropdown list like this
#Html.DropDownListFor(m => m.State, Model.States, "--Select a State--")
The 1st parameter is the property of the model to update, the 2nd is the list of data, and 3rd is the default message that will be displayed
Hope this helps.
Create a data layer that retrieves a list of what you want. Then use EF to get all the states.
//assuming you have a table of states..
var states = db.States();
The states table should be a Unique list of states.
var selectList = new List<SelectListItem>();
foreach(var thing in states){
//if you got everything, thus the ID field for the value...
selectList.Add(new SelectListItem {Text =thing.State, Selected = false, Value = thing.ID);
}
Make sure in your Viewmodel class that selectlist is a public property.....and set to what you did above. You also need to provied a string for the view selection post back.
StatesSelectList = selectList;
public IEnumberable<SelectListItem> StatesSelectList {get;set;}
public string SelectedState {get;set;}
In your view, do this:
#Html.DropDownListFor(p=>Model.SelectedState, Model.StatesSelectList)
Very simple Code step by step
1) In Entity Framework Class
var productsList = (from product in dbContext.Products
select new ProductDTO
{
ProductId = product.ProductId,
ProductName = product.ProductName,
}).ToList();
2) In Controller
ViewBag.productsList = new EntityFrameWorkClass().GetBusinessSubCategoriesListForDD();
3) In View
#Html.DropDownList("Product_ProductId", new SelectList(ViewBag.productsList, "ProductId", "ProductName"), new { #class = "form-control" })
OR
#Html.DropDownListFor(m=>m.Product_ProductId, new SelectList(ViewBag.productsList , "ProductId", "ProductName"), new { #class = "form-control" })
I assume there is a States model that has a Id and a StateName property.
Change to the list to ViewData["State"] to ensure easy binding on POST.
Id is the value that will be sent in the POST data ie.. State = Id. The StateName is what will be displayed in the Select list. So for your model this is not correct as State is a string. So needs to be
this.ViewData["State"] = new SelectList(db.States.ToList(), "StateName", "StateName");
Then in your view
#Html.DropDownList("State")
I have a problem to get multi select dropdown list values.can anyone suggest me how to get select multiple dropdownlist values as well as how to get them in controller.
My code is like this:-
Model
public string BusinessUnitSiteSafetyRepresentative { get; set; }
Controller
[HttpPost]
public ActionResult AddClientBusinessUnitSite(LocalAddClientBusinessUnitSite local)
{
var query = from o in entitydb.systemusersorganizations.toList()
from c in entitydb.contacts.toList()
where o.orgId == clientId
select new SelectListItem
{
Text = c. Name;
Value = c.OrgId.toString()
}
ViewBag.list1 = query.ToList();
}
Well, I can get if single value is selected & can save to DB.But how to select multiple values as well as to get them in Controller so as to save them.
Note: - I am retrieving the dropdownlist values from DB as shown above.
View
#Html.ListBoxFor(x => Model.BusinessUnitSiteSafetyRepresentative,new
MultiSelectList((IEnumerable<SelectListItem>)#Viewbag.list1)
I have gone through some examples but none of them helped me.Please help me.
What I suggest is that your model needs to have a one to many relationship with the items in your multi select list.
An example is a Blog with multiple tags:
Your blog model may look like:
public class Blog
{
public Blog()
{
Tags = new List<Tag>();
}
public string BlogTitle{ get; set; }
public string Body{ get; set; }
public virtual ICollection<Tag> Tags{ get; set; }
}
And your tag model like so:
public int TagID{ get; set; }
public string TagName{ get; set; }
public virtual ICollection<Blog> Blogs{ get; set; }
Now I recommend you use a view model:
public class BlogViewModel
{
public Blog blog{ get; set; }
public List<int> SelectedTags { get; set; }
public virtual List<Tag> Tags{ get; set; }
public BlogViewModel()
{
}
public BlogViewModel(Blog _blog, List<Tag> _Tags)
{
blog = _blog;
Tags = _Tags;
SelectedTags = new List<int>();
}
}
And finally in your View (which inherits from the ViewModel);
#Html.ListBoxFor(m => m.SelectedTags,
new MultiSelectList(Model.Tags, "TagID", "Tag")
, null)
The JQuery Chosen plugin is excellent for this http://harvesthq.github.io/chosen/. You can use it by:
#Html.ListBoxFor(m => m.SelectedTags,
new MultiSelectList(Model.Tags, "TagID", "Tag")
, new { #class = "chzn-select", data_placeholder = "Tags..." })
Replace this with your own model and controllers and this should solve your problem. Also, this will work in your form for creating a new blog post, and for editing an existing post (adding and removing tags)
edit:
In your Blog Create controller action, you would populate this as:
public ActionResult Create()
{
var blog = new Blog();
var AllTags = from t in db.Tags
select t;
BlogViewModel viewModel = new BlogViewModel(blog,
Tags.ToList());
return View(viewModel);
}
public ActionResult Create(BlogViewModel blogViewModel)
{
Blog blog = blogViewModel.blog;
if (blogViewModel.SelectedTags != null)
{
foreach (var TagID in blogViewModel.SelectedTags)
{
Tag tag = db.Tags.Where(t => t.TagID == TagID).First();
blog.Tags.Add(tag);
}
}
db.Blog.Add(blog);
db.SaveChanges();
}
Try to change your modelproperty to a list type to accept multiple values:
public IEnumerable<string> BusinessUnitSiteSafetyRepresentative { get; set; }
Good answer by EvoNet. Its a different approach but worked well for me.
Here is Microsofts official way to do it: http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/creating-a-more-complex-data-model-for-an-asp-net-mvc-application
Search for:
A join table is required in the database, however, as shown in the following database diagram:
I tried it and yes it created the table but I had to start editing the controller to get it to write to the table. Then I also had to think about creating cases for when a relationship already exists etc.
So I reveted to this method which worked just fine for me.
I'm new to MVC, so bear with me...
I've got my new form\view working (Creating and Adding a client)
But now I want to get the user so specifiy the Country where the new client is from A drop downlist. But im to sure how I to do this?
ViewModel
public class ClientNew
{
public string Company { get; set; }
public string Address { get; set; }
//New
public IEnumerable<CountryList> Country{ get; set; }
}
public class CountryList
{
public string Id { get; set; }
public string Name { get; set; }
}
Controller
(This is where is may be wrong, and is this the best way to do it?)
public ActionResult New()
{
var cl= new List<CountryList>();
cl.Add(new CountryList(){Id = "abcd",Name = "UK"});
cl.Add(new CountryList() { Id = "abce", Name = "USA" });
var model = new ViewModels.ClientNew();
model.Country= cl;
return View("New", model);
}
View (not sure how to plumb this in)
Html.DropDownList("Id" ??????)
In your view you will set up your dropdown on the property Id. This will be the current value selected in the dropdown when you POST to your form. The data that will be used for the dropdown is a SelectList called Countries that exists in your model.
#Html.DropDownListFor(m => m.Id, Model.Countries)
Your view model will have your Id, Name and Countries properties plus whatever else you need.
public class ClientNewViewModel {
public string Id { get; set; }
public string Name { get; set; }
public SelectList Countries { get; set; }
}
In your controller you need to pass the model to the view. You will need to populate the Countries SelectList. Keep in mind you will need to populate this value when you POST and fail validation as well.
public ActionResult New()
{
var model = new ClientNewViewModel();
model.Countries = new SelectList(service.GetCountries(),
"Id", "Name"); // set up what properties are used for id/name of dropdown
return View(model);
}
[HttpPost]
public ActionResult New(ClientNewViewModel model)
{
if( !ModelState.IsValid )
{
model.Countries = new SelectList(service.GetCountries(),
"Id", "Name");
return View(model);
}
// redirect on success
return RedirectToAction("Index");
}
Html.DropDownList("Id",
Country.Select(x => new SelectListItem
{
Text = x.Name,
Value = x.Id
}));
There's a good blog post on the in's and out's of how to do this here -> http://277hz.co.uk/Blog/Show/10/drop-down-lists-in-mvc--asp-net