Binding array to multiple DropDownListFor - asp.net-mvc

I have a scenario where I want to pick 3 items out of a list of checkboxes. This works fine, but if the list of checkboxes gets long, the page looks unwieldy. So I wanted to change it to 3 dropdown lists. so the page was a lot smaller, but none of the dropdown lists honor the selected value.
So I tried code like this
#Html.DropDownListFor(model => model.CheckedLarcenyTypes, new SelectList(Model.LarcenyTypes, "Key", "Value", Model.CheckedLarcenyTypes.Length > 0 ? Model.CheckedLarcenyTypes[0] : null as Int32?), String.Empty, new { id = "Larceny1" })
#Html.DropDownListFor(model => model.CheckedLarcenyTypes, new SelectList(Model.LarcenyTypes, "Key", "Value", Model.CheckedLarcenyTypes.Length > 1 ? Model.CheckedLarcenyTypes[1] : null as Int32?), String.Empty, new { id = "Larceny2" })
#Html.DropDownListFor(model => model.CheckedLarcenyTypes, new SelectList(Model.LarcenyTypes, "Key", "Value", Model.CheckedLarcenyTypes.Length > 2 ? Model.CheckedLarcenyTypes[2] : null as Int32?), String.Empty, new { id = "Larceny3" })
Now the dropdowns are created correctly and the correct value is getting bound and on a POST I see the values getting sent back in the view model.
I just can't get the selectvalue to show in the dropdown. upon re-loading the page, the dropdowns are still just blank.
What am I doing wrong here? Is this even possible?

Your problem is related to what's described here.
Reading the Selection
If you are using the same model to accept input from the edit view
during a postback, you might think the default model binder will
repopulate the Albums collection with all the album information and
set the selected album. Unfortunately - the web doesn’t work this way
and the Albums collection will be empty.
So you must have a ViewModel like this:
public class LarcenyViewModel
{
public int CheckedLarcenyType1 { get; set; }
public int CheckedLarcenyType2 { get; set; }
public int CheckedLarcenyType3 { get; set; }
public IEnumerable<SelectListItem> LarcenyTypes { get; set; }
}
Populate the ViewModel like this:
LarcenyViewModel larcenyViewModel = new LarcenyViewModel();
// Use a database, enum or whatever to get the LarcenyTypes... :)
larcenyViewModel.LarcenyTypes = new SelectList(
database.FindAllLarcenyTypes(), "LarcenyId", "Name");
View code:
#Html.DropDownListFor(model => model.CheckedLarcenyType1, Model.LarcenyTypes, String.Empty, new { id = "Larceny1" })
#Html.DropDownListFor(model => model.CheckedLarcenyType2, Model.LarcenyTypes, String.Empty, new { id = "Larceny2" })
#Html.DropDownListFor(model => model.CheckedLarcenyType3, Model.LarcenyTypes, String.Empty, new { id = "Larceny3" })

Related

Using MVC5, How can I databind a collection to a DropDownListFor

