I have two multi-select list boxes; one with months and one with years such as the following:
#Html.ListBoxFor(m => m.IncludeGuestsArrivedInTimeframeMonths, Model.TimeframeMonths)
#Html.ListBoxFor(m => m.IncludeGuestsArrivedInTimeframeYears, Model.TimeframeYears)
I'm saving the both values to the database as a comma separated list (string/nvarchar). The values are being like this: Months: 1,3,7; Years: 2002,2005
For some reason when I pull the values back out to the form, the months are pre-selecting in the listbox fine, but the years are not.
Any ideas?
EDIT - Additional code samples:
Controller - Manage
public ActionResult Manage(Guid id)
{
var list = _listService.GetList(id);
var model = LoadModelFromObject(list);
model.TimeframeMonths = GenerateMonthDropdown();
model.TimeframeYears = GenerateYearDropdown(model.IncludeGuestsArrivedInTimeframeYears.Split(','));
model.DaysOfWeek = GenerateDaysOfWeekDropdown();
return View(model);
}
Controller - 'Helper'
private IList<SelectListItem> GenerateYearDropdown(string[] selected)
{
var list = new List<SelectListItem>();
var startYear = DateTime.Now.Year - 10;
for (int idx = startYear; idx < startYear + 11; idx++)
{
list.Add(new SelectListItem
{
Value = idx.ToString(),
Text = idx.ToString(),
Selected = selected != null && selected.Contains(idx.ToString())
});
}
return list;
}
View
#Html.CheckBoxFor(m => m.IncludeGuestsArrivedInTimeframe)
#Html.LabelFor(m => m.IncludeGuestsArrivedInTimeframe)
#Html.ListBoxFor(m => m.IncludeGuestsArrivedInTimeframeMonths, Model.TimeframeMonths)
#*#Html.ListBoxFor(m => m.IncludeGuestsArrivedInTimeframeYears, Model.TimeframeYears)*#
#Html.ListBox("IncludeGuestsArrivedInTimeframeYears", Model.TimeframeYears)
You need to set Selected property of SelectItemList class to true for the options which you want to be pre-selected.
Example:
#{
var foo = new List<SelectListItem> {
new SelectListItem { Text = "Foo 1", Value = "1", Selected = true },
new SelectListItem { Text = "Foo 2", Value = "2" },
new SelectListItem { Text = "Foo 3", Value = "3", Selected = true }
};
}
#Html.ListBox("foo", foo)
In this case, Foo 1 and Foo 3 should appear as pre-selected. The above code generates the below html markup:
<select id="foo" multiple="multiple" name="foo">
<option selected="selected" value="1">Foo 1</option>
<option value="2">Foo 2</option>
<option selected="selected" value="3">Foo 3</option>
</select>
Edit:
First of all, replace this code:
list.Add(new SelectListItem {
Value = idx.ToString(),
Text = idx.ToString(),
Selected = selected != null && selected.Contains(idx.ToString())
});
with this code:
list.Add(new SelectListItem {
Value = idx.ToString(),
Text = idx.ToString(),
Selected = true
});
Then run the app. If all the options are pre-selected, then I bet that this selected != null && selected.Contains(idx.ToString()) does not satisfy the condition.
Another way of doing it
#Html.ListBox("categoryId", new MultiSelectList(ViewData["categories"] as System.Collections.IEnumerable, "Id", "CategoryName", Model.tIdx_ProductCategories.Select(x => x.CategoryId).AsEnumerable()))
Explanation:
categoryId - would be name of control, when rendered as html
ViewData["categories"] - is list of categories with Id and CategoryName properties
Model.tIdx_ProductCategories carries the list of category ids to be selected in list box.
As it seems (e.g. here Preselect Items in Multiselect-Listbox (MVC3 Razor)) the Razor engine happily ignores the Selected property and compares the list from the model and the supplied option list by calling ToString() on the objects from the model and using string comparison to select.
What I cannot explain to myself is why this behavior is used with Html.ListBox(), which does not use any model and expects a collection of SelectListItem.
Think it's a bug, but it does not seem to have been addressed recently.
Related
I am testing a new plugin that converts a multiple-select ListBox to a set of multiple mutually-exclusive single-select drop-down lists. This should have been very straight-forward, as I just wanted to start with some items selected, but the answer eludes me so far.
I have reduced the problem to the following simple controller code (the plugin was turned off so is not a factor):
List<SelectListItem> selectList2 = db.LookupType.Select(x => new SelectListItem() { Selected = x.LookupTypeId < 4, Value = x.LookupTypeId.ToString(), Text = x.Name }).ToList();
ViewBag.LookupTypeId2 = selectList2;
This will mark Selected = true on the first three items as the IDs start at 1 (confirmed in debugger that the first 3 items have Selected = true).
And the view looks like this:
#Html.ListBox("LookupTypeId2", (IEnumerable<SelectListItem>)ViewBag.LookupTypeId2, htmlAttributes: new { #class = "form-control"})
The resulting output however has no selected items:
<select name="LookupTypeId2" class="form-control" id="LookupTypeId2" multiple="multiple">
<option value="1">Degree</option>
<option value="2">WorkdayLanguage</option>
<option value="3">Language_Ability_Type_ID</option>
<option value="4">National_ID</option>
<option value="5">Phone_Device_Type_ID</option>
<option value="6">Background_Check_Status_ID</option>
<option value="7">Educational_Institution_Type_ID</option>
<option value="8">Gender_Code</option>
<option value="9">Military_Status_ID</option>
<option value="10">Ethnicity_ID</option>
</select>
I have no problem selecting multiples with CTRL-Click etc, but the initial selections are not visible. What am I missing here?
Update: based on answer below I changed it to a ListBoxFor
#Html.ListBoxFor(x=>x.LookupTypeId2, (IEnumerable<SelectListItem>)ViewBag.LookupTypeId2, htmlAttributes: new { #class = "form-control"})
have added the following value initialization:
List<SelectListItem> selectList2 = db.LookupType.Select(x => new SelectListItem() { Selected = x.LookupTypeId < 4, Value = x.LookupTypeId.ToString(), Text = x.Name }).ToList();
lookup.LookupTypeId2 = new string[]{"1","2","3","4"};
ViewBag.LookupTypeId2 = selectList2;
Also tried with int array as suggested:
List<SelectListItem> selectList2 = db.LookupType.Select(x => new SelectListItem() { Selected = x.LookupTypeId < 4, Value = x.LookupTypeId.ToString(), Text = x.Name }).ToList();
lookup.LookupTypeId2 = new int[]{1,2,3,4};
ViewBag.LookupTypeId2 = selectList2;
But still nothing is selected.
Firstly you need a property to bind to. A multiple select will only post back a collection of value types, but your trying to bind to List<SelectListItem>. Add a property (say) public int[] SelectedItems { get; set; } to your model and then use
#Html.ListBoxFor(m => m.SelectedItems, (IEnumerable<SelectListItem>)ViewBag.LookupTypeId2, htmlAttributes: new { #class = "form-control"})
Then if the values of SelectedItems matches any of the options, those options will be selected, for example if model.SelectedItems = new int[] { 2, 10 }; then the 2nd and last options will be selected when you display the page. You do not (and should not) set the Selected property of SelectListItem when your binding to a property.
I have an action that retrieves data and sends it to a view. In a view I have two dropdown menus.
First drop down shows salutation (such as "Mr.", "Ms.", etc.) and does not select value I sent for some reason. The other dropdown shows language list and correctly selects value I sent to the view. The relevant code in view is shown below.
#Html.DropDownListFor(model => model.Salutation, ViewBag.salutation as IEnumerable<SelectListItem>)
#Html.DropDownListFor(model => model.Language, ViewBag.languages as IEnumerable<SelectListItem>)
In the controller I have the following code to get the dropdown data.
ViewBag.salutation = new List<SelectListItem>() {
new SelectListItem() { Text = "", Value = "" },
new SelectListItem() { Text = "Mr.", Value = "Mr." },
new SelectListItem() { Text = "Ms.", Value = "Ms." },
new SelectListItem() { Text = "Mrs.", Value = "Mrs." }
};
and
var languages = (from l in db.Languages.ToList()
select new SelectListItem()
{
Text = l.Language,
Value = l.LanguageId.ToString()
}).ToList();
languages.Insert(0, new SelectListItem() { Text = "", Value = "" });
ViewBag.languages = languages;
The only difference I could think of is that the languages dropdown has an integer as value, whereas salutation dropdown has text as value. Is this why the salutation dropdown doesn't work? I know I could go through each salutation List<SelectListItem> item and set Selected property based on the value I retrieved from database. But I was hoping there would be a cleaner way to do this.
Any ideas?
Thanks
UPDATE
I decided to do what I did for another project.
IList<SelectListItem> _salutation = new List<SelectListItem>()
{
new SelectListItem() { Value = "", Text = "" },
new SelectListItem() { Value = "Mr.", Text = "Mr." },
new SelectListItem() { Value = "Ms.", Text = "Ms." },
new SelectListItem() { Value = "Mrs.", Text = "Mrs." }
};
// I could put the following in the declaration above, but for testing purposes it's in foreach loop.
foreach (var item in _salutation)
{
// compare to what's retrieved from database
item.Selected = item.Value == _viewData.Salutation;
}
ViewBag.salutation = _salutation;
After foreach loop I output .Value, .Selected property of each item in _salutation and I get all the correct values with one item being selected. Inside the view I did the following.
#foreach (var item in ViewBag.salutation as IEnumerable<SelectListItem>)
{
<b>#item.Value : #item.Text : #item.Selected</b><br />
}
All the correct Text/Values come up but none are Selected! This happens if I output the values after I execute #Html.DropDownListFor(). If I output the ViewBag.salutation before the html helper the correct value is selected.
SOLUTION
I found the following article useful: DropDownListFor with ASP.NET MVC.
Instead of using ViewBag I added the following to the ViewModel. (Showing the part for salutations drop down.)
public class TheViewModel
{
private IList<string> _salutations = new List<string>() { "", "Mr.", "Ms.", "Mrs." };
public IEnumerable<SelectListItem> SalutationItems
{
get
{
var salutations = _salutations.Select(s => new SelectListItem { Value = s, Text= s });
return salutations;
}
}
// The rest of the ViewModel
}
And in the View I have the following.
#Html.DropDownListFor(model => model.Salutation, Model.SalutationItems)
Instead of just supplying the list to the DropDownListFor helper you could provide it a SelectList. The SelectList constructor takes the list and allows you to explicitly set the selected value as well as an overload that lets you specify the Text and Value fields.
#Html.DropDownListFor(model => model.Salutation,
new SelectList(ViewBag.salutation as IEnumerable<SelectListItem>,
"Value", "Text", Model.Salutation))
Try this,
#Html.DropDownListFor(m =>m.DDCountryModel,IEnumerable<SelectListItem>)ViewBag.salutation)
#Html.DropDownListFor(model => model.Language, IEnumerable<SelectListItem>)ViewBag.languages)
Your Model should be like this,
public class Model
{
public IEnumerable<SelectListItem> DDCountryModel{ get; set; }
public IEnumerable<SelectListItem> Language{ get; set; }
}
I have one static dropdown list in my view
<select>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
I want to bind this drop down list with my model so that when I submit selected value of drop down , I get that value from model in my controller.
Also I want to select option in drop down as per data in my model.
I just made list item for static dropdown list & passed it to dropdown list where I binded it with my viewmodel.
#{
var listItems = new List<ListItem> {new ListItem {Text = "Single", Value = "Single"}, new ListItem {Text = "Married", Value = "Married"}, new ListItem {Text = "Divorse", Value = "Divorse"}};
}
#Html.DropDownListFor(model => model.EmployeeDetail.MaritalStatus, new SelectList(listItems),"-- Select Status --")
It works perfectly for me as it shows value which comes from model & also stores value of dropdown list in model when I submit data.
Instead of constructing the drop down list in HTML, build it in your service/controller and add it to your model:
ViewModel:
public class YourViewModel
{
public string SelectedCarManufacturer { get; set; }
public Dictionary<string, string> CarManufaturers { get; set; }
// your other model properties
}
Controller get action method
[HttpGet]
public ActionResult SomeAction()
{
var model = new YourViewModel
{
SelectedCarManufacturer = null, // you could get this value from your repository if you need an initial value
CarManufaturers = new Dictionary<string, string>
{
{ "volvo", "Volvo" },
{ "saab", "Saab" },
{ "audi", "Audi" },
/// etc.
}
};
return this.View(model);
}
In your view, replace the hard coded drop down list with:
#Html.DropDownListFor(m => m.SelectedCarManufacturer , new SelectList(Model.CarManufaturers , "Key", "Value"), "Select a manufacturer...")
Controller post action method
[HttpPost]
public ActionResult SomeSaveAction(YourViewModel model)
{
// do something with the model...
// model.SelectedCarManufacturer
}
OR
[HttpPost]
public ActionResult SomeSaveAction()
{
var model = someService.BuildYourViewModel()
this.TryUpdateModel(model);
// do something with the model...
someService.SaveYourViewModel(model);
}
I hope this helps...
in controller
List<SelectListItem> items = new List<SelectListItem>();
items.Add(new SelectListItem { Text = "Volvo", Value = "volvo"});
items.Add(new SelectListItem { Text = "Saab", Value = "saab" });
items.Add(new SelectListItem { Text = "Mercedes", Value = "mercedes" });
items.Add(new SelectListItem { Text = "Audi", Value = "audi" });
on view
Html.DropDownListFor(Model.items)
I have a list of Organizations (CalledOrganizationSelectList) and one of them is the one that's being called (CalledOrganizationId). I set both in my ViewModel but MVC / Razor doesn't render the dropdown correctly (ie does not set the selected="selected" attribute for the correct item in the dropdown).
There is a workaround which makes even less sense. I added a new member to my ViewModel and bound the dropdown to that. This works fine. Until just now when it's stopped working...
public class CallViewModel{
public int? CalledOrganizationId { get; set; }
public SelectList CalledOrganizationSelectList { get; set; }}
In my controller:
var vm = new CallViewModel();
var calledOrganizationSelectList = new List<object>();
calledOrganizationSelectList.Add( new {Text="",Id=""});
calledOrganizationSelectList.AddRange(
db.MyOrganizations.OrderBy(x=>x.Name)
.Select(x => new { Text = x.Name, Id = x.Id.ToString()}).ToList());
var sl = new SelectList(calledOrganizationSelectList, "Id", "Text",
vm.CalledOrganizationId);
vm.CalledOrganizationSelectList = sl;
return View(vm);
In my view:
<div>CalledOrganizationId = #Model.CalledOrganizationId :
select list contains
<ul>#foreach (var itm in Model.CalledOrganizationSelectList)
{<li>Value: #itm.Value Text: #itm.Text Is Selected: #itm.Selected</li>}
</ul>
</div>
#Html.DropDownList("CalledOrganizationId", Model.CalledOrganizationSelectList)
In my rendered page source:
<div>CalledOrganizationId = 38 : select list contains
<ul>
<li>Value: Text: Is Selected: False</li>
<li>Value: 37 Text: rrr Is Selected: False</li>
<li>Value: 38 Text: sss1 Is Selected: True</li>
</ul>
</div>
<select data-val="true" data-val-number="The field CalledOrganizationId must be a number." id="CalledOrganizationId" name="CalledOrganizationId">
<option selected="selected" value=""></option>
<option value="37">rrr</option>
<option value="38">sss1</option> </select>
I've worked around it after first pulling out what was left of my hair and then by introducing a new variable on my ViewModel, which seems to work ok.
Having 2 properties on your viewmodel is the correct way to do DropDownLists. One property holds all of the available options, and the other holds the currently selected option:
public class CallViewModel
{
// this captures the selected item
public int? CalledOrganizationId { get; set; }
// this contains the select list options
public IEnumerable<SelectListItem> CalledOrganizationSelectList { get; set; }
}
However, you do not need to add the empty option in the controller. You can do this in the view:
#Html.DropDownListFor(m => m.CalledOrganizationId,
Model.CalledOrganizationSelectList, "")
You can just get away with this in the controller:
var vm = new CallViewModel();
//var calledOrganizationSelectList = new List<object>();
//calledOrganizationSelectList.Add( new {Text="",Id=""});
//calledOrganizationSelectList.AddRange(
//db.MyOrganizations.OrderBy(x=>x.Name)
// .Select(x => new { Text = x.Name, Id = x.Id.ToString()}).ToList());
//var sl = new SelectList(calledOrganizationSelectList, "Id", "Text",
// vm.CalledOrganizationId);
//vm.CalledOrganizationSelectList = sl;
vm.CalledOrganizationSelectList = db.MyOrganizations.OrderBy(x => x.Name)
.Select(x => new SelectListItem
{
Text = x.Name,
//Id = x.Id.ToString() // do not use "Id" for the property name,
Value = x.Id.ToString() // use "Value" -- it is a SelectListItem property
});
// if you want to set the selected item on the dropdownlist, do it here
vm.CalledOrganizationId = 37;
return View(vm);
I am using a dropdown list as follows.
<%=Html.DropDownList("ddl", ViewData["Available"] as SelectList,
new { CssClass = "input-config", onchange = "this.form.submit();" })%>
On its selection change I am invoking post action. After the post the same page is shown on which this drop down is present. I want to know about the HTML attribute for the drop down which will let me preserve the list selection change. But as of now the list shows its first element after the post.
e.g. The dropdoen contains elements like 1,2,3,etc. By default 1 is selected. If I select 2, the post is invoked and the same page is shown again but my selection 2 goes and 1 is selected again.
How can preserve the selection?
Thanks,
Kapil
You have to make the list of select list items again and tell which of the items is the selected one in every post (Selected property of the SelectListItem).
When you perform the post you will be setting the ViewData["Available"] again, you can set the select item here. So when you create the drop down list in the html the selected item is already selected. So your code could look something like:
ViewData["Available"] = new SelectList( items, "dataValueField", "dataTextField", "selectedValue" );
You have to take the property model ddl, or receive it as a parameter in the action, such as:
public ActionResult Action(Model model, string ddl)
Then to create ViewData [" Available "], you have to pass it as selected value
public ActionResult Action(Model model, string ddl)
{
ViewData["Available"] = List<SelectListItem>
{
new SelectListItem { Text = "1", Value = "1", Selected = (ddl == "1") },
new SelectListItem { Text = "2", Value = "2", Selected = (ddl == "2") },
new SelectListItem { Text = "3", Value = "3", Selected = (ddl == "3") }
};
return View(model);
}
OR:
public ActionResult Action(Model model, string ddl)
{
var list = List<SelectListItem>
{
new SelectListItem { Text = "1", Value = "1" },
new SelectListItem { Text = "2", Value = "2" },
new SelectListItem { Text = "3", Value = "3" }
};
ViewData["ddl"] = new SelectList(list, "value", "text", ddl);
return View(model);
}
EDIT: See also this
This worked for me:
<%=Html.DropDownList("Ibus", ViewData["Ibus"] as SelectList, new { **#class** = "dASDropDown" })%>