MVC4 SelectList not selected default object - asp.net-mvc

My select list isn't selecting the default object being brought in through code.
I first create my SelectList like so:
public SelectList CreateSelectList(object objSelected = null)
{
return new SelectList(GetAll().OrderBy(s => s.NumericalValue), "PeriodID", "Name", objSelected);
}
My objSelected gets filled with a Guid that's associated with the PeriodID.
Inside my controller I define my viewbag variable to the new select list.
public ActionResult Edit(Guid id)
{
Classroom classroom = classroomRepository.GetByID(id);
ViewBag.PeriodID = periodRepository.CreateSelectList(classroom.PeriodID);
return View(classroom);
}
Then in my View here's how I'm displaying my SelectList:
<div class="control-group">
#Html.LabelFor(model => model.PeriodID, "Period", new { #class = "control-label" })
<div class="controls">
#Html.DropDownListFor(model => model.PeriodID, ViewBag.PeriodID as SelectList, String.Empty, new { #class = "span3" })
#Html.ValidationMessageFor(model => model.PeriodID)
</div>
</div>

You have two problems here. First, this:
#Html.DropDownListFor(model => model.PeriodID, ViewBag.PeriodID as SelectList,
String.Empty, new { #class = "span3" })
Change ViewBag.PeriodID to ViewBag.Periods or ViewBag.PeriodList. This is confusing, and there are a number of situations in which MVC will get confused if you use the same named object. It's just best to make sure everything is named differently.
Second, The SelectList class ignores the selecteditem member of the SelectListItem. It's not used at all. DropDownListFor will take the value of model.PeriodID and make it the selected value. However, I see in your code that those should be the same so I'm guessing the naming may be a factor here.

Related

System.NullReferenceException in mvc partial view when passing a list

I am trying to add a list in search box in partial view, but I am always getting System.NullReferenceException. A similar option is working when I keep as a separate view. I am not what I am doing wrong when passing List?
Following is the snippet from views and controllers:
1] _layout.cshtml:
<div class="row">
#Html.Partial("SearchBarPartial2", Model)
</div>
2] SearchPartialView2.cshtml:
<div class="form-group">
#using (Html.BeginForm("SearchBarPartial2", "Search"))
{
#Html.LabelFor(m => m.CompanyList, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownListFor(
m => m.CompanyList,
new SelectList(Model.CompanyList, "fldt", "Value", Model.CompanyList.First().Value),
new { #class = "form-control" }
)
</div>
}
</div>
3] SearchController.cs:
public ActionResult SearchBarPartial2(cmpnytable cmpnytable1)
{
List<Company> objcompany = new List<Company>();
objcompany = GetCompanyList();
SelectList objlistofcompanytobind = new SelectList(objcompany, "ID", "Name", 0);
cmpnytable1.CompanyList = objlistofcompanytobind;
return View(cmpnytable1);
}
Your drop down list declaration already shown evidence of the error:
#Html.DropDownListFor(m => m.CompanyList, new SelectList(Model.CompanyList, "fldt", "Value", Model.CompanyList.First().Value), new { #class = "form-control" })
As Stephen said, you're assigned the model binding expression pointed to CompanyList, which becomes the source of all option tags to be rendered. It has no sense to pass the SelectList items as both binding target and source of the option list.
To resolve this issue, place additional model property with integer/string type for holding DropDownList selection result as this:
// Model
public class cmpnytable
{
// other properties here
public int SelectedId { get; set; }
}
// View
#model myproj.Models.cmpnytable
#Html.DropDownListFor(m => m.SelectedId, Model.CompanyList, new { #class = "form-control" })
Since CompanyList itself passed to view as SelectList, it's no use to create new instance of SelectList on the view.

Html Helper Drop Down List switches value to top option on submit / in database

