HTML.DropDownList does not respect pre-selected value - asp.net-mvc

I want to use Html.DropDownList(string name, IEnumerable SelectList, Object htmlAttributes) to render a select list with a preselected value for me.
My select list is in the model object of my view, so I have been writting the following code:
<%= Html.DropDownList("aName", mySelectList, new { }) %>
Which will render the select list without the pre-selected value.
A workaround I have found is passing the SelectList as ViewData and doing the following:
In the controller:
ViewData["TimeZones"] = mySelectList;
In the view:
<%= Html.DropDownList("TimeZones", null, new { }) %>
This way the select list will be rendered with the preselected value, however, I don't want to be forced to pass my select list as view data. What am I doing wrong? Thank you in advance for your help.

You can simply do this (it works):
<%: Html.DropDownList("DropdownName", new SelectList(ListItems, "Value", "Text", selectedItem), htmlAttributes)%>
Let me know in case this does not work for you.

Autobinding the selected item
The other approach is to automatically bind the selected item, and passing the list of items as parameter to the DropDownList helper method.
In the controller you do exactly the same thing as before, just don’t set to true the Selected property of any item. Then you also have to put into the view model the value of the item you want to select.
var model = new IndexViewModel()
{
ListItems = items,
SelectedItem = 2
};
And finally in the view, you use the overload which accepts also the selectList parameter:
<%= Html.DropDownList("SelectedItem", Model.ListItems) %>
The only difference in the HTML rendered is that, with first approach, the name of the HTML select element is ListItems, with the second it is SelectedItem.
These approaches are fine if you are creating your own list, but they are not the optimal solution if you are receiving the list of options for an external method, for example from a DB repository.
Take a look at this link

I know this is an old post, but I ran into a similar issue in MVC5. The solution is simple, but I struggled with it for a while, so I thought I'd share.
VS auto-generates DropDownLists with this in the view:
#Html.DropDownList("EpisodeTypeId", null, htmlAttributes: new { #class = "form-control" })
In the controller, VS auto-generates the ViewBag code differently for a Create vs. and Edit.
Here's the code from a create:
ViewBag.EpisodeTypeId = new SelectList(db.EpisodeTypes, "Id", "Name");
And from an edit:
ViewBag.EpisodeTypeId = new SelectList(db.EpisodeTypes, "Id", "Name", episode.EpisodeTypeId);
That fourth argument is important for Edits, as you might expect. If you leave it out, the database value for that record will not be pre-selected in the DropDown.
The VS auto-generated code will be correct, but if you're adding in fields manually later, this is easy to miss.

Related

ASP.NET MVC multiselect DropDownFor/listBoxFor [duplicate]

I have read many articles about using MultiSelectList and have yet to understand what is going wrong with my DropDownListFor. I have a ListBoxFor with the same View, ViewModel and data that works fine. I want to use the DropDownListFor because of its optionLabel parameter that ListBoxFor doesn't have.
When the View is first loaded, both the DropDownListFor and the ListBoxFor show the multiple selected items.
When the Submit button is clicked, the selected items collection is posted back to the Controller action okay and the view is refreshed with the ListBoxFor still showing both selected items but the DropDownListFor is only showing one selected item.
The controller action is constructing the MultiSelectList like this:
vm.TasksFilterGroup.Assignees = new MultiSelectList(employees, "Id", "FullName", new string[] { "51b6f06a-e04d-4f98-88ef-cd0cfa8a2757", "51b6f06a-e04d-4f98-88ef-cd0cfa8a2769" });
The View code looks like this:
<div class="form-group">
<label>ListBoxFor</label>
#Html.ListBoxFor(m => m.TasksFilterGroup.SelectedAssignees, Model.TasksFilterGroup.Assignees, new { #class = "form-control", multiple = "multiple" })
</div>
<div class="form-group">
<label>DropDownListFor</label>
#Html.DropDownListFor(m => m.TasksFilterGroup.SelectedAssignees, Model.TasksFilterGroup.Assignees, new { #class = "form-control", multiple = "multiple" })
</div>
Why does the DropDownListFor lose the multiple selection after Submit but the ListBoxFor doesn't?
As the names of the methods imply, DropDownListFor() is for creating a <select> (to select 1 option) and ListBoxFor() is for creating a <select multiple> (to select multiple options). While both methods share a lot of common code, they do produce different results.
Adding the multiple="multiple" attribute changes the display, but it does not change the functionality of the code executed by these methods.
If you inspect the source code, you will note that all the overloads of DropDownListFor() ultimately call the private static MvcHtmlString DropDownListHelper() method, and similarly ListBoxFor() ultimately calls the private static MvcHtmlString ListBoxHelper() method.
Both these methods call the private static MvcHtmlString SelectInternal() method, but the difference is that DropDownListHelper() passes allowMultiple = false while the ListBoxHelper() passes allowMultiple = true.
Within the SelectInternal() method, the key line of code is
object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));
The value of defaultValue is then used when building html for the <option> elements and is used to set the selected attribute(s).
In the case of ListBoxFor(), the value of defaultValue will be the array defined by your SelectedAssignees property. In the case of DropDownListFor() it returns null because the value of your property cannot be cast to string (its an array).
Because defaultValue is null, none of the <option> elements have the selected attribute set and you lose model binding.
As a side note, if you were to set the values of SelectedAssignees in the GET method before you pass the model to the view, you will see that none of them are selected when using DropDownListFor() for the same reasons described above.
Note also that the code for generating the SelectList should just be
vm.TasksFilterGroup.Assignees = new SelectList(employees, "Id", "FullName" });
There is no point setting the 3rd parameter when using either the DropDownListFor() or ListBoxFor() methods because its the value of the property your binding to (SelectedAssignees) that determines which options are selected (the 3rd parameter is ignored by the methods). If you want the options matching those Guid values to be selected, then in the GET method, use
vm.TasksFilterGroup.SelectedAssignees= new string[]{ "51b6f06a-e04d-4f98-88ef-cd0cfa8a2757", "51b6f06a-e04d-4f98-88ef-cd0cfa8a2769" };