I am developing an MVC5 application. I have a controller/view to Create new AccountDtos. An AccountDto has a collection (List<KeyValuePair<string, string>>) of AssociatedVendors. My AccountDto is a simple POCO and looks like this
public class AccountDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Reference { get; set; }
public List<KeyValuePair<string, string>> AssociatedVendors { get; set; }
. . .
}
In my view, I have two multi-select controls (see image below). One lists all Vendors, the other lists the vendors I select. The goal is to allow me to select a sub-set of vendors to associate with my AccountDto upon creation.
I add items from either select control using javascript
$("#vendor-add").on("click", function(e) {
e.preventDefault();
$("#vendor-source-select").find(':selected').appendTo($("#vendor-dest-select"));
});
$("#vendor-addall").on("click", function (e) {
e.preventDefault();
$("#vendor-source-select").children().appendTo($("#vendor-dest-select"));
});
$("#vendor-remove").on("click", function (e) {
e.preventDefault();
$("#vendor-dest-select").find(':selected').appendTo($("#vendor-source-select"));
});
$("#vendor-removeall").on("click", function (e) {
e.preventDefault();
$("#vendor-dest-select").children().appendTo($("#vendor-source-select"));
});
The problem is databinding. I am trying to databind the select control on the right to the AssociatedVendors that I select. I have tried various flavours of ListBoxFor and DropdownListFor such as
#Html.DropDownListFor(m => m.AssociatedVendors,
new SelectList(Model.AssociatedVendors.Select(s => new SelectListItem{Text = s.Value, Value = s.Key})),
htmlAttributes: new { id = "vendor-dest-select", #class = "vendor-select", multiple = "multiple" })
#Html.DropDownListFor(x => x.AssociatedVendors, new SelectList(Model.AssociatedVendors, "Key", "Value"))
#Html.ListBoxFor(x => x.AssociatedVendors, new SelectList(Model.AssociatedVendors, "Key", "Value"))
When I submit my view back to the controller, the AssociatedVendors property is always 0 length. I'd appreciate any help or pointers in the right direction. Thank you.
I've come up with a kind of fix. I realise that I cannot data bind to the same list that's being edited, so I added a new collection of AssocVendorIds to my AccountDto. Then I can data bind to that collection using
#Html.DropDownListFor(M => M.AssocVendorIds,
new MultiSelectList(Model.Vendors.Select(s => new SelectListItem{Value = s.Id.ToString(), Text = s.Name}),
"Value", "Text"), htmlAttributes: new { id = "vendor-dest-select", #class = "vendor-select", multiple = "multiple" })

DropDownList cant get selected values

I'm stucked at creating dropdownlist in ASP.NET MVC.
ViewModel:
public MultiSelectList users { get; set; }
I set the values in controller:
var allUsers = db.Users.Select(u => new {
id = u.UserId,
name = u.Name
}).ToList();
model.users = new MultiSelectList(allUsers, "id", "name");
so selectbox values are set.
In view:
#Html.DropDownListFor(m => m.users, Model.users, new { #class = "form-control" })
The problem is that if I select the value and click submit i get this error:
No parameterless constructor defined for this object.
I think the problem is in the way how I create the dropdownlist in view, I'm not sure how to set it, thanks.
EDIT: If I dont choose any user from dropdown all goes well, but if I choose then the error appears.
You're trying to post to the MultiSelectList property. That's not going to work regardless, but the specific error is related to the fact that MultiSelectList has no parameterless constructor, and there's no way for the modelbinder to new up a class with parameters. Anything involved in the modelbinding process must have a parameterless constructor.
What you should be doing is have an additional property like:
public List<int> SelectedUserIds { get; set; }
And, then bind to that in your view:
#Html.ListBoxFor(m => m.SelectedUserIds, Model.Users)
Also, as you'll notice, I changed DropDownListFor to ListBoxFor. If you're wanting to have a select multiple, you need ListBoxFor.
Looks like it is failing when trying to bind, so to prevent it from binding:
[HttpPost]
public ActionResult YourMethod([Binding(Exclude = "users")] SomeViewModel model)
The post back should go to an IEnumerable to capture the selected items.
Add to view model
public IEnumerable UserList { get; set; }
Change view to
#Html.DropDownListFor(m => m.UserList, Model.users, new { #class = "form-control" })
If you want get selected user id from a dropdownlist you must add a property to your model
public MultiSelectList users { get; set; }
public int SelectedUser { get;set;}
And in view
#Html.DropDownListFor(m => m.SelectedUser, Model.users, new { #class = "form-control" })

How to work with DropDownListFor in an EDIT view

Hi I have a problem with DropDownListFor on the Edit view.
Basically I'm using a partial view which contains my form and in my Edit and Create view I call this partial view.
I have around 5 similiar DropdownlistFor and these work well on create action but in edit doesn't, mainly i'm not getting (unable) to set the selected value.
In my Edit Action (GET), I fill my property ViewModel if the true object has the property filled.
if(icb.BAOfficer != null)
editICB.BAOfficer = icb.BAOfficer;
List<Staff> staffs = _fireService.GetAllStaffs().ToList();
staffs.Insert(0, new Staff { StaffId = -1, Name = "" });
editICB.BAOfficers = staffs;
return View(editICB);
This is how I'm filling my drop down and how I'm trying to set the selected value.
#Html.DropDownListFor(model => model.BAOfficerSelected, new SelectList(Model.BAOfficers, "StaffId", "Name", (Model.BAOfficer!= null ? Model.BAOfficer.StaffId:-1)), new { #class = "rounded indent" })
#Html.ValidationMessageFor(model => model.BAOfficer.StaffId)
I solve the problem setting a value to my model.BAOfficerSelected in Edit Action, this was the (easy) secret.
I need the first item like a empty option because is not a required information, but on the edit view if has value I need to set it as selected option.
In the end, it was my code.
My Model
public int BAOfficerSelected { get; set; }
public SelectList BAOfficers { get; set; }`
My Controller Create/Edit Action
if (icb.BAOfficer != null) // only for edit action
editICB.BAOfficerSelected = icb.BAOfficer.StaffId; //this will set the selected value like a mapping
//for Edit and Create
List<Staff> staffs = _fireService.GetAllStaffs().ToList();
staffs.Insert(0, new Staff { StaffId = -1, Name = "" });
editICB.BAOfficers = new SelectList(staffs, "StaffId", "Name");
return View(editICB);`
My View
#Html.DropDownListFor(model => model.BAOfficerSelected, Model.BAOfficers, new { #class = "rounded indent" })
I hope this can help others.
The best and cleanest way of doing this is setting the selected value in server side, in the SelectList object.
So, if your BAOfficerSelected is nullable... it is all simpler: You don't need to rely in adding a dummy item to hold the -1 for not selected value.
Instead, you do it this way:
List<Staff> staffs = _fireService.GetAllStaffs().ToList();
editICB.BAOfficers = new SelectList(staffs, "StaffId", "Name", editICB.BAOfficer != null ? editICB.BAOfficer.StaffId : null);
Of course, the BAOfficers need to be changed type from List<Staff> to SelectList.
Then, in your partial view you do:
#Html.DropDownListFor(model => model.BAOfficerSelected, Model.BAOfficers, "Select one...", new { #class = "rounded indent" })
Adding the 3rd parameter is needed to indicate that the default value (if nothing is selected) is that text.
Instead of using a SelectList, I often find it works better to use a List<SelectListItem>.
Further, I usually use an EditorTemplate for my dropdowns to keep my views clean.
So if my select list returns List<SelectListItem>:
public List<SelectListItem> BAOfficers { get; set };
You can set it up like this:
model.BAOfficers = staffs.Select(staff =>
new SelectListItem { Text = staff.Name, Value = staff.StaffId }).ToList();
Then in your EditorTemplate:
<!-- EditorTempaltes\DropDownList.cshtml -->
#model System.String
<p>
#Html.LabelFor(m => m):
#Html.DropDownListFor(m => m, new SelectList(
(List<SelectListItem>)ViewData["selectList"], "Value", "Text",
String.IsNullOrEmpty(Model) ? String.Empty : Model), String.Empty)
#Html.ValidationMessageFor(m => m)
</p>
And then in the view, just pass the SelectList into the EditorTemplate:
#Html.EditorFor(m => m.BAOfficerSelected, "DropDownList",
new { selectList = Model.BAOfficers() })
I met the same problem ,too.
According the article https://dotnetfiddle.net/PIGNLF which way gave a simple way to deal with this problem without two Models or more classes.enter link description here
here is my code
add model
public class NoteSelectLisModel
{
public string Value { get; set; }
public string Name { get; set; }
}
add Controller
public ActionResult Edit(int? _ID)
{
ViewBag.NoteState = new SelectList(new List<NoteSelectLisModel>()
{
new NoteSelectLisModel() {Value="1",Name="A)"},
new NoteSelectLisModel() {Value="2",Name="B"},
new NoteSelectLisModel() {Value ="3",Name ="C"}
}, "Value", "Name", 1);
Table ut = _db.Tables.Find(_ID);
if (ut == null)
{
return HttpNotFound();
}
else
{
return View(ut);
}
}
add View .cshtml
#Html.DropDownListFor(m => m.NOTE, (IEnumerable<SelectListItem>)ViewBag.NoteState, "No Selected")
The edit's Model is the same and the dropdownlist passed by View.bag