I am filling out a form, however when selecting an option from the drop down list and click submit, no matter what option I select, it always parses the top one through. The displayed value never changes, so it you leave it as the default option 'please select...' and click submit, this stays as 'please select...' but the entry in the database is always the one that appears at the top of the drop down.
Here is the model:
public enum Medium
{
[Description("Teleconference & Report")]
Teleconference_Report,
[Description("Email & Telephone")]
Email_Telephone
}
[Required]
[Display(Name = "Medium")]
public Medium Medium { get; set; }
Here is the field in the form:
<div class="form-group">
#Html.LabelFor(model => model.Medium, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.DropDownList("MediumID", null, "Please select...", htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Medium, "", new { #class = "text-danger" })
</div>
</div>
The "MediumID" DropDownList is populated using a viewbag which is set to whatever the following returns:
// Puts all of the mediums of communication into a user friendly dropdownlist.
public List<SelectListItem> GetMediumList()
{
List<SelectListItem> mediumList = new List<SelectListItem>();
foreach (Medium state in EnumToList<Medium>())
{
mediumList.Add(new SelectListItem
{
Text = GetEnumDescription(state),
Value = state.ToString(),
});
}
return mediumList;
}
Below shows the form section for another enum called 'Frequency', but these are not changed to user friendly strings (and is working fine).
<div class="form-group">
#Html.LabelFor(model => model.Frequency, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.EnumDropDownListFor(model => model.Frequency, "Please select...", htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Frequency, "", new { #class = "text-danger" })
</div>
</div>
Below here, shows the two methods which turn the enums into user friendly strings:
// Returns a 'user friendly', readable version of the enum.
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
// Puts all of the same enums into a list.
public static IEnumerable<T> EnumToList<T>()
{
Type enumType = typeof(T);
// Can't use generic type constraints on value types,
// so have to do check like this.
if (enumType.BaseType != typeof(Enum))
throw new ArgumentException("T must be of type System.Enum");
Array enumValArray = Enum.GetValues(enumType);
List<T> enumValList = new List<T>(enumValArray.Length);
foreach (int val in enumValArray)
{
enumValList.Add((T)Enum.Parse(enumType, val.ToString()));
}
return enumValList;
}
Finally, here is the method signature where the fields are binded/bound:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Point,ApplicationID,MediumID,Frequency,StartDate,EndDate")] TouchPoint touchPoint)
Within this method, the dropdown is passed to the view using the following:
ViewBag.MediumID = GetMediumList();
Any help is greatly appreciated.
Your model has a property named Medium but your view does not bind to that property. The name of the <select> your generating is MediumID which does not exist in your model, so the default value for Medium when you submit will Teleconference_Report (the first enum value).
Change the view to
#Html.DropDownListFor(m => m.Medium, (IEnumerable<SelectListItem>)ViewBag.MediumID, "Please select...", new { #class = "form-control" })
although I would recommend changing the ViewBag property name to say MediumList to make it more obvious that its a collection. And even better, use a view model with a property public IEnumerable<SelectListItem> MediumList { get; set; } so that the viewcan be #Html.DropDownListFor(m => m.Medium, Model.MediumList, .... ).
You also need to change the [Bind] attribute to include "Medium" (and remove "MediumID") although using a view model means the [Bind] attribute is not required.
Side note: You do not need the [Required] attribute unless you want to add a specific error message using the ErrorMessage = "..." property (an enum is always required by default unless you make the property nullable).

Custom editor template for Enum - How to handle null in Create view?

I am trying to create a custom editor template for enum properties.
I thought I had it good. Rob Lyndon helped me with a htmlHelper extension and it worked great on the Edit view.
But on the Create view it errors out because the value is null.
If I make the model nullable Enum? then I won't be able to get the values to populate the select list.
I don't want to have to initialize all models with a default value. And I would like to avoid having to create a different editor template for each type of enum.
Are there any better alternatives?
HtmlHelper
public static MvcHtmlString EnumTextDropDownListFor<TModel>(this HtmlHelper<TModel> html, Expression<Func<TModel, Enum>> expression, Type enumType, object htmlAttributes)
{
var enumValues = Enum.GetValues(enumType).OfType<Enum>().Select(v => v.ToString()).ToArray();
var selectList = new SelectList(enumValues.Select(v => new SelectListItem { Text = v, Value = v }));
return html.DropDownListFor(expression, selectList, htmlAttributes);
}
Editor Template
#model Enum
#{
var htmlAttributesFromView = ViewData["htmlAttributes"] ?? new { };
var htmlAttributes = Html.MergeHtmlAttributes(htmlAttributesFromView, new { #class = "form-control" });
var type = Model.GetType();
}
<div class="form-group">
#Html.LabelFor(model => model, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-8">
#Html.EnumTextDropDownListFor(model => model, type, htmlAttributes)
#Html.ValidationMessageFor(model => model)
</div>
<a class="infoonclick col-md-1" title="#Html.DisplayNameFor(model => model)" data-content="#Html.DescriptionFor(model => model)">
<span class="fa fa-info-circle"></span>
</a>
</div>
I use
#model Enum
#Html.EnumDropDownListFor(model => model, new { #class = "form-control" })
in my Enum.cshtml view
It might be a bit late, but I just came across this problem and this is how I solved it:
#{
var type = Nullable.GetUnderlyingType(ViewData.ModelMetadata.ModelType)
?? ViewData.ModelMetadata.ModelType;
}
This will allow the model to be null and avoid the nullref error you would otherwise get trying to query the type of the "null" model.
in latest dotnet core just use the following:
1- make the model dynamic
#model dynamic
2- get the enum type with:
var enumType = ViewData.ModelMetadata.UnderlyingOrModelType;
this will handle both nullable and non-nullable enums

