MenuType definiation:
public string Code { get; set; }
public string Name { get; set; }
ASP.NET MVC 5
I have searched and read before I'm posting my question here,
I'm trying to LOAD the data in the asp.net mvc dropdownlist why is that so complicated?
//controller
public class ClientController : BaseController
{
public ActionResult Index()
{
List<MenuType> ctypelist = db.ContractTypes.OrderBy(x => x.TypeOfContract).ToList();
IEnumerable<SelectListItem> list = new SelectList(ctypelist.ToList());
ViewBag.DropDownTypeOfContract = list;
return View();
}
}
//html
#model myapp.Models.Client
#Html.DropDownList("Codes", (List<SelectListItem>)ViewBag.DropDownTypeOfContract , new { style = "max-width: 600px;" })%>
What's complicated is that you can't seem to decide which type you're using...
First you have a List<MenuType> (I assume ContractTypes is actually of type MenuType?) Then you create a SelectList, passing the List<MenuType> to it, which implies that MenuType must have at least two properties, one called Text and one called Value. If not, you will have to specify the Text and Value property names in the SelectList constructor parameters.
After that, for some reason you convert it to a IEnumerable<SelectListItem>, then you assign that to a ViewBag item and call your View. So, at this point, your ViewBag.DropDownTypeOfContract is of type IEnumerable<SelectListItem>.
Next, in your View, you for some reason define an #model depite not passing any model at all to the view. Ok.... Whatever...
So now we get to the real problem.
#Html.DropDownList("Codes",
(List<SelectListItem>)ViewBag.DropDownTypeOfContract ,
new { style = "max-width: 600px;" })%>
Ok, let's ignore for a moment the fact that you have a WebForms closing code block indicator (%>) for some reason... The biggest problem here is that you're trying to cast ViewBag.DropDownTypeOfContract to a List<SelectListItem>, which is something it is not, and never was.
You converted the List<MenuType> to a SelectList which you then converted to an IEnumerable<SelectListItem>. There was never any List<SelectListItem> involved.
So, the simple fix (besides rewriting your code to be sane) is to change your cast as such:
#Html.DropDownList("Codes",
(IEnumerable<SelectListItem>)ViewBag.DropDownTypeOfContract,
new { style = "max-width: 600px;" })
EDIT:
Since your MenuType does not contain the appropriate properties, you will have to modify your SelectList as such (Which I mention above). FYI, ctypelist is already a list, no need to convert it to a list again... that's just silly.
IEnumerable<SelectListItem> list = new SelectList(ctypelist, "Code", "Name");
Note: I have posted this answer without knowledge of what variables your MenuType Class has. Please add to your question and I will edit this answer according to youe MenuType Class
All Dropdowns are a collection of Value and Text Pairs.
<select>
<option value=1>TEXT 1</option>
<option value=2>TEXT 2</option>
<option value=3>TEXT 3</option>
</select>
You have a list of List<MenuType>, Which values from the MenuType do you want to display in the DropDown List?
Assuming you have this as MenuType.cs
public class MenuType
{
public int MenuTypeId {get;set;}
public string Name {get;set;}
}
Your dropDown should be generated like this:
public ActionResult Index()
{
Dictionary<int,string> ctypelist = db.ContractTypes.OrderBy(x => x.TypeOfContract).ToDictionary(s => s.MenuTypeId, s=> s.Name);
IEnumerable<SelectListItem> selectListItems = ctypelist.Select(s => new SelectListItem() { Value = s.Key.ToString(), Text = s.Value });
ViewBag.DropDownTypeOfContract = selectListItems;
return View();
}
In View:
#{
var items = (IEnumerable<SelectListItem>) ViewBag.DropDownTypeOfContract;
}
#Html.DropDownList("Codes", items , "Select Item")
public class DropDown
{
public string[] SelectedItems { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
Am trying to get a DropDown in MVC from DB with above structure.
From DataBase I got the Text and value field and having in IEnumerable<T> where T has 2 properties id and text.
What is the best way convert ienumerable id to array of string and assign id, text to SelectedListItem ?
I thought of looping through the ienumerable and forming the DropDown, but thought there will be better ways.
update
For example :
In DB i have student table with
ID,
Name,
Class,
Section
And i got id and Name in ienumerable<student> . From there i need to convert into a DropDown
IEnumerable<Student> studentList = GetStudentList();
IEnumerable<SelectListItem> dropdownItems = new MultiSelectList(studentList, "ID", "Name",selectedValues);
DropDown dropDown = new DropDown { Items = dropdownItems };
If you want to create a listbox you can do it with HTML helpers like this:
#Html.ListBoxFor(item => item.SelectedStudents, new MultiSelectList(Model.StudentList, "ID", "Name",selectedValues))
I'm unsure why you are trying to create your own DropDown class, why not use the built-in MVC HTML Helper?
If you have an IEnumerable that you want to turn into a SelectList which will be consumed by the helper, something like this would work:
var selectListItems = from t in items
select new SelectListItem{
Text = t.Text,
Value = t.Id
}.ToList();
Then fire it over to your view, you could ideally use a viewmodel, but the ViewBag works too:
ViewBag.SelectListItems = selectListItems;
Finally, have your HTML Helper build your drop down for you in your view:
#Html.DropDownList("SelectListItems")
It looks like maybe you want multiple dropdowns for every array position in selectedItems? If so, I just had this problem last week. I called a foreach inside Html.DropDownListFor(...).
<% for(int i = 0; i < Model.SelectedItems.length; i++) %>
<%: Html.DropDownListFor(m => m.SelectedItems[i], Model.Items %>
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
I am trying to create a drop down list using asp.net mvc.
Model:
public string Status { get; set; }
public List<SelectListItem> StatusList { get; set; }
public AddUser()
{
StatusList = new List<SelectListItem>
{
new SelectListItem{Value = "0",Text = "0"},
new SelectListItem{Value = "1",Text = "1"}
};
}
View:
<%: Html.DropDownListFor(m=>m.Status,new SelectList(Model.StatusList,"Value","Text")) %>
<%: Html.ValidationMessageFor(m => m.Status) %>
I don't know why but I keep getting this error:
Object reference not set to an instance of an object.
Anyone knows what I am doing wrong?
This error usually happens when you leave a field of model null or the model is null
Make sure that "Status" isn't null and your return the model in "View" just like this:
//end of an action code
AddUser model = new AddUser();
model.Status = "0"; //status can't be null (cause the the exception cited earlier)
return View(model); //if you do not pass the model as argument the model will be null in view
}
And one more tip for u:
The class SelectList implements IEnumerable<SelectListItem> so in view your code may look something like this:
<%: Html.DropDownListFor(m=>m.Status, Model.StatusList) %>
<%: Html.ValidationMessageFor(m => m.Status) %>
You don't need instantiate again.
I think Travis is right, you've forgot install StatusList, maybe it's null.
Like this poster, I'm a bit confused by ASP.NET MVC Html.ListBoxFor(...). Specifically I'm putting the selection results in a List but after I post the results I'm getting
InvalidOperationException: The ViewData item that has the key 'SelectedDeclarations' is of type 'System.String[]' but must be of type 'IEnumerable<SelectListItem>'
Here is my abbreviated ViewModel that I'm passing to the strongly-typed razor view
public MyViewModel
{
public MyViewModel()
{
(...)
this.VendorsRequiringDeclaration = new List<SelectListItem>();
this.SelectedDeclarations = new List<String>();
}
public IEnumerable<String> SelectedDeclarations { get; set; }
public List<SelectListItem> VendorsRequiringDeclaration { get; set; }
}
and here is the view code that references them
#Html.ListBoxFor(m=>m.SelectedDeclarations, Model.VendorsRequiringDeclaration, new { #class="editor-field", #size=6})
If I change MyViewModel such that SelectedDeclarations is a List of SelectedListItem rather than a List of String, upon post to the appropriate controller action it thinks my model is invalid:
{"The parameter conversion from type 'System.String' to type 'System.Web.Mvc.SelectListItem' failed because no type converter can convert between these types."}
Ideas? I probably have the wrong LINQ expression for the first parameter, but I can't see it from the similar questions. Thanks in advance!
In the case of ModelState is not valid, you need to reset the ViewData object inside the controller.
Since data inside VendorsRequiringDeclaration is not saved anywhere.
As it turns out the problem was SQL permissions on an underlying data table that I am accessing via EF; the post failed and made it appear to be the Html Helper -- apologies for any confusion!
Based on AlexanderB's suggestion though I did rework the Html.ListBoxFor(...) thusly, and it seems to work fine:
#Html.ListBoxFor(m=>m.SelectedDeclarations,
new MultiSelectList(
Model.VendorsRequiringDeclaration,
"Id",
"VendorName",
Model.VendorsRequiringDeclaration.Select(
x => new SelectListItem()
{
Selected = false,
Text = x.VendorName,
Value = x.Id.ToString()
}).ToList()),
new { #class = "editor-field", #size = 6 } )
I have tried this is RC1 and then upgraded to RC2 which did not resolve the issue.
// in my controller
ViewData["UserId"] = new SelectList(
users,
"UserId",
"DisplayName",
selectedUserId.Value); // this has a value
result: the SelectedValue property is set on the object
// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["UserId"])%>
result: all expected options are rendered to the client, but the selected attribute is not set. The item in SelectedValue exists within the list, but the first item in the list is always defaulted to selected.
How should I be doing this?
Update
Thanks to John Feminella's reply I found out what the issue is. "UserId" is a property in the Model my View is strongly typed to. When Html.DropDownList("UserId" is changed to any other name but "UserId", the selected value is rendered correctly.
This results in the value not being bound to the model though.
This is how I fixed this problem:
I had the following:
Controller:
ViewData["DealerTypes"] = Helper.SetSelectedValue(listOfValues, selectedValue) ;
View
<%=Html.DropDownList("DealerTypes", ViewData["DealerTypes"] as SelectList)%>
Changed by the following:
View
<%=Html.DropDownList("DealerTypesDD", ViewData["DealerTypes"] as SelectList)%>
It appears that the DropDown must not have the same name has the ViewData name :S weird but it worked.
Try this:
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
And then:
var list = new[] {
new Person { Id = 1, Name = "Name1" },
new Person { Id = 2, Name = "Name2" },
new Person { Id = 3, Name = "Name3" }
};
var selectList = new SelectList(list, "Id", "Name", 2);
ViewData["People"] = selectList;
Html.DropDownList("PeopleClass", (SelectList)ViewData["People"])
With MVC RC2, I get:
<select id="PeopleClass" name="PeopleClass">
<option value="1">Name1</option>
<option selected="selected" value="2">Name2</option>
<option value="3">Name3</option>
</select>
You can still name the DropDown as "UserId" and still have model binding working correctly for you.
The only requirement for this to work is that the ViewData key that contains the SelectList does not have the same name as the Model property that you want to bind. In your specific case this would be:
// in my controller
ViewData["Users"] = new SelectList(
users,
"UserId",
"DisplayName",
selectedUserId.Value); // this has a value
// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["Users"])%>
This will produce a select element that is named UserId, which has the same name as the UserId property in your model and therefore the model binder will set it with the value selected in the html's select element generated by the Html.DropDownList helper.
I'm not sure why that particular Html.DropDownList constructor won't select the value specified in the SelectList when you put the select list in the ViewData with a key equal to the property name. I suspect it has something to do with how the DropDownList helper is used in other scenarios, where the convention is that you do have a SelectList in the ViewData with the same name as the property in your model. This will work correctly:
// in my controller
ViewData["UserId"] = new SelectList(
users,
"UserId",
"DisplayName",
selectedUserId.Value); // this has a value
// in my view
<%=Html.DropDownList("UserId")%>
The code in the previous MVC 3 post does not work but it is a good start. I will fix it. I have tested this code and it works in MVC 3 Razor C# This code uses the ViewModel pattern to populate a property that returns a List<SelectListItem>.
The Model class
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
The ViewModel class
using System.Web.Mvc;
public class ProductListviewModel
{
public List<SelectListItem> Products { get; set; }
}
The Controller Method
public ViewResult List()
{
var productList = new List<SelectListItem>();
foreach (Product p in Products)
{
productList.Add(new SelectListItem
{
Value = p.ProductId.ToString(),
Text = "Product: " + p.Name + " " + p.Price.ToString(),
// To set the selected item use the following code
// Note: you should not set every item to selected
Selected = true
});
}
ProductListViewModel productListVM = new ProductListViewModeld();
productListVM.Products = productList;
return View(productListVM);
}
The view
#model MvcApp.ViewModels.ProductListViewModel
#using (Html.BeginForm())
{
#Html.DropDownList("Products", Model.Products)
}
The HTML output will be something like
<select id="Products" name="Products">
<option value="3">Product: Widget 10.00</option>
<option value="4">Product: Gadget 5.95</option>
</select>
depending on how you format the output. I hope this helps. The code does work.
If we don't think this is a bug the team should fix, at lease MSDN should improve the document. The confusing really comes from the poor document of this. In MSDN, it explains the parameters name as,
Type: System.String
The name of the form field to return.
This just means the final html it generates will use that parameter as the name of the select input. But, it actually means more than that.
I guess the designer assumes that user will use a view model to display the dropdownlist, also will use post back to the same view model. But in a lot cases, we don't really follow that assumption.
Use the example above,
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
If we follow the assumption,we should define a view model for this dropdownlist related view
public class PersonsSelectViewModel{
public string SelectedPersonId,
public List<SelectListItem> Persons;
}
Because when post back, only the selected value will post back, so it assume it should post back to the model's property SelectedPersonId, which means Html.DropDownList's first parameter name should be 'SelectedPersonId'. So, the designer thinks that when display the model view in the view, the model's property SelectedPersonId should hold the default value of that dropdown list. Even thought your List<SelectListItem> Persons already set the Selected flag to indicate which one is selected/default, the tml.DropDownList will actually ignore that and rebuild it's own IEnumerable<SelectListItem> and set the default/selected item based on the name.
Here is the code from asp.net mvc
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
{
...
bool usedViewData = false;
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = htmlHelper.GetSelectData(name);
usedViewData = true;
}
object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string));
// If we haven't already used ViewData to get the entire list of items then we need to
// use the ViewData-supplied value before using the parameter-supplied value.
if (defaultValue == null && !String.IsNullOrEmpty(name))
{
if (!usedViewData)
{
defaultValue = htmlHelper.ViewData.Eval(name);
}
else if (metadata != null)
{
defaultValue = metadata.Model;
}
}
if (defaultValue != null)
{
selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}
...
return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
}
So, the code actually went further, it not only try to look up the name in the model, but also in the viewdata, as soon as it finds one, it will rebuild the selectList and ignore your original Selected.
The problem is, in a lot of cases, we don't really use it that way. we just want to throw in a selectList with one/multiple item(s) Selected set true.
Of course the solution is simple, use a name that not in the model nor in the viewdata. When it can not find a match, it will use the original selectList and the original Selected will take affect.
But i still think mvc should improve it by add one more condition
if ((defaultValue != null) && (!selectList.Any(i=>i.Selected)))
{
selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}
Because, if the original selectList has already had one Selected, why would you ignore that?
Just my thoughts.
This appears to be a bug in the SelectExtensions class as it will only check the ViewData rather than the model for the selected item. So the trick is to copy the selected item from the model into the ViewData collection under the name of the property.
This is taken from the answer I gave on the MVC forums, I also have a more complete answer in a blog post that uses Kazi's DropDownList attribute...
Given a model
public class ArticleType
{
public Guid Id { get; set; }
public string Description { get; set; }
}
public class Article
{
public Guid Id { get; set; }
public string Name { get; set; }
public ArticleType { get; set; }
}
and a basic view model of
public class ArticleModel
{
public Guid Id { get; set; }
public string Name { get; set; }
[UIHint("DropDownList")]
public Guid ArticleType { get; set; }
}
Then we write a DropDownList editor template as follows..
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script runat="server">
IEnumerable<SelectListItem> GetSelectList()
{
var metaData = ViewData.ModelMetadata;
if (metaData == null)
{
return null;
}
var selected = Model is SelectListItem ? ((SelectListItem) Model).Value : Model.ToString();
ViewData[metaData.PropertyName] = selected;
var key = metaData.PropertyName + "List";
return (IEnumerable<SelectListItem>)ViewData[key];
}
</script>
<%= Html.DropDownList(null, GetSelectList()) %>
This will also work if you change ArticleType in the view model to a SelectListItem, though you do have to implement a type converter as per Kazi's blog and register it to force the binder to treat this as a simple type.
In your controller we then have...
public ArticleController
{
...
public ActionResult Edit(int id)
{
var entity = repository.FindOne<Article>(id);
var model = builder.Convert<ArticleModel>(entity);
var types = repository.FindAll<ArticleTypes>();
ViewData["ArticleTypeList"] = builder.Convert<SelectListItem>(types);
return VIew(model);
}
...
}
The problems is that dropboxes don't work the same as listboxes, at least the way ASP.NET MVC2 design expects: A dropbox allows only zero or one values, as listboxes can have a multiple value selection. So, being strict with HTML, that value shouldn't be in the option list as "selected" flag, but in the input itself.
See the following example:
<select id="combo" name="combo" value="id2">
<option value="id1">This is option 1</option>
<option value="id2" selected="selected">This is option 2</option>
<option value="id3">This is option 3</option>
</select>
<select id="listbox" name="listbox" multiple>
<option value="id1">This is option 1</option>
<option value="id2" selected="selected">This is option 2</option>
<option value="id3">This is option 3</option>
</select>
The combo has the option selected, but also has its value attribute set. So, if you want ASP.NET MVC2 to render a dropbox and also have a specific value selected (i.e., default values, etc.), you should give it a value in the rendering, like this:
// in my view
<%=Html.DropDownList("UserId", selectListItems /* (SelectList)ViewData["UserId"]*/, new { #Value = selectedUser.Id } /* Your selected value as an additional HTML attribute */)%>
In ASP.NET MVC 3 you can simply add your list to ViewData...
var options = new List<SelectListItem>();
options.Add(new SelectListItem { Value = "1", Text = "1" });
options.Add(new SelectListItem { Value = "2", Text = "2" });
options.Add(new SelectListItem { Value = "3", Text = "3", Selected = true });
ViewData["options"] = options;
...and then reference it by name in your razor view...
#Html.DropDownList("options")
You don't have to manually "use" the list in the DropDownList call. Doing it this way correctly set the selected value for me too.
Disclaimer:
Haven't tried this with the web forms view engine, but it should work too.
I haven't tested this in the v1 and v2, but it might work.
I managed to get the desired result, but with a slightly different approach. In the Dropdownlist i used the Model and then referenced it. Not sure if this was what you were looking for.
#Html.DropDownList("Example", new SelectList(Model.FeeStructures, "Id", "NameOfFeeStructure", Model.Matters.FeeStructures))
Model.Matters.FeeStructures in above is my id, which could be your value of the item that should be selected.