SelectList selected value not carried over to DropDownList

I have a Razor page with a drop down list inside a form:
#using (Html.BeginForm("ProductsByOwners", "Report", FormMethod.Post, new { #id = "ProductsByOwners" }))
{
#Html.Label("Choose product owner: ")
#Html.DropDownList("OwnerList", (SelectList)ViewBag.OwnerList, new { #onchange = "this.form.submit();" })
}
The selected value of my SelectList is not being carried over to the DropDownList. I've debugged and stepped through the code and found that (SelectList)ViewBag.OwnerList evaluates properly and has the expected value selected, but the resulting HTML does not have any of the option tags selected.
Can anyone see what I'm doing wrong here?
UPDATED
Here is how the SelectList is created in my action:
ViewBag.OwnerList = new SelectList(ListUtils.ProductOwners(), "Key", "Value", values["OwnerList"]);
The result has the value indicated by values["OwnerList"] selected.
Thanks!
You are not using the DropDownList helper properly. In order to create a dropdownlist you need 2 things:
a scalar property to bind to the selected value when the form is submitted
a collection to bind the options to
In your example you have only one of those 2 things (the second). Your first argument is called OwnerList and you have ViewBag.OwnerList passed as second argument.
So:
#Html.DropDownList(
"SelectedOwnerId",
(SelectList)ViewBag.OwnerList,
new { #onchange = "this.form.submit();" }
)
Obviously I would recommend you using strongly typed views ans view models. And obviously get rid of the weakly typed ViewBag/ViewData/ViewCrap.
So start by designing a view model to meet the requirements of your view (which from what you have shown so far is to display a dropdownlist):
public class OwnerViewModel
{
[DisplayName("Choose product owner: ")]
public string SelectedOwnerId { get; set; }
public IEnumerable<SelectListItem> OwnerList { get; set; }
}
then a controller:
public class ReportController: Controller
{
public ActionResult ProductsByOwners()
{
var model = new OwnerViewModel
{
// preselect the second owner
SelectedOwnerId = "2",
// obviously those come from your database or something
OwnerList = new[]
{
new SelectListItem { Value = "1", Text = "owner 1" },
new SelectListItem { Value = "2", Text = "owner 2" },
new SelectListItem { Value = "3", Text = "owner 3" },
}
};
return View(model);
}
[HttpPost]
public ActionResult ProductsByOwners(OwnerViewModel model)
{
...
}
}
and you have a corresponding strongly typed view:
#model OwnerViewModel
#using (Html.BeginForm("ProductsByOwners", "Report", FormMethod.Post, new { id = "ProductsByOwners" }))
{
#Html.LabelFor(x => x.SelectedOwnerId)
#Html.DropDownListFor(
x => x.SelectedOwnerId,
Model.OwnerList,
new { onchange = "this.form.submit();" }
)
}
The most common reason the selected item is not selected in the DDL is you've named the selectlist the same as the model.
Strongly typed views are preferred, but it's fine to pass the SelectList in a Viewbag. See my tutorial Working with the DropDownList Box and jQuery and my blog Cascading DropDownList in ASP.Net MVC