Why does the DropDownListFor lose the multiple selection after Submit but the ListBoxFor doesn't?

I have read many articles about using MultiSelectList and have yet to understand what is going wrong with my DropDownListFor. I have a ListBoxFor with the same View, ViewModel and data that works fine. I want to use the DropDownListFor because of its optionLabel parameter that ListBoxFor doesn't have.
When the View is first loaded, both the DropDownListFor and the ListBoxFor show the multiple selected items.
When the Submit button is clicked, the selected items collection is posted back to the Controller action okay and the view is refreshed with the ListBoxFor still showing both selected items but the DropDownListFor is only showing one selected item.
The controller action is constructing the MultiSelectList like this:
vm.TasksFilterGroup.Assignees = new MultiSelectList(employees, "Id", "FullName", new string[] { "51b6f06a-e04d-4f98-88ef-cd0cfa8a2757", "51b6f06a-e04d-4f98-88ef-cd0cfa8a2769" });
The View code looks like this:
<div class="form-group">
<label>ListBoxFor</label>
#Html.ListBoxFor(m => m.TasksFilterGroup.SelectedAssignees, Model.TasksFilterGroup.Assignees, new { #class = "form-control", multiple = "multiple" })
</div>
<div class="form-group">
<label>DropDownListFor</label>
#Html.DropDownListFor(m => m.TasksFilterGroup.SelectedAssignees, Model.TasksFilterGroup.Assignees, new { #class = "form-control", multiple = "multiple" })
</div>
Why does the DropDownListFor lose the multiple selection after Submit but the ListBoxFor doesn't?
As the names of the methods imply, DropDownListFor() is for creating a <select> (to select 1 option) and ListBoxFor() is for creating a <select multiple> (to select multiple options). While both methods share a lot of common code, they do produce different results.
Adding the multiple="multiple" attribute changes the display, but it does not change the functionality of the code executed by these methods.
If you inspect the source code, you will note that all the overloads of DropDownListFor() ultimately call the private static MvcHtmlString DropDownListHelper() method, and similarly ListBoxFor() ultimately calls the private static MvcHtmlString ListBoxHelper() method.
Both these methods call the private static MvcHtmlString SelectInternal() method, but the difference is that DropDownListHelper() passes allowMultiple = false while the ListBoxHelper() passes allowMultiple = true.
Within the SelectInternal() method, the key line of code is
object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));
The value of defaultValue is then used when building html for the <option> elements and is used to set the selected attribute(s).
In the case of ListBoxFor(), the value of defaultValue will be the array defined by your SelectedAssignees property. In the case of DropDownListFor() it returns null because the value of your property cannot be cast to string (its an array).
Because defaultValue is null, none of the <option> elements have the selected attribute set and you lose model binding.
As a side note, if you were to set the values of SelectedAssignees in the GET method before you pass the model to the view, you will see that none of them are selected when using DropDownListFor() for the same reasons described above.
Note also that the code for generating the SelectList should just be
vm.TasksFilterGroup.Assignees = new SelectList(employees, "Id", "FullName" });
There is no point setting the 3rd parameter when using either the DropDownListFor() or ListBoxFor() methods because its the value of the property your binding to (SelectedAssignees) that determines which options are selected (the 3rd parameter is ignored by the methods). If you want the options matching those Guid values to be selected, then in the GET method, use
vm.TasksFilterGroup.SelectedAssignees= new string[]{ "51b6f06a-e04d-4f98-88ef-cd0cfa8a2757", "51b6f06a-e04d-4f98-88ef-cd0cfa8a2769" };

Where does Html.DropDownList return the selected value to?

