Pardon my inexperience, I've got C# chops but I'm just starting to learn ASP.NET MVC framework.
So I've got some models that look like this:
public abstract class Movement
{
public int Id { get; set; }
public string Name { get; set; }
public abstract MovementType Type { get; }
}
public class WeightedMovement : Movement
{
public override MovementType Type
{
get
{
return MovementType.WeightBased;
}
}
public double Weight { get; set; }
}
public class LengthMovement : Movement
{
public int Distance { get; set; }
public override MovementType Type
{
get
{
return MovementType.LengthBased;
}
}
}
public enum MovementType
{
LengthBased,
WeightBased
}
In the Create view and I want to use a combo box to select the type and then have the appropriate form elements appear (right now that's just either a text box for the Weight or Distance).
Here's what the view looks like so far:
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Type, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EnumDropDownListFor(model => model.Type)
#Html.ValidationMessageFor(model => model.Type)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
I've only ever done web work for a hobby, never professionally so I my go to naive way to do this would be to use jQuery to remove or add elements to the DOM and then casting to the correct concrete type based upon the Type property in the Controller method.
That all seems to be working around the framework instead of working with it. Are there razor tags I don't know about? Am I approaching this wrong? This obviously isn't new but I couldn't think of the right search terms for it.
Related
I am developing an web application (Food blog) in ASP.NET MVC where I want to allow the user to type in a Recipe. A recipe does of course contains a lot of ingredients.
I have the following classes:
public class Recipe
{
public string Name { get; set; }
public int ID { get; set; }
public string Auhtor { get; set; }
public string Category { get; set; }
public string Text { get; set; }
public List<Ingredient> Ingredients { get; set; }
public byte[] Picture { get; set; }
}
public class Ingredient
{
public int ID { get; set; }
public double Amount { get; set; }
public string Unit { get; set; }
public string Name { get; set; }
}
And in html i would like to make a page where the user can type in a recipe with a lot of ingredients.
The create-view so far looks like this:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Recipe</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Category, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Category, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Category, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Text, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Text, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Text, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" id="Ingredient">
#Html.LabelFor(model => model.Ingredients, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10" id="IngredientDiv">
<label>Ingredient</label>
</div>
</div>
<div class="AddEl">
<p><a>Tilføj endnu en ingredients</a></p>
</div>
<div class="RemoveEl">
<p><a>Fjern en ingredients</a></p>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Picture, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Picture, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Picture, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
<script type="text/javascript" src="/Scripts/jquery-1.10.2.js"></script>
<script>
//Ajax to navigate between days
$(document)
.ready(function () {
var counter = 1;
$('.AddEl').click(function () {
var el = $('#IngredientDiv').clone().attr('id', 'element_' + ++counter).appendTo('#Ingredient');
});
$('.RemoveEl').click(function () {
var el = $(this).parents('.elem');
if (counter > 1) {
el.remove();
counter--;
}
});
});
</script>
</div>
}
How can I link the Ingredients to this view?
AS you can see I have already written the code to dynamically add and remove elements of the ingredient-div.
Thanks for your help!
Not sure how you want to show List<Ingredient> in your view but you can have List<Ingredient> by having view.bag in your controller:
ViewBag.Ingredients= new SelectList(db.GetIngredient(), "Value", "Text");
and in your view you could have :
#Html.DropDownListFor(model => model.Ingredients, new SelectList(ViewBag.Ingredients, "Value", "Text")
Probably you need more that one #Html.DropDownListFor and you can keep the first DropDownListFor visibale and the rest invisible. Then as soon as user adds one Ingredient you can make the next DropDownListFor visible to let user add another Ingredient.
First of all you need to make user there is relationship between tables. Add new attribute in Recipe Class:
public int IngredientsID;
Then need to encapsulate both of your entity classes in a View Model. You can accomplish same thing with view bag but using view model is the best practice. Create view model like this:
public class FoodViewModel {
public Recipe Recipe {get; set;}
public IEnumerable<Ingredient> Ingredients {get; set;}
}
Retrieve the list of Ingredients from context and supply it to your view model and pass your view model to your view .cshtml
public ActionResult New()
{
var _list = _context.Ingredients.ToList();
var viewModel = new FoodViewMode
{
Ingredients = _list
};
return View(viewModel);
}
Now, strongly type your view .cshtml with FoodViewModel.
#model Models.FoodViewModel
You will need to make some changes in your view .cshtml since your view model contains two entity models. You will need to use fully qualified name here. e.g.
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
Will turn into:
#Html.LabelFor(model => model.Receipe.Name, htmlAttributes: new { #class = "control-label col-md-2" })
Instead of "model.Name", we are using "model.Receipe.Name".
Now to get drop down list you can do:
#Html.DropDownListFor(x => x.Recipe.IngredientsID, new SelectList(Model.Ingredients, "ID", "Name"));
I hope it will solve your problem.
I have the following two classes:
public class Game
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public Console Console { get; set; }
}
public class Console
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime ReleaseDate { get; set; }
public string Company { get; set; }
}
After this I added a GameController with scaffolding. Now I am working on the Create view of the Game Object and I am experiencing the problem that the MVC HTML.helpers are rendering input fields for all of the properties of a Console object instead of the just the title (which I guess is logical, given the fact that the property is an actual Console object).
I still want the user to choose a console to be taken along for the Game Object being created, so I tried solving it in the following way:
<div class="form-group">
#Html.LabelFor(model => model.Genre, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Genre, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Genre, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Console, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Console.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Console.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
I thought I would simply mention the title property of the model, but I'm still receiving input fields for all the Console properties in my Game's create view.
I understand this is simply the way MVC works. But how should I deal with something like this? I also tried things like:
Console: #Html.DropDownList("console", "All")
Of course the problem here is that the HTML form does not understand that this field is meant to be taken along as the console field for the game object being created. What is the correct solution for this problem?
EDIT:
I did the following:
Add a gameCreation viewModel:
public class GameCreation
{
public Game game { get; set; }
public SelectList Consoles { get; set; }
}
Then I built my ViewModel and passed it to view via controller:
public ActionResult Create()
{
var gc = new GameCreation();
gc.Consoles = cb.BuildList(db);
return View(gc);
}
Note that cb.BuildList(db) returns a SelectList item. Then in the view I tried:
<div class="form-group">
#Html.LabelFor(model => model.Console.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("console", gc.Consoles)
#Html.ValidationMessageFor(model => model.Console.Title, "", new { #class = "text-danger" })
</div>
</div>
This does not work, as gc is not known. I also tried passing the data via the ViewBag but this item is also not known here. Also I then received the error:
HTMLHelper has no applicable method named 'dropdownlist'. How can I access the data of the viewmodel which I passed?
I am fairly new to MVC5 and C# and I am trying to achieve something that I don't fully understand.
I have a Team Model such as this:
public class Team
{
[Key]
public Guid ID { get; set; }
public string TeamName { get; set; }
public string Coach { get; set; }
public string Conference { get; set; }
}
I also have a Player Model such as this:
public class Player
{
[Key]
public Guid Id { get; set; }
[ForeignKey("Teams")]
public Guid TeamId { get; set; }
public string Name { get; set; }
public virtual Team Teams { get; set; }
}
View Model is
public class TeamViewModel
{
public string TeamName { get; set; }
public string Coach { get; set; }
public string Conference { get; set; }
public List<Player> Players { get; set; }
}
With this structure, you are suppose to be able to add and infinite number of players to each team. As such I have a Teams table with few properties and a Player table that contains the player name as well as the player TeamId so that we know to what team they belong.
My problem comes when I am creating a team. I have Create Controller such as this:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(TeamViewModel model)
{
if (ModelState.IsValid)
{
var team = new Team { TeamName = model.TeamName, Coach = model.Coach, Conference = model.Conference, Player = model.Player };
db.Teams.Add(team);
var result = await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View();
}
And my View is as follows:
#model SoccerTeams.Models.ViewModels.TeamViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Team</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.TeamName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TeamName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TeamName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Coach, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Coach, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Coach, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Conference, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Conference, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Conference, "", new { #class = "text-danger" })
</div>
</div>
#if (#Model != null)
{
foreach (var p in Model.Player)
{
<div class="form-group">
#Html.Raw("<label class=\"control-label col-md-2\">" + p.ToString() + "</label>")
<div class="col-md-10">
#Html.Raw("<input class=\"form-control text-box single-line\" name=\"Player\" type-\"text\"")
</div>
</div>
}
}
else
{
<div class="form-group">
#Html.Raw("<label class=\"control-label col-md-2\">Player</label>")
<div class="col-md-10">
#Html.Raw("<input class=\"form-control text-box single-line\" name=\"Player\" type-\"text\"")
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
From my understanding, the View is suppose to be able to convert the input element to a list and pass it on to my ViewModel. However, my ViewModel is always coming up as null.
What am I missing and how would I make this work?
P.S. I understand that I can user Html.EditorFor, but I was not able to get it working, so I just printed it out as Html as I need to solve my other problem first.
Edit
I have altered my View to have the following code
<div class="form-group">
#Html.Raw("<label class=\"control-label col-md-2\">Player</label>")
<div class="col-md-10">
#Html.Raw("<input class=\"form-control text-box single-line\" name=\"model.Players[0].Name\" type-\"text\"")
</div>
</div>
As a result, the model now properly populates the Players Array, however all other values have now become null. If I remove the input element, the values are populated but players array is null again as there are no form fields for it. Do you know what could be the culprit?
In the TeamViewModel I have also renamed Player to Players.
In order for MVC to bind your form data to the Action method's parameters
their names should match.
Supposing your ViewModel has property for List<Player> Players your code should be:
In your case:
foreach (var p in Model.Player)
{
<div class="form-group">
#Html.Raw("<label class=\"control-label col-md-2\">" + p.ToString() + "</label>")
<div class="col-md-10">
#Html.Raw("<input class=\"form-control text-box single-line\" name=\"Player\" type-\"text\"")
</div>
</div>
}
Should be:
for (int i = 0; i < Model.Player.Length; i++)
{
<div class="form-group">
#Html.Raw("<label class=\"control-label col-md-2\">" + p.ToString() + "</label>")
<div class="col-md-10">
#Html.Raw("<input class=\"form-control text-box single-line\" name=\"model.Player[" + i + "].Name\" type-\"text\"")
</div>
</div>
}
Because this is the name of the parameter that you have provided:
Create(TeamViewModel model)
Also be careful because the indexes should not be broken, which means that they should be 0, 1, 2.. etc. without skipping a number.
The way that we read in the properties is by looking for
parameterName[index].PropertyName. The index must be zero-based and
unbroken.
NOTE You can read more about binding collections in Scott Hanselman's post - here
And last I suggest if you have a property that is list of something - in your case list of Player to use the plural form for the property name - Players.
EDIT
Try removing the "model." in front in the name. Make it like this "Players[0].Name". Since you only have one parameter in your Create Action method it should be fine.
I suggest you to use the helper #Html.EditorFor, so to do this you will create a partial view that will be used as template to inputs of the nested property. see the example:
Shared/EditorTemplates/Player.cshtml
#model Player
<div class="form-group">
#Html.HiddenFor(e => e.Id)
#Html.HiddenFor(e => e.TeamId)
<label class="control-label col-md-2" for="player">Player</label>
<div class="col-md-10">
#Html.TextBoxFor(e => e.Name, new { #class = "form-control text-box single-line", id = "player", name = "Player"})
</div>
</div>
Players form on Team view:
#Html.EditorFor(e => e.Player)
Instead of:
foreach (var p in Model.Player)
{
<div class="form-group">
#Html.Raw("<label class=\"control-label col-md-2\">" + p.ToString() + "</label>")
<div class="col-md-10">
#Html.Raw("<input class=\"form-control text-box single-line\" name=\"Player\" type-\"text\"")
</div>
</div>
}
See this article for more information about editor templates: Editor and display templates
I'm almost loosing my head trying to use the default asp.net MVC features to acomplish this mission. First of all i'll introduce what i've so far and what is my goal.
I have those two models.
public class ClassifiedViewModel
{
public int? ClassifiedId { get; set; }
public List<ClassifiedWishListViewModel> wishListModel {get; set;}
[Required]
public String UserId { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
}
public class ClassifiedWishListViewModel
{
public string KeyWord { get; set; }
public int CategoryId { get; set; }
public int SubCategoryId { get; set; }
}
Added more information
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ClassifiedId, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(model => model.UserId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-3">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.Description, new { #class = "form-control", rows = "10", cols = "60" })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" id="wishList">
</div>
<button type="button" class="btn" id="btnAddWish">
<span>Add item</span>
</button>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default" id="btnSave">Save</button>
</div>
</div>
</div>
}
script
$("#btnAddWish").on("click", function () {
$.get('/Classified/ClassifiedWishList', function (template) {
$("#wishList").append(template);
});
});
Controller - method to get partial view
[HttpGet]
public ActionResult ClassifiedWishList()
{
var model = new ClassifiedWishListViewModel();
var categoriesBO = new CategoriesBO();
var categories = categoriesBO.GetAllCategories();
model.Categories =
from c in categories
select new SelectListItem
{
Text = c.Name,
Value = c.CategoryId.ToString()
};
return PartialView("ClassifiedWishList", model);
}
And i'm trying to create a view to edit the Classified. But the classifiedwishList is a variable lenght property. So, i've created a partial view to that generates the ClassigiedWishListViewModel editor many times in the same form. But when i try to submit the entire form the ClassifiedWishListViewModel never gets binded to my original ClassifiedViewModel property.
Is there a way that i can acomplish that by simple using Asp.net MVC features? Every place i look suggests me to use a javascript framework and send all the information to the Controller through serialized jsons, but by using that i will loose all the validation that the Annotations performs (like required, maxlenght, others...) in the MVC, correct?
It's really frustrating that if you need a more dynamic page on Asp.net MVC you have to forget all the tools the framework offers you and start to just send and retrieve data through JSON communication.
Any kind of help is welcome! Thanks guys!
I'm new to MVC5/C# (fresh off a Silverlight project) and have a web application (not ASP.net) that I'm working on. I can't figure out how to get the value from a dropdown list that is populated from a ViewBag and not the model. Everything I've seen is geared towards ASP.NET and/or populating the dropdown from the model.
I have this model for shifts:
public class Shift
{
public Guid ShiftID { get; set; }
public string AreaOfOperation { get; set; }
public string UserName { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
And this for AreaOfOperations:
public class AreaOfOperations
{
public Guid AreaOfOperationsID { get; set; }
public String AreaOfOperation { get; set; }
public bool InUse { get; set; }
}
The relevant controller code, which populates the view nicely with a working dropdown:
public ActionResult Create(DateTime? datetime)
{
List<AreaOfOperations> list = db.AreaOfOperations.Where(i => i.InUse == true).OrderBy(aoo => aoo.AreaOfOperation).ToList();
ViewBag.DropDownAOOs = new SelectList(list, "AreaOfOperationsID", "AreaOfOperation");
Shift shift = new Shift();
shift.ShiftID = Guid.NewGuid();
shift.StartTime = DateTime.Now;
shift.UserName = User.Identity.Name;
return View(shift);
}
// POST: Shifts/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ShiftID,AreaOfOperations,UserName,StartTime")] Shift shift)
{
try
{
if (ModelState.IsValid)
{
shift.ShiftID = Guid.NewGuid();
db.Shifts.Add(shift);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(shift);
}
And my view:
#model CRMgr5.Models.Shift
#{
ViewBag.Title = "Start Shift";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Shift</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.AreaOfOperations, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("AreaOfOperation", ViewBag.DropDownAOOs as SelectList, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.UserName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.UserName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.UserName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StartTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StartTime, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StartTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input id="btnStartShift" type="submit" value="Start Shift" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Any help would be greatly appreciated. Thanks.
In the drop down list you named your select as "AreaOfOperation" but the model property is called "AreaOfOperations." Hence the binder will not be able to bind it.
As someone here already suggested you should use strongly typed html helpers such as DropDownListFor:
#Html.DropDownListFor(m => m.AreaOfOperations, ViewBag.DropDownAOOs as SelectList)
You did it for the label not sure why you opted not to use it when generating a drop down list?
I just recreated the whole thing and it worked fine
I removed the s of AreaOfOperations in your Bind Attribute
[Bind(Include = "ShiftID,AreaOfOperation(s),UserName,StartTime")]
As far as i know, you can remove this parameter attribute alltogether.
This is only used when you only want to bind to certain Attributes of your view model.
However there was one mistake: you have to repopulate the Select List if your ModelState is not valid. Otherwise your
return View(shift);
does not have the data to render a new SelectList.
Another approach is that you put the data in your ViewModel and initialize it in the default constructor. Then you dont have to worry about the data or casting.