Challenges with selecting values in ListBoxFor

Working on my first ASP.Net MVC2 web app recently, I came across some issues when I needed to select multiple values in a list box. I worked around it with some jQuery, but went ahead and put together some very simple code to demonstrate. I'm using EF for the model, with two entities - Customers and HelpDeskCalls:
Controller:
public ActionResult Edit(int id)
{
Customer currCustomer = ctx.Customers.Include("HelpDeskCalls").Where(c => c.ID == id).FirstOrDefault();
List<HelpDeskCall> currCustCalls = (ctx.HelpDeskCalls.Where(h => h.CustomerID == id)).ToList();
List<SelectListItem> currSelectItems = new List<SelectListItem>();
List<String> selectedValues = new List<string>();
foreach (HelpDeskCall currCall in currCustCalls)
{
bool isSelected = (currCall.ID % 2 == 0) ? true : false;
//Just select the IDs which are even numbers...
currSelectItems.Add(new SelectListItem() { Selected = isSelected, Text = currCall.CallTitle, Value = currCall.ID.ToString() });
//add the selected values into a separate list as well...
if (isSelected)
{
selectedValues.Add(currCall.ID.ToString());
}
}
ViewData["currCalls"] = (IEnumerable<SelectListItem>) currSelectItems;
ViewData["currSelected"] = (IEnumerable<String>) selectedValues;
return View("Edit", currCustomer);
}
View:
<div class="editor-field">
<%: Html.ListBoxFor(model => model.HelpDeskCalls, new MultiSelectList(Model.HelpDeskCalls, "ID", "CallTitle", (IEnumerable) ViewData["currSelected"]), new { size = "12" })%>
<%: Html.ListBoxFor(model => model.HelpDeskCalls, ViewData["currCalls"] as IEnumerable<SelectListItem>, new { size = "12"}) %>
<%: Html.ListBox("Model.HelpDeskCalls", new MultiSelectList(Model.HelpDeskCalls, "ID", "CallTitle", (IEnumerable)ViewData["currSelected"]), new { size = "12"}) %>
<%: Html.ValidationMessageFor(model => model.HelpDeskCalls) %>
</div>
For this sample, I'm just selecting HelpDeskCall.IDs which are even. I'm trying two different syntaxes for ListBoxFor: One uses an IEnumerable of values for selections, one using an IEnumerable of SelectListItems. By default, when I run this code, no selections are made to either ListBoxFor, but the non-strongly typed ListBox selects correctly.
I read this post on ASP.Net and this thread on SO, but no joy. In fact, if I add the override ToString() to my HelpDeskCall class (as suggested in the ASP.net thread) all values are selected, which isn't right either.
If someone could shed some light on how this should work (and what I'm missing or doing wrong), this then neophyte would be very grateful.
Here's an example illustrating the strongly typed version:
Model:
public class MyViewModel
{
public int[] SelectedItemIds { get; set; }
public MultiSelectList Items { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
// Preselect items with id 1 and 3
var selectedItemIds = new[] { 1, 3 };
var model = new MyViewModel
{
Items = new MultiSelectList(
new[]
{
// TODO: Fetch from your repository
new { Id = 1, Name = "item 1" },
new { Id = 2, Name = "item 2" },
new { Id = 3, Name = "item 3" },
},
"Id",
"Name",
selectedItemIds
)
};
return View(model);
}
}
View:
<%: Html.ListBoxFor(x => x.SelectedItemIds, Model.Items) %>
I don't know if this behaviour has changed in the RTM of MVC3 that I'm using, but it seems that selection and binding now works out of the box. The only catch is that the model should contain a property with the IDs, like that :
public class MyViewModel {
public int[] ItemIDs { get; set; }
}
Then the following in the view would work fine, both pre-selecting the correct values and binding correctly during post:
#Html.ListBoxFor(model => model.ItemIDs, (IEnumerable<SelectListItem>)(new[] {
new SelectListItem() { Value = "1", Text = "1" },
new SelectListItem() { Value = "2", Text = "2" }
}))
I have found better workaround. Usual way to preselect select list:
#Html.ListBoxFor(
model => model.Roles,
new MultiSelectList(db.Roles, "Id", "Name")
)
#Html.ValidationMessageFor(model => model.Roles)
Doesn't work.., never ever any option is selected, until:
public ActionResult Edit(int id)
{
var user = db.Users.Find(id);
// this is workaround for http://aspnet.codeplex.com/workitem/4932?ProjectName=aspnet
ViewData["Roles"] = user.Roles.Select(r => r.Id);
return View(user);
}
Selected Roles has to be stored in ViewData, to workaround nasty bug.
Another option is to take advantage of nameof, you could do something like this;
Html.ListBox(nameof(MainProjectViewModel.Projects), Model.Projects)

Resources