I am trying to use a dropdownlist in my mvc application, but am very confused by how it operates. It appears that you pass the list that appears in the dropdown, and a temporary description, but never tell it where you would like the selected value stored. Am I missing something or how do I retrieve a value from my drop-down list?
My drop-down list:
#Html.DropDownList("EmployeeNames", null, "Select an Employee", htmlAttributes: new { #class = "form-control" })
My current drop-down list appears correctly but I am unable to retain the selected value.
You tell where the value to be stored, the property EmployeeNames from the current Model.
You can take a look at MVC Model Binding. The first parameter of DropDownList is the name. The MVC bind the value by the form elements name.
The following
#Html.DropDownList("EmployeeNames", null, "Select an Employee", htmlAttributes: new { #class = "form-control" })
In HTML will look similar to this
<select id="EmployeeNames" name="EmployeeNames">
<!-- options -->
</select>
You notice name="EmployeeNames", that will help to bind to the property EmployeeNames from the curent Model.
Actually, it's pretty straightforward.. just grab the variable on the method call which handle the particular form action in your controller, if the name of your DropDownList is EmployeeNames , then your method should look like this
public ActionResult handleForm(string employeeNames)
{
return View();
}

How do you set the initial value of a EnumDropDownListFor built using helper extension?

I followed a tutorial for MVC5 about coding a helper extension to populate dropdownlists using enums. This works perfectly until using an edit view.
On a create page I fill the ddl and select no problem. But when I want to update the value on an edit view, using a ddl filled just as on the create, I can't default the actual value (from the database) as the displayed value.
I've searched high and low but starting to think it can't be done. I'm not sure what code will help so here goes;
My ddl on the edit view
#Html.EditorFor(model => model.ProjectStatus)
ProjectStatus is the enum that is used to retrieve the enum value in the helper extension and populate the ddl.
So my ddl may contain statuses such as:
New,
WIP,
Rejected,
Fixed,
Closed
and my current value may = New. So when I load the edit view I would like the value New to be defaulted in to the ddl, and be able to change this value by selecting another using the ddl.
The value I would like to default to is actually in the view (model.Status). I just can't work out how to include this in the code above.
I hope this makes sense and any help appreciated.
Thanks for reading.
I'm not exactly sure what your extension method looks like. If you want a more direct answer, you'll need to include more details on your specific implementation. That being said, here is some sample code from an edit.cshtml template on Autoquoter.com
<div class="control-group">
#Html.LabelFor(model => model.AddonType)
<div class="controls">
#Html.DropDownListFor(model => model.AddonType, EnumHelper.SelectListFor((AddonType)Model.AddonType))
#Html.ValidationMessageFor(model => model.AddonType)
</div>
</div>
Notice the parameter to EnumHelper.SelectListFor. We pass in the current value to the helper method. Then we add it as the final parameter to the SelectList constructor.
public static SelectList SelectListFor<T>(T selected) where T : struct
{
Type t = typeof(T);
return !t.IsEnum ? null
: new SelectList(BuildSelectListItems<T>(), "Value", "Text", selected.ToString());
}
For anyone that may be interested I fixed this using jquery.
I was getting hung up on drop downs filled by enums, razor and html helpers etc. I looked at the raw html produced and approached it from that angle.
The following code works for me but I'm no expert so there may well be better solutions
// set initial value of Status ddl
$(document).ready(function () {
var initialValue = $("##Html.IdFor(model => model.Status)").val();
$("select option").filter(function () {
return $(this).text() == initialValue;
}).prop("selected", true);
});
I hope this helps.

Populating a dropdown from ViewData

I have viewdata in my controller which is populated by a list:
List<employee> tempEmpList = new List<employee>();
tempEmpList = context.employees.ToList();
ViewData["tempEmpList"] = tempEmpList;
and I am passing this into my view, the question is, how do I place the content of the viewdata list into a dropdown list?
The display data will be .name from the list item.
I know I could do a foreach on the Viewdata and create a select list, but this seems a bit long winded
You can use the DropDownList html helper:
#Html.DropDownList("SelectedEmployee",
new SelectList((IEnumerable) ViewData["tempEmpList"]), "Id", "Name")
In the SelectList constructor, you can specify which properties of the Employee class should be used as both the text and the value within the dropdown (e.g. "Id", "Name")
The name of the dropdown ("SelectedEmployee") will be used when you post back your data to the server.
Set up your ViewData in the normal way, assigning a Key name that maps to a property in your model that will be bound on Post ...
ViewData["ModelPropertyName"] = new SelectList(...)
Then in your view simply add a Html.DropDownList ...
#Html.DropDownList("ModelPropertyName")
Please try with that. I have tried with MVC5
#Html.DropDownList("SelectedEmployee", new SelectList((System.Collections.IEnumerable) ViewData["tempEmpList"],"id","Name"))

Resources