DropDownListFor selection not working -- again - asp.net-mvc

Yeah, I know, this question's been asked/answered 34798796873.5 times. I looked through all 3 bajillion of them, and I still have the problem. What am I missing here?
I tried several approaches and none of them work. Here are my latest attempts:
<%:Html.DropDownList("Author",
Model.AuthorItems.Select(i =>
new SelectListItem
{
Text = i.Name,
Value = i.Id.ToString(),
Selected = i.Id == Model.Author.Id
}), "無し")%>
<%:Html.DropDownListFor(m => m.Author,
new SelectList(Model.AuthorItems,
"Id",
"Name",
Model.Author),
"無し") %>
My view model is very straightforward:
public class EditArticleViewModel
{
public AuthorItem Author { get; set; }
public IList<AuthorItem> AuthorItems { get; set; }
public class AuthorItem
{
public int Id { get; set; }
public string Name { get; set; }
}
}
I made sure my action is working correctly; sure enough, Author has an Id of 5, and AuthorItems has an entry whose Id is 5.
I even tried overriding Equals and GetHashCode in the model.
Blahhhhh!!1

In your view model replace:
public AuthorItem Author { get; set; }
with
public int? SelectedAuthorId { get; set; }
and in your view bind the dropdown list to this SelectedAuthorId:
<%:Html.DropDownListFor(
m => m.SelectedAuthorId,
new SelectList(Model.AuthorItems, "Id", "Name"),
"無し"
) %>
Now as long as you provide a valid SelectedAuthorId value in your controller action:
model.SelectedAuthorId = 123;
The HTML helper that renders the dropdown will correctly preselect the item from the list that has this given id.
The reason for this is that in a dropdown list you can select only a single value (a scalar type) and not an entire Author (all that is sent in the HTTP request when you submit the form is this selected value).

DropDownList/DropDownListFor used ModelState value. So set ViewDataDictionary selectedValue in controller.
public ActionResult Index()
{
var model = new EditArticleViewModel
{
Author = new EditArticleViewModel.AuthorItem() {Id = 3, Name = "CCC"},
AuthorItems = new List<EditArticleViewModel.AuthorItem>()
{
new EditArticleViewModel.AuthorItem() {Id = 1, Name = "AAA"},
new EditArticleViewModel.AuthorItem() {Id = 2, Name = "BBB"},
new EditArticleViewModel.AuthorItem() {Id = 3, Name = "CCC"},
new EditArticleViewModel.AuthorItem() {Id = 4, Name = "DDD"},
}
};
ViewData["Author"] = model.Author.Id;
return View(model);
}
View code simple.
<%:Html.DropDownList("Author",
new SelectList(Model.AuthorItems,
"Id",
"Name"), "無し")%>
<%:Html.DropDownListFor(m => m.Author,
new SelectList(Model.AuthorItems,
"Id",
"Name"),
"無し")%>
ViewData key "Author" is using model state binding for selected value.
Hope this help.

Related

Dropdownlistfor returns null value

