ASP.NET MVC3 DropDownListFor Enum - asp.net

Hi I'm fairly new to MVC3 and I'm trying to do something that I think must be fairly common, but can't quite get it.
I have a model I wish store the value for enum DayOfWeek in:
public class Booking
{
public int ID { get; set; }
public int Day { get; set; }
....
}
I've made it an int to store in the database.
I want to edit in the View as a DropDownList:
<div class="editor-field">
#Html.DropDownListFor(model => model.Booking.Day, new SelectList(Enum.GetValues(typeof(DayOfWeek))))
#Html.ValidationMessageFor(model => model.Booking.Day)
</div>
However I get the error: "The field day must be a number".
I know I'm missing something, and probably something simple, can anyone help?

Add a SelectList property to your viewmodel and populate as per #Brandon's answer here.
Then you will change your code to:
#Html.DropDownListFor(model => model.Booking.Day, Model.SelectListProperty)
(where SelectListProperty is the name of your property on your viewmodel)

You can also change your model class using DayOfWeek:
public class Booking
{
public int ID { get; set; }
public DayOfWeek Day { get; set; }
}

Related

ASP.NET MVC 5 prevent value changes

When I update my website, it hints me this problem "{"The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.\r\nThe statement has been terminated."}"
The screenshot is list below, there is a value named RecordDate, it has value, but I will not change anything about that value so I didn't display it on the screen.
The problem is MVC automatically update that value for me, and the value of the date becomes 0000-00-01 i think, maybe something else, how to prevent it? just keep the origin value and update other columns.
The model class looks like this
public class ShiftRecord
{
public int ID { get; set; }
public int EmployeeID { get; set; }
[Display(Name="Company Vehicle?")]
[UIHint("YesNo")]
public bool IsCompanyVehicle { get; set; }
[Display(Name="Own Vehicle?")]
[UIHint("YesNo")]
public bool IsOwnVehicle { get; set; }
//Problem comes from this line
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString="{0:yyyy-MM-dd}")]
public DateTime RedordDate { get; set; }
[Display(Name="Day Type")]
public Nullable<DayType> DayType { get; set; }
[Display(Name="Normal Hrs")]
public Nullable<int> NormalHours { get; set; }
[Display(Name="Time and Half Hrs")]
public Nullable<int> TimeAndHalfHours { get; set; }
[Display(Name="Double Time Hrs")]
public Nullable<int> DoubleTimeHours { get; set; }
[Display(Name="Shift Hrs")]
public Nullable<int> ShiftHours { get; set; }
public string Comment { get; set; } // System manager can leave any comment here
public bool IsRead { get; set; } // has this shift record been read
public virtual Employee Employee { get; set; }
public virtual ICollection<JobRecord> JobRecords { get; set; }
}
In the controller, I didn't change anything about the model, so it looks like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,EmployeeID,IsCompanyVehicle,IsOwnVehicle,RecordDate,DayType,NormalHours,TimeAndHalfHours,DoubleTimeHours,ShiftHours,Comment,IsRead")] ShiftRecord shiftrecord)
{
if (ModelState.IsValid)
{
db.Entry(shiftrecord).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.EmployeeID = new SelectList(db.Employees, "ID", "LastName", shiftrecord.EmployeeID);
return View(shiftrecord);
}
And I didn't change Edit view as well, the only thing is I made RecordDate unchangeable, changed it from #Html.EditorFor to #Html.DisplayFor
<div class="form-group">
#Html.LabelFor(model => model.RedordDate, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DisplayFor(model => model.RedordDate)
#Html.ValidationMessageFor(model => model.RedordDate)
</div>
</div>
Your issue is .net uses a default 1/1/0001 datetime min value, and the sql minimum value is 1/1/1753, which is incompatible. If you use datetime?, it will resolve as null and work OK, or put in code to update the date to a default value before committing to the database.
Your understanding is incorrect. ASP.NET MVC did not automatically update the value for you, the problem arises because you did not post RedordDate to the controller action so RedordDate will have its default value (i.e. default(DateTime)).
DateTime is a value type in .NET such that it cannot be null and its default value is DateTime.MinValue (i.e. 01/01/0001 00:00:00).
You can solve it by making the RedordDate property Nullable by changing its type from DateTime to DateTime? so that it accepts null values.
One thing to note is that if you save this value back to a SQL Server but your underlying SQL datatype is datetime instead of datetime2, you will receive an exception since 01/01/0001 00:00:00 is out-of-range in datetime
Further reading:
MSDN recommends using datetime2 in a new development
Difference between value types and reference types explained by Jon Skeet
You do render any controls for property RedordDate so when you post back, the DefaultModelBinder initializes a new instance of ShiftRecord and RedordDate has a value of DateTime.MinValue (1/1/0001).
Add a hidden control for the property to post it back
#Html.HiddenFor(m => m.RedordDate)
Then in the POST method, remove the [Bind] attribute. Currently, even if the value is posted back, it will not bind because it has been excluded from the Include list. You have RecordDate but not RedordDate (a typo?). Note by default all properties will be bound so the attribute is not necessary unless you are specifically excluding properties.
A better alternative is to create a view model that contains only those properties you want to display and edit (What is a view model in MVC) and then in the POST method, get the original data model and map the view model properties to it.
Side note: Can the vehicle be both IsCompanyVehicle and IsOwnVehicle?

ASP.net MVC3 Select Foreign Key in Veiw using DropDownList

First off, sorry for brinning up, yet again, a topic that has been coverd many times before on this site - As much as I try, I just can't get my code to work.
I have the following model classes:
public class ProjectsTable {
public int ID {get; set;}
public String ProjectName {get; set;}
...
public virtual Manager Manager_Id {get; set;} // Stores foreign key of Manager
}
public class Manager {
public int ID {get; set;}
public String ManagerName {get; set;}
...
public virtual ICollection<ProjectsTable> Projects {get; set;}
}
The ProjectsTable Controller class contains the following method to double as a display and edit page:
private SynthContext db = new SynthContext();
...
// GET: /ProjectsTable/Parameters/5
public ActionResult Parameters(int id) {
ProjectsTable projectstable = db.ProjectsTable.Find(id);
ViewBag.Manager_Id = new SelectList(db.Manager, "ID", "ManagerName");
return View(projectstable);
}
// POST: /ProjectsTable/Parameters/5
public ActionResult Parameters(ProjectsTable projectstable) {
if(ModelState.IsValid) {
db.Entry(projectstable).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Parameters");
}
ViewBag.Manager_Id = new SelectList(db.Manager, "ID", "ManagerName");
return View(projectstable);
}
Finally, Parameters.cshtml contains the following code to display the dropdown
#model Synth.Models.ProjectsTable
...
<fieldset>
...
<div class="editor-lable">
#Html.LabelFor(model => model.Manager_ID)
</div>
<div class="editor-field">
#HTML.DropDownList("Manager_ID", String.Empty)
</div>
<input type="submit" value="Update">
</fieldset>
When I test the code, I can navigate to /ProjectsTable/Parameters/x without any problem; the drop-down is correctly generated containing a list of the manager names. However, when I click on the "Update" button, the Manager_ID is highlighted in red indicating the change has not registers. I can confirm that the changes have not registered by navigating to /ProjectsTable/list where I find no changes have taken effect.
I have tried adding #Html.ValidationMessageFor(Model.Manager_ID), the result is alway "The value 'x' is invalid", where x is a valid ID value (I have checked it against the database).
I have looked at the generated HTML source and can't see anything wrong there neither.
I have a suspicion that I need to replace #HTML.DropDownList with #HTML.DropDownListFor, but I can't figure out what parameters it needs in order to get it working.
EDIT - I have now managed to get DropDownListFor working, but the problem persists. A related question to help me resolve this issue might be how to view the post data sent back to the server.
Any help would be much appreciated.
Thanking you all in advance...
Chopo
I think you are confusing yourself because you have a model property Manager_Id and a viewBag object Manager_Id. Your modelState error is referring to the model property, not any of the values in the drop down list, which are bound to the viewBag object. I suggest re-naming your ViewBag to avoid confusion (or, better yet, not using ViewBag at all), and adding a hidden field for the model property:
#Html.HiddenFor(m => m.Manager_Id)
As a side note, from your descriptions of your troubleshooting efforts above, I also suggest becoming comfortable debugging in visual studio, setting breakpoints and inspecting values. It will make your life so much easier in the long run.
So I finally found the problem. It had nothing to do with the view or the controller. Apparently I hadn't properly written up the model: The foreign key should be stored as an int? as well as a pointer to the model in question.
public class ProjectsTable {
public int ID {get; set;}
public String ProjectName {get; set;}
...
public int? ManagerID {get; set;} // Stores foreign key of Manager
public virtual Manager Manager // I have no idea if this line does anything
}
public class Manager {
public int ID {get; set;}
public String ManagerName {get; set;}
...
public virtual ICollection<ProjectsTable> Projects {get; set;}
}
Whilst not necessary, the View also benefits from using the function:
Html.DropDownListFor(model => model.Manager_ID, ViewBag.Manager_Id as SelectList, String.Empty)
The function #HTML.DropDownList("ManagerID", String.Empty) also works, but in order for it to do so the name of the ViewBag object being passed from the controller (ManagerID) must exactly match the foreign key of the model. This is convoluted and confusing for my taste. I prefer the strongly typed approach.
For completeness, I include the fully corrected code:
Controller:
private SynthContext db = new SynthContext();
...
// GET: /ProjectsTable/Parameters/5
public ActionResult Parameters(int id) {
ProjectsTable projectstable = db.ProjectsTable.Find(id);
// The following line has been amended, but this change is not strictly necessary
ViewBag.m_id = new SelectList(db.Manager, "ID", "ManagerName", projectstable.ManagerID);
return View(projectstable);
}
// POST: /ProjectsTable/Parameters/5
public ActionResult Parameters(ProjectsTable projectstable) {
if(ModelState.IsValid) {
db.Entry(projectstable).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Parameters");
}
ViewBag.m_id = new SelectList(db.Manager, "ID", "ManagerName", projectstable.ManagerID);
return View(projectstable);
}
View:
#model Synth.Models.ProjectsTable
...
<fieldset>
...
<div class="editor-lable">
#Html.LabelFor(model => model.ManagerID)
</div>
<div class="editor-field">
Html.DropDownListFor(model => model.ManagerID, ViewBag.m_id as SelectList, String.Empty)
#Html.ValidationMessageFor(model => model.ManagerID)
</div>
<input type="submit" value="Update">
</fieldset>

ASP.NET MVC3 - problem handling multiple values from a strongly typed Listbox

I'm having a problem handling multiple values from a strongly typed Listbox.
I have an Event Class that can have multiple Technology classes.
Here's my simplified Event class:
public class Event
{
public int ID { get; set; }
public IEnumerable<Technology> Technologies { get; set; }
}
I was using this
public List<Technology> Technologies { get; set; }
and changed to
public IEnumerable<Technology> Technologies { get; set; }
but still got the same error.
Here's the Technology class, it's a really simple one
public class Technology
{
public int ID { get; set; }
public String Description{ get; set; }
}
here's my simplified Controller
public ActionResult Create()
{
var TechnologyQry = from d in db.Technology
orderby d.Description
select new { d.ID, d.Description };
ViewBag.eventTechnology = new MultiSelectList(TechnologyQry ,"ID","Description");
return View();
}
and here's the view portion that renders the ListBox
#Html.ListBoxFor(model => model.Technologies, (MultiSelectList)ViewBag.eventTechnology)
and thit's the error I'm getting
The ViewData item that has the key 'Technologies' is of type 'System.Collections.Generic.List`1[[stuff.Models.Technology, stuff, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' but must be of type 'IEnumerable'./
Sorry, English is not my main language.
I'd appreciate any help I can get.
Thank you.
What Model have do you have defined in the View #model ...?
Besides that, the Create method isn't returning something to be used for/as the model.
If you want the listbox to be called "Technologies" in the name attribute, you could instead use:
#Html.ListBox("Technologies", (MultiSelectList)ViewBag.eventTechnology)

Html.ListBoxFor error problem asp.mvc 3

I have something like this in my code and I am getting error: Exception Details: System.ArgumentException: Value cannot be null or empty.
Parameter name: name . What am I doing wrong ? Thanks for help
#model IEnumerable<NHibernateFluentProject.Patient>
#Html.ListBoxFor(model => model, new SelectList(Model,"ID", "FirstName"));
#Html.ListBoxFor is used for your strong typed viewmodel. which could help to bind to your property. First part will take a lambda expression for a single item as a default seleced for your listbox, second part will take the item collections to dispaly all the listbox items.
For example: you have following two classes.
public class HospitalViewModel
{
public string SelectedPatient { get; set; }
public IEnumerable<Patient> AllPatients { get; set; }
}
public class Patient
{
public int Id { get; set; }
public string FirstName { get; set; }
}
From you view, you should do something like
#model HospitalViewModel
#Html.ListBoxFor(model => model.SelectedPatient, new SelectList(Model.AllPatients,"Id", "FirstName"));
OR if you only want to bind all your patients to a listbox, then use Html.ListBox instead
#model IEnumerable<Patient>
#Html.ListBox("ListBoxName", new SelectList(Model,"Id", "FirstName"));
You need to pass a lambda expression containing the property to bind the listbox to.

Populate dropdownlist from another dropdownlist with objects in MVC 2

I am trying to develop a simple MVC 2 timesheet application for my small business.
I have a sort of mock model for now until I have a database in place, just to make things simpler while I develop the functionality. It consists of the following:
public class CustomersRepository
{
public CustomersRepository()
{
Customers = new List<Customer>();
}
public List<Customer> Customers { get; set; }
}
public class Task
{
public Task()
{
Customer = new Customer();
TimeSegments = new List<TimeSegment>();
}
public override string ToString()
{
return Name;
}
public string Name { get; set; }
public Customer Customer { get; set; }
public List<TimeSegment> TimeSegments { get; set; }
}
public class TimeSegment
{
public string Id { get; set; }
public string Date { get; set; }
public int Hours { get; set; }
}
public class Customer
{
//To show the name in the combobox instead of the object name.
public override string ToString()
{
return Name;
}
public Customer()
{
Tasks = new List<Task>();
}
public List<Task> Tasks { get; set; }
public string Name { get; set; }
}
I initialize the repository in the controller, and pass the "model" to the view:
CustomersRepository model = new CustomersRepository();
public ActionResult Index()
{
InitializeRepository();
return View(model);
}
Now, in the view I populate a dropdownlist with the customers:
<div>
<%:Html.DropDownListFor(m => m.Customers, new SelectList(Model.Customers), new {#id="customerDropDownList"}) %>
</div>
But then I need to populate a second dropdownlist (taskDropDownList for the tasks associated with a particular customer) based on the selection the user chooses in the customer dropdownlist.
But how do I do this exactly? I have seen examples with jQuery, but I'm not sure how to apply them to this situation. Also, the examples seem to just populate the lists with string values. I need to be able to access the objects with all their properties. Because the next thing I need to do is to be able to populate the TimeSegments list of the selected task with values from input fields (i.e. the hours worked for particular dates). And for that to be saved to the "model" (eventually to the database) in the controller, how do I get it there, unless the objects are all part of the same model bound to the View?
I'm on rather thin ice with this since I still find the connection between the View and the Controller hard to handle, compared with e.g. Windows development, where these things are rather easy to do. So I would really appreciate a good step by step example if anyone would be so kind as to provide that!
I found the answer here:
http://www.pieterg.com/post/2010/04/12/Cascading-DropDownList-with-ASPNET-MVC-and-JQuery.aspx
It needed some tweaks, and I got help here from CGK. See this post:
Cascading dropdownlist with mvc and jQuery not working

Resources