DropDownList Error in MVC3

After adding the class to the html.dropdownlist am facing the below error.
Error:'System.Web.Mvc.HtmlHelper' does not contain a definition for 'DropDownList' and the best extension method overload 'System.Web.Mvc.Html.SelectExtensions.DropDownList(System.Web.Mvc.HtmlHelper, string, string)' has some invalid arguments
<li>
#Html.LabelFor(m => m.BuildType)
#Html.DropDownList("BuildType", new { #class = "BuildType" })
#Html.ValidationMessageFor(m => m.BuildType)
</li>
<li>#Html.LabelFor(m => m.BuildMode)
#Html.DropDownList("BuildMode", new { #class = "BuildMode" })
#Html.ValidationMessageFor(m => m.BuildMode) </li>
<li>
Where are your list options? You need to provide a list of options via an IEnumerable<SelectListItem> object (see this overload).
So your model would have something like this:
public IEnumerable<SelectListItem> BuildModeOptions { get; set; }
And your view would pass the list into the helper:
#Html.DropDownList("BuildMode", Model.BuildModeOptions, new { #class = "BuildType" })
Or, since you're using the type-safe For versions on your other helpers, use DropDownListFor on this one as well:
#Html.DropDownListFor(m => m.BuildMode, Model.BuildModeOptions, new { #class = "BuildType" })
But keep in mind, Model.BuildMode is your selected value -- Model.BuildModeOptions is for your dropdown options.
You can use:
#Html.DropDownListFor(m => m.BuildType, (SelectList)Viewbag.YourSelectList, "Select Build type", new { #class = "BuildType" })
or
#Html.DropDownListFor(m => m.BuildType, Model.YourSelectList, "Select Build type", new { #class = "BuildType" })
When you use #Html.DropDownList, you specify a name for the dropdownlist... but you are missing the SelectList itself. I think that out of the box, the helper will try to use the name of the DropDownList (in your case "BuildType") to search in the ViewData collection for the SelectList.
When you use a #Html.DropDownListFor you don't use a name, but a lamda expression m => m.BuildType that will help you in same cases to not have harcoded names.
Your SelectList (the second parameter) can be grabbed from Viewbag or from a property in your Model.
The second parameter should be the list of items you want to show in the dropdown.
so It will look like :
#Html.DropDownListFor("BuildType", m.yourListofItems, new { #class = "BuildType" })

DataBinding: 'System.Web.Mvc.SelectListItem' does not contain a property with the name 'CategoryTypeID'

I am using MVC. I want to pass the category data I entered from my view and passed to my Post/ Createcontroller, but it's not's letting me pass my categoryTypeID that I have selected from my dropdownlist.
Here is the error:
DataBinding: 'System.Web.Mvc.SelectListItem' does not contain a property with the name 'CategoryTypeID'.
Here is my code:
My CreateController:
//
// POST: /Category/Create
[HttpPost]
public ActionResult Create(Category category)
{
if (ModelState.IsValid)
{
db.Categories.Add(category);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CategoryTypes = new SelectList(db.CategoryTypes, "CategoryTypeID", "Name", category.CategoryTypeID);
return View(category);
}
My Create View
#model Haykal.Models.Category
<div class="editor-label">
#Html.LabelFor(model => model.CategoryTypeID, "CategoryType")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.CategoryTypeID,
new SelectList(ViewBag.CategoryTypes as System.Collections.IEnumerable, "CategoryTypeID", "Name"),
"--select Category Type --", new { id = "categoryType" })
#Html.ValidationMessageFor(model => model.CategoryTypeID)
</div>
I faced this error. I was binding an object of the View Model:
editPanelViewModel.Panel = new SelectList(panels, "PanelId", "PanelName");
In the View, I created the ListBox like this:
#Html.ListBoxFor(m => m.Panel, new SelectList(Model.Panel, "PanelId", "PanelName"))
It should be like this in fact:
#Html.ListBoxFor(m => m.Panel, new SelectList(Model.Panel, "Value", "Text"))
You are defining your SelectList twice, in your controller as well as in your view.
Keep the view clean. Just the following would be enough in your case:
#Html.DropDownListFor(model => model.CategoryTypeID, (SelectList)ViewBag.CategoryTypes)
I have to admit that DropDownListFor is quite confusing in the beginning :)

Resources