I have a customer class with a property Gender. I created a list of Gender type which contains and id number and Gender type. When form is submitted, I am getting null value.
View
#model MovieRentals.ViewModel.CustomerView
<div class="form-group">
<h4>#Html.LabelFor(l => l.Customer.BirthDate)</h4>
#Html.DropDownListFor(l => l.CustomerGender, new SelectList(Model.CustomerGender, "GenderId", "GenderType"), "Select Gender", new { #class = "form-control" })
</div>
Model
public class CustomerView
{
public IEnumerable<MembershipType> MembershipTypes{ get; set; }
public Customer Customer { get; set; }
public List<GenderClass> CustomerGender{ get; set; }
}
public class GenderClass
{
public int GenderId { get; set; }
public string GenderType { get; set; }
}
Controller
public ActionResult New()
{
var MembershipTy = _context.MemebershipType.ToList();
var ViewModel = new CustomerView();
ViewModel.CustomerGender = new List<GenderClass>()
{
new GenderClass(){ GenderId = 1, GenderType = "Male"},
new GenderClass() { GenderId = 2, GenderType = "Female"}
};
ViewModel.MembershipTypes = MembershipTy;
return View(ViewModel);
}
You need two properties: one to hold the selected value and one to hold the options. The one that holds the options should be IEnumerable<SelectListItem>. Your GenderClass class is completely superfluous.
Also, using an integer id as the value doesn't make sense when the meaning of that value is not obvious. Here, the fact that 1 means Male only exists in the New action. Anywhere else, you will then have to repeat this logic (which introduces opportunities for errors, e.g. was male 1 or 0). Further, if you decide to change those values, you must remember to change them everywhere. If you want to use an integer id, then you should abstract away the meaning somewhere, be it an enum, static class, database table, etc. The far better choice is to just keep it a string, and use the dropdown merely to enforce normalization of that string value.
public string CustomerGender { get; set; }
public IEnumerable<SelectListItem> CustomerGenderChoices
{
get
{
return new List<SelectListItem>
{
new SelectListItem { Value = "Male", Text = "Male" },
new SelectListItem { Value = "Female", Text = "Female" }
}
}
}
Then, in your view:
#Html.DropDownListFor(m => m.CustomerGender, Model.CustomerGenderChoices, "Select Gender", new { #class = "form-control" })
Alternatively, if you were to use an enum:
public enum Genders
{
Male = 1,
Female = 2
}
Then, in your view model, you would only need one property, just to store the value:
public Genders CustomerGender { get; set; }
Then, in your view, you can make use of EnumDropDownListFor:
#Html.EnumDropDownListFor(m => m.CustomerGender, "Select Gender", new { #class = "form-control" })
As an enum, the value stored would be an int, but the benefit here is that you have a strongly-typed association between those integer values and what they mean. For example, rather than doing something like:
if (customer.CustomerGender == 1) // Male
You can do:
if (customer.CustomerGender == Genders.Male)
Obviously, the second version is much more obvious in meaning.

ASP.Net MVC view model for dropdownlist?

I am trying to work with a generated Month dropdownlist in MVC.
My viewmodel is:
public class MyViewModel{
public MyViewModel()
{
var monthNames = DateTimeFormatInfo.CurrentInfo.MonthNames.Take(12).ToList();
Months = new SelectList(monthNames.Select(m=> new{Id=monthNames.IndexOf(m)+1, Name=m}).ToList(),"Id","Name");
}
public IEnumerable<SelectListItem> Months{ get; set; }
public string Month{ get; set; }
}
My View is:
#Html.DropDownListFor(model=>model.Month, new SelectList(Model.Months))
The problem is that the Months property always returns a null value so the page errors when trying to render the DDL.
Seems pretty simple. What am I missing?
You're missing the part where you actually set the Months property to something other than null.
You should just define a custom getter on the property so it always returns an enumerable:
public IEnumerable<SelectListItem> Months
{
List<string> monthNames = DateTimeFormatInfo.CurrentInfo.MonthNames.Take(12).ToList();
foreach (var month in monthNames)
{
yield return new SelectListItem
{
Value = monthNames.IndexOf(month) + 1,
Text = month
};
}
}
As another possible solution using templates:
// in your model, decorate it to use the template
[UIHint("MonthName")]
public String Month { get; set; }
Then in ~/Views/Shared/EditorTemplates/MonthName.cshtml:
#model String
#Html.DropDown(
String.Empty,
#Model,
new SelectList(
System.Globalization.DateTimeFormatInfo.CurrentInfo.MonthNames
.Where(x => !String.IsNullOrEmpty(x))
.Select((x,y) => new { Text = x, Value = y + 1 }),
"Value",
"Text"
)
)
And finally, in your view:
#Html.EditorFor(x => x.Month)
though this is really only worth while on fixed-lists (like months), and not for things that may be dynamic based on the view being displayed.

Drop down list value returns to ---select--- after page refresh

So in my application the user will select a name from the drop down list, click 'view' and the corresponding values will display on page.
A hyperlink is then used to sort the list in ascending order. For this to happen the page refreshes and displays the new order of the list.
The value of the drop down list returns back to its original value of 'select' instead of remaining the name of the person selected.
My Model:
public class HolidayList
{
public List<Holiday> HList4DD { get; set; }
public List<Person> PList4DD { get; set; }
public int currentPersonID { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
public HolidayList()
{
HList4DD = new List<Holiday>();
PList4DD = new List<Person>();
}
}
}
my controller:
[HttpPost]
public ViewResult Index(int HolidayDate)
{
var holidays = db.Holidays.Include("Person");
HolidayList model = new HolidayList();
model.currentPersonID = HolidayDate;
model.PList4DD = db.People.ToList();
model.Categories = holidays.Select(x => new SelectListItem
{
Value = x.Id.ToString(),
Text = x.Person.Name
}
);
int data = HolidayDate;
model.HList4DD = db.Holidays.Where(h => h.PersonId == HolidayDate).ToList();
return View(model);
}
[HttpGet]
public ViewResult Index(string sortOrder, int? currentPersonID)
{
var holidays = db.Holidays.Include("Person");
HolidayList model = new HolidayList();
//not null
if (currentPersonID.HasValue)
{
model.currentPersonID = currentPersonID.Value;
}
else
{
model.currentPersonID = 0;
}
model.PList4DD = db.People.ToList();
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "date" : "";
var dates = from d in db.Holidays
where d.PersonId == currentPersonID.Value
select d;
switch (sortOrder)
{
case "date":
dates = dates.OrderBy(p => p.HolidayDate);
break;
}
model.HList4DD = dates.ToList();
return View(model);
}
my view
i've tried a number of different attempts here, the following code worked but has the drop list problem
#Html.DropDownListFor(model => model.HList4DD.First().HolidayDate,
new SelectList(Model.PList4DD, "Id", "Name"),
// Model.currentPersonID
"---Select---"
) *#
my attempts to resolve this are:
#Html.DropDownList("HolidayDate", Model.Categories, "---Select---")
#Html.DropDownListFor("HolidayDate", x => x.HolidayDate, Model.Categories)
Any help much appreciated
You are binding the DropDownFor to a wrong property.
Basically what you want to do is in your Model, create a new Property to bind the value selected by the dropdown.
public int SelectedDate {get;set;}
Then in your code front you wanted to use dropdownFor to bind the property like this
#Html.DropDownListFor(model => model.SelectedDate ,
new SelectList(Model.PList4DD, "Id", "Name"),
// Model.currentPersonID
"---Select---"
)
Not this.
#Html.DropDownListFor(model => model.HList4DD.First().HolidayDate ,
new SelectList(Model.PList4DD, "Id", "Name"),
// Model.currentPersonID
"---Select---"
)
Finnaly, in the action that you wanted to do the sorting, you will need to pass the SelectedDate into the action. Then before you returning it, assign it to Model. And the whole thing will work like magic.

MVC DropDownList SelectedValue not displaying correctly

I tried searching and didn't find anything that fixed my problem. I have a DropDownList on a Razor view that will not show the the item that I have marked as Selected in the SelectList. Here is the controller code that populates the list:
var statuses = new SelectList(db.OrderStatuses, "ID", "Name", order.Status.ID.ToString());
ViewBag.Statuses = statuses;
return View(vm);
Here is the View code:
<div class="display-label">
Order Status</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.StatusID, (SelectList)ViewBag.Statuses)
#Html.ValidationMessageFor(model => model.StatusID)
</div>
I walk through it and even in the view it has the correct SelectedValue however the DDL always shows the first item in the list regardless of the selected value. Can anyone point out what I am doing wrong to get the DDL to default to the SelectValue?
The last argument of the SelectList constructor (in which you hope to be able to pass the selected value id) is ignored because the DropDownListFor helper uses the lambda expression you passed as first argument and uses the value of the specific property.
So here's the ugly way to do that:
Model:
public class MyModel
{
public int StatusID { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
// TODO: obviously this comes from your DB,
// but I hate showing code on SO that people are
// not able to compile and play with because it has
// gazzilion of external dependencies
var statuses = new SelectList(
new[]
{
new { ID = 1, Name = "status 1" },
new { ID = 2, Name = "status 2" },
new { ID = 3, Name = "status 3" },
new { ID = 4, Name = "status 4" },
},
"ID",
"Name"
);
ViewBag.Statuses = statuses;
var model = new MyModel();
model.StatusID = 3; // preselect the element with ID=3 in the list
return View(model);
}
}
View:
#model MyModel
...
#Html.DropDownListFor(model => model.StatusID, (SelectList)ViewBag.Statuses)
and here's the correct way, using real view model:
Model
public class MyModel
{
public int StatusID { get; set; }
public IEnumerable<SelectListItem> Statuses { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
// TODO: obviously this comes from your DB,
// but I hate showing code on SO that people are
// not able to compile and play with because it has
// gazzilion of external dependencies
var statuses = new SelectList(
new[]
{
new { ID = 1, Name = "status 1" },
new { ID = 2, Name = "status 2" },
new { ID = 3, Name = "status 3" },
new { ID = 4, Name = "status 4" },
},
"ID",
"Name"
);
var model = new MyModel();
model.Statuses = statuses;
model.StatusID = 3; // preselect the element with ID=3 in the list
return View(model);
}
}
View:
#model MyModel
...
#Html.DropDownListFor(model => model.StatusID, Model.Statuses)
Make Sure that your return Selection Value is a String and not and int when you declare it in your model.
Example:
public class MyModel
{
public string StatusID { get; set; }
}
Create a view model for each view. Doing it this way you will only include what is needed on the screen. As I don't know where you are using this code, let us assume that you have a Create view to add a new order.
Create a new view model for your Create view:
public class OrderCreateViewModel
{
// Include other properties if needed, these are just for demo purposes
// This is the unique identifier of your order status,
// i.e. foreign key in your order table
public int OrderStatusId { get; set; }
// This is a list of all your order statuses populated from your order status table
public IEnumerable<OrderStatus> OrderStatuses { get; set; }
}
Order status class:
public class OrderStatus
{
public int Id { get; set; }
public string Name { get; set; }
}
In your Create view you would have the following:
#model MyProject.ViewModels.OrderCreateViewModel
#using (Html.BeginForm())
{
<table>
<tr>
<td><b>Order Status:</b></td>
<td>
#Html.DropDownListFor(x => x.OrderStatusId,
new SelectList(Model.OrderStatuses, "Id", "Name", Model.OrderStatusId),
"-- Select --"
)
#Html.ValidationMessageFor(x => x.OrderStatusId)
</td>
</tr>
</table>
<!-- Add other HTML controls if required and your submit button -->
}
Your Create action methods:
public ActionResult Create()
{
OrderCreateViewModel viewModel = new OrderCreateViewModel
{
// Here you do database call to populate your dropdown
OrderStatuses = orderStatusService.GetAllOrderStatuses()
};
return View(viewModel);
}
[HttpPost]
public ActionResult Create(OrderCreateViewModel viewModel)
{
// Check that viewModel is not null
if (!ModelState.IsValid)
{
viewModel.OrderStatuses = orderStatusService.GetAllOrderStatuses();
return View(viewModel);
}
// Mapping
// Insert order into database
// Return the view where you need to be
}
This will persist your selections when you click the submit button and is redirected back to the create view for error handling.
I hope this helps.
For me, the issue was caused by big css padding numbers ( top & bottom padding inside the dropdown field). Basically, the item was being shown but not visible because it was way down. I FIXED it by making my padding numbers smaller.
I leave this in case it helps someone else. I had a very similar problem and none of the answers helped.
I had a property in my ViewData with the same name as the selector for the lambda expression, basically as if you would've had ViewData["StatusId"] set to something.
After I changed the name of the anonymous property in the ViewData the DropDownList helper worked as expected.
Weird though.
My solution was this...
Where the current selected item is the ProjectManagerID.
View:
#Html.DropDownList("ProjectManagerID", Model.DropDownListProjectManager, new { #class = "form-control" })
Model:
public class ClsDropDownCollection
{
public List<SelectListItem> DropDownListProjectManager { get; set; }
public Guid ProjectManagerID { get; set; }
}
Generate dropdown:
public List<SelectListItem> ProjectManagerDropdown()
{
List<SelectListItem> dropDown = new List<SelectListItem>();
SelectListItem listItem = new SelectListItem();
List<ClsProjectManager> tempList = bc.GetAllProductManagers();
foreach (ClsProjectManager item in tempList)
{
listItem = new SelectListItem();
listItem.Text = item.ProjectManagerName;
listItem.Value = item.ProjectManagerID.ToString();
dropDown.Add(listItem);
}
return dropDown;
}
Please find sample code below.
public class Temp
{
public int id { get; set; }
public string valueString { get; set; }
}
Controller
public ActionResult Index()
{
// Assuming here that you have written a method which will return the list of Temp objects.
List<Temp> temps = GetList();
var tempData = new SelectList(temps, "id", "valueString",3);
ViewBag.Statuses = tempData;
return View();
}
View
#Html.DropDownListFor(model => model.id, (SelectList)ViewBag.Statuses)
#Html.ValidationMessageFor(model => model.id)

Adding an extra data text field in SelectList in MVC

I am using a select list to pass text to the checkbox , What I want is to pass the 2 text fields to the checkboxlist , but Select list doesnt have any options to provide 2 or 3 data text field , I tried to customize it like but cant get the Intellisense working :
public ActionResult Create()
{
IProductRepository ProductResp = new ProductRepository();
IQueryable<Object> getAllProducts = ProductResp .GetProductsSelectlist();
List<object> newList = new List<object>();
foreach (var events in getAllProducts)
newList.Add(new
{
Id = getAllProducts.Name, // I cant get .Name or DateAdded Intellisense here
Name = getAllProducts.Name + " " + getAllProducts.DateAdded
});
ViewData["events"] = new SelectList(newList.ToList(), "Id","Name");
return View();
}
ProductRepository
public IQueryable<Object> GetProductssSelectlist()
{
ApexWorldEntities entity = new ApexWorldEntities();
var query = from v in entity.Products
where v.Date > DateTime.Now
select new { ProductID = v.ID, v.Name , v.Date};
return query.OrderBy(v => v.Date);
}
First, create a class to hold your result:
public class ApexWorldResult
{
public int ProductID { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
}
Then, cast your target collection to an actual type:
var query = from v in entity.Products
where v.Date > DateTime.Now
select new ApexWorldResult { ProductID = v.ID, v.Name, v.Date };
In the Create() method, change the IQueryable<Object> declaration to IQueryable<ApexWorldResult>, and it should work for you...
in addition to my comment
what you need to define is instead of object use "SelectListItem"
This is build used for select list items. where you can specify text, value and value.
and
Model Definition:
class FooModel{
public SelectListItem selectList{get;set;}
...
}
And in controller you use:
public ActionResult YourAction(){
FooModel model = new FooModel();
//Define your collection of list items
List<SelectListItem> listItems = new List<SelectListItem>();
listItems.Add(new SelectListItem(){Selected = false, Text = "Text", Value = "MyValue"});
//Assign the list to the collection
model.selectList = new SelectList(listItems);
//Pass to the view
return View(model);
}

Resources