I've been struggling to bind a MultiSelectList from Html helper to a specific model.
Model
In my model I have something like this:
public class DefaultSelection
{
public SelectionItem Selection1 {get;set;}
public SelectionItem Selection2 {get;set;}
public SelectionItem Selection3 {get;set;}
public IEnumerable<SelectionItem> Items{get;set;}
public DefaultSelection()
{
Selection1 = new SelectionItem(true, "Item1");
Selection2 = new SelectionItem(false, "Item2");
Selection3 = new SelectionItem(true, "Item3");
Items = new List<SelectionItem>(new SelectionItem[]
{Selection1,Selection2,Selection3});
}
}
public class SelectionItem
{
public bool Selected {get;set;}
public string Name {get;set;}
public SelectionItem(bool selected, string name)
{
Selected = selected;
Name = name;
}
}
What I want to do is binding my ListBox selection to Selected property of a SelectionItem.
I've tried this in my View:
#Html.ListBoxFor(model => model.Items, new MultiSelectList( Model.Items, Model.Items.Where(item => item.Selected)), new { #class = "selectpicker" })
Unfortunatly neither the selected items are shown properly (nothing is selected) nor has a selection any effects on the model...
Is there a way to solve this?
Can I get rid of this Items property for it is just a reference to the needed properties?
I think it's a lot easier than you think. Let's give this a try.
Create a good view model:
public class DefaultSelection
{
public IEnumerable<SelectListItem> Items { get; set; }
//Set a property to hold the selected items IDs.
public IEnumerable<string> SelectedItems { get; set; }
public DefaultSelection()
{
//preselected features
Items = new List<SelectListItem>
{
new SelectListItem { Text = "Item1", Selected = true},
new SelectListItem { Text = "Item2", Selected = false},
new SelectListItem { Text = "Item3", Selected = true}
};
}
}
Render the ListBox in the view:
<div class="form-group">
#Html.LabelFor(x => x.Items)
#Html.ListBoxFor(x => x.SelectedItems, Model.Items, new { #class = "selectpicker" })
</div>
Note that I'm using here the built-in System.Web.Mvc.SelectListItem class instead of your custom one SelectionItem- you can safely get rid of it.
Now you'll get the selected items id's (in this case the name, because you didn't specify value property when create the items) in your model after posting.
Hope this helps!
your #Html.ListBoxFor should be like this
#Html.ListBoxFor(model => model.SelectedNames, new MultiSelectList(Model.Items,"Name", "Name",Model.Items.Where(x=>x.Selected).Select(x=>x.Name)), new { #class = "selectpicker" })
you have to specify Value field and Text field of Model.Items which is "Name", "Name" and Model.Items.Where(x=>x.Selected).Select(x=>x.Name) will select all the values where selected is true but your model will return the values in list of string so you should define a field
public List<string> SelectedNames { get; set; }
where you will get the values of the selected items
Related
I have this model:
public class CampoTipoDocumentoViewModel
{
public int TipoDocumentoId { get; set; }
public string[] CamposId { get; set; }
private List<MiddleTier.Models.ICampo> _todosCampos;
public IEnumerable<SelectListItem> TodosCampos
{
get
{
foreach (var campo in _todosCampos)
yield return new SelectListItem { Text = campo.Nombre, Value = campo.Id.ToString() };
}
}
public void SetFields(List<MiddleTier.Models.ICampo> campos)
{
_todosCampos = campos;
}
}
In controller, CamposId property is assigned with elements which has to be selected in the view.
Controller also calls SetFields method populating _todosCampos to the whole list of records in the system.
The idea is to create a View with a SELECT that has initially some records selected.
This is my view:
#Html.DropDownListFor(m => m.CamposId, Model.TodosCampos, new { #class = "form-control", multiple = "multiple", width = "100%" })
The fact is that the HTML SELECT element is created with the list, but no option is selected.
For example, if _todosCampos contains:
Text = "One", Value = "1"
Text = "Two", Value = "2"
Text = "Three", Value = "3"
Text = "Four", Value = "4"
and CamposId contains:
Array of "2", "4"
I need the view to create a SELECT with those 4 options, and option 2 and 4 to be initially selected.
How can I achieve this?
Thanks
Jaime
In order to use <select> element with multiple="multiple" attribute, you need to declare List<string> property:
public List<string> CamposId { get; set; }
And then use ListBoxFor helper instead of DropDownListFor:
#Html.ListBoxFor(m => m.CamposId, Model.TodosCampos, new { #class = "form-control", multiple = "multiple", width = "100%" })
If you want to set some option values are selected by default, then set Selected property into SelectListItem:
public IEnumerable<SelectListItem> TodosCampos
{
get
{
foreach (var campo in _todosCampos)
{
// assumed you have 'campo.IsDefault' which is boolean property
yield return new SelectListItem
{
Text = campo.Nombre,
Value = campo.Id.ToString(),
Selected = campo.IsDefault // set default selected values
};
}
}
}
Note: Usually ID property contain integer values, you can try for List<int> CamposId depending on actual ID data type in database.
This is done by adding a view from my controller and selecting my dto as template
My DTO
public class Company_DTO
{
public long ID_Company { get; set; }
public string ESTATE_Company { get; set; }
}
MyController
public ActionResult UpdateCompany()
{
ViewBag.ListOfCompanies = DependencyFactory.Resolve<ICompanyBusiness>().GetCompany(); // this return a List<int> and following what I read for viewbag this should be right.
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateCompany([Bind]Company_DTO company_DTO)
{
try
{
//code
}
catch
{
return View();
}
}
View
<div class="form-group">
#Html.DropDownListFor(model => model.ID_Company , ViewBag.ListOfCompanies) // Here I get an error on my #Html that my dto does nothave a list.
</div>
I want the selected item to be ID_Company, but here it seems to be trying to add the whole list when I just want the selected item, I cant find any documentation or question that can solve my issue.
I Cant EDIT the DTO.
Thanks for any help and hope I am being clear enough.
This should solve your problem:
View
<div class="form-group">
#Html.DropDownListFor(model => model.ID_Company, new SelectList(ViewBag.Accounts, "ID_Company", "ESTATE_Company"))
</div>
Supposing your view is strongly typed (#model Company_DTO).
Hope this helps
consider the following example:
public class HomeController : Controller
{
private List<SelectListItem> items = new List<SelectListItem>()
{
new SelectListItem() { Text = "Zero", Value = "0"},
new SelectListItem() { Text = "One", Value = "1"},
new SelectListItem() { Text = "Two", Value = "2"}
};
public ActionResult Index()
{
ViewBag.Items = items;
return View(new Boo() { Id = 1, Name = "Boo name"});
}
}
public class Boo
{
public int Id { get; set; }
public string Name { get; set; }
}
the view:
#model WebApi.Controllers.Boo
#Html.DropDownListFor(x=>x.Id, (IEnumerable<SelectListItem>) ViewBag.Items)
so, ViewBag.ListOfCompanies should contain IEnumerable. Each SelectListItem has Text and Value property , you need to assign ESTATE_Company and ID_Company respectively. something like this:
var companiesList = //get companies list
ViewBag.ListOfCompanies = companiesList.Select(x => new SelectListItem() {Text = x.ESTATE_Company, Value = x.ID_Company.ToString()});
....
#Html.DropDownListFor(x=>x.ID_Company, ViewBag.Items as IEnumerable<SelectListItem>)
I have this MultipleSelectList in my view:
#Html.ListBoxFor(s => s.Id,
new MultiSelectList((IEnumerable<SelectListItem>)ViewData["ddlList"], "Value", "Text", Model.Id),
new { #style = "margin-top:250px", multiple = "multiple" })
This list is populated here
#{
using (var b = new Entity())
{
ViewData["ddlList"] = b.Table.Select(e => new SelectListItem()
{
Value = e.Id.ToString(),
Text = e.Name
}).ToList();
}
}
This is my model
public string Name { get; set; }
public int[] Id { get; set; }
The problem is that when I select multiple options, only the first one gets to my controller like this:
int[] value = modelObj.Id;
modelObj.Id;--stores my selected values
Someone knows how can I solve this?
Selected values will be array of string not array of int. So, change the type of Id property to type of string[]:
public string[] Id { get; set; }
When I run the code, I can only select one item at a time, that's weird because 'ListBoxFor()' is used to select multiple items, so what i want is:
Select multiple items
View (Index.cshtml):
<div>
#Html.ListBoxFor(m => m.DropDownItems, new MultiSelectList(Repository.DDFetchItems(), "Value", "Text", Model.DropDownItems))
</div>
Model (ModelVariables.cs):
public class ModelVariables
{
public List<SelectListItem> DropDownItems { get; set; }
}
public static class Repository
{
public static List<SelectListItem> DDFetchItems()
{
return new List<SelectListItem>()
{
new SelectListItem(){ Text = "Dogs", Value = "1", Selected = true},
new SelectListItem(){ Text = "Cats", Value = "2"},
new SelectListItem(){ Text = "Death", Value = "3"}
};
}
}
Controller (HomeController.cs):
[HttpGet]
public ActionResult Index()
{
ModelVariables model = new ModelVariables()
{
DropDownItems = Repository.DDFetchItems()
};
return View(model);
}
You cannot bind a <select multiple> to a collection of complex objects (which is what List<SelectListItem> is). A <select multiple> posts back an array of simple values (in your case, if you select the 1st and 3rd options, it will submit [1, 3] (the values of the selected options).
Your model needs a IEnumerable<int> property to bind to.
public class ModelVariables
{
public IEnumerable<int> SelectedItems { get; set; }
public IEnumerable<SelectListItem> DropDownItems { get; set; }
}
and then in the GET method
public ActionResult Index()
{
var ModelVariables= new ModelVariables()
{
DropDownItems = Repository.DDFetchItems(),
SelectedItems = new List<int>(){ 1, 3 } // to preselect the 1st and 3rd options
};
return View(model);
}
and in the view
#Html.ListBoxFor(m => m.SelectedItems, Model.DropDownItems)
Side notes
Remove Selected = true in the DDFetchItems() method - its
ignored by the ListBoxFor() method because its the value of the
property your binding to which determines what is selected
There is not need to build a new identical SelectList from the
first one inside the ListBoxFor() method (property DropDownItems
is already IEumerable<SelectListItem>)
I have a many to many relationship between cars and many other tables. In my view I want to be able to have a multiselect list for items such as lights. I've tried many different formats I can't seem to get the list to set selected items.My preferred way to do it would be #Html.ListBoxFor(model => model.enitity.relatedentity, Model.SomeSelectListItemList) , but I can not seem to get list to set selected items in the view.
Below is my code I was testing with
I use Asp.net MVC5, C# and entity framework 6.
This is my controller action
// GET: Car/Edit/
[Authorize(Roles = "Ecar")]
public ActionResult Edit(int id)
{
CarsEditViewModel carEditViewModel = new CarsEditViewModel();
carEditViewModel.Cars = unitOfWorkcar.CarRepository.FindIncluding(id);
IList<Approval> approvalList = unitOfWorkcar.ApprovalRepository.All.ToList();
IList<Connector> connectorList = unitOfWorkcar.ConnectorRepository.All.ToList();
IList<InputVoltage> inputVoltagesList = unitOfWorkcar.InputVoltageRepository.All.ToList();
carEditViewModel.ApprovalList = from c in approvalList select new SelectListItem { Text = c.Name, Value = c.Id.ToString(), Selected = true};
carEditViewModel.Categories = new MultiSelectList(carEditViewModel.ApprovalList, "Value", "Text", "Selected");
// ,carEditViewModel.ApprovalList.Select(c => c.Text),carEditViewModel.ApprovalList.Select(c => c.Selected)
//carEditViewModel.ApprovalList = from c in approvalList select new MultiSelectList( //{ Selected = (carEditViewModel.Cars.Approvals.Any(app => app.Id == c.Id)) , Text = c.Name, Value = c.Id.ToString() };
// carEditViewModel.ConnectorList = from c in connectorList select new SelectListItem { Selected = true, Text = c.Name, Value = c.Id.ToString() };
carEditViewModel.InputVoltageList = from c in inputVoltagesList select new SelectListItem { Text = c.Name, Value = c.Id.ToString() };
return View(carEditViewModel);
}
Here is my view
#model NewBobPortal.ViewModels.CarsEditViewModel
#using (Html.BeginForm())
{
#* #Html.ListBoxFor("SelectedApprovals",model => model., new { #class = "multiselect" })*#
#*#Html.ListBoxFor(model => model.Cars.Approvals, Model.ApprovalList)
#Html.ListBoxFor(model => model.Cars.Connectors,Model.ConnectorList, new {Multiple = "multiple"})
#Html.ListBoxFor(model => model.ConnectorList, Model.ConnectorList)*#
#*#Html.ListBox("test",Model.Cars.InputVoltages, Model.InputVoltageList)*#
#Html.DropDownList("somethingelse", new MultiSelectList(Model.InputVoltageList, "Value", "Text", Model.InputVoltageList.Select(c => c.Value)), new { multiple = "multiple" })
#Html.DropDownListFor(model => model.Cars.InputVoltages , new MultiSelectList(Model.LensColorList, "Value", "Text", Model.LensColorList.Select(c => c.Value)), new { multiple = "multiple" })
#Html.ListBoxFor(m => m.Cars.Approvals, Model.Categories)
<p>
<input type="submit" value="Save" />
</p>
}
This is my viewmodel
using NewBobPortal.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace NewBobPortal.ViewModels
{
public class CarsEditViewModel
{
public Car Cars { get; set; }
public IEnumerable<SelectListItem> ApprovalList { get; set; }
public IEnumerable<MultiSelectList> ConnectorList { get; set; }
public IEnumerable<SelectListItem> InputVoltageList { get; set; }
public MultiSelectList Categories { get; set; }
public IEnumerable<Approval> SelectedCategories { get; set; }
}
}
The biggest problem with posting values for a many-to-many relationship is that there's no direct field to bind to on your model. This is where view models become very handy, which you're already using, but not quite in the right way for this.
First you need your SelectList, which can actually just be an IEnumerable<SelectListItem>. This will contain all available options, which is easy enough. So in your view model:
public IEnumerable<SelectListItem> CategoryChoices { get; set; }
And in your action:
carEditViewModel.CategoryChoices = approvalList.Select(m => new SelectListItem {
Text = c.Name,
Value = c.Id.ToString()
});
Notice that I'm not setting Selected: we'll let the HtmlHelper handle that. I'm also not dealing with a MultiSelectList yet either.
Now, you'll also need something to post back to, since your values will be ids, we'll use a List<int>, so in your view model:
private List<int> selectedCategories;
public List<int> SelectedCategories
{
get
{
if (selectCategories == null)
{
selectedCategories = Categories.Select(m => m.Id).ToList();
}
return selectedCategories;
}
set { selectedCategories = value; }
}
There's a bit going on here. The set method of the property is simple: when we get a posted value back, just set selectedCategories to that. The get is a bit more complicated: here we need to condense down your list of category objects (called Categories here because I don't know where this is actually coming from) into a simple list of ids for those categories.
Now, in your view:
#Html.ListBoxFor(m => m.SelectedCategories, Model.CategoryChoices)
That's all you need. You're using a ListBox control so it's already a multiple select list. And, by binding it to the list of all currently selected ids, it knows which items to select automatically in the list of SelectListItems it gets from Model.CategoryChoices.
In your post action, you then need to translate these ids into their associated objects:
var newCategories = repository.Categories.Where(m => carEditViewModel.SelectedCategories.Contains(m.Id));
Then, you can set your model's categories to this new list manually:
car.Categories = newCategories;