Html.EnumDropdownListFor: Showing a default text - c#

In my view I have a enumdropdownlist (a new feature in Asp.Net MVC 5.1).
#Html.EnumDropDownListFor(m => m.SelectedLicense,new { #class="form-control"})
If I execute the above code I get dropdownlist for my following enum.
public enum LicenseTypes
{
Trial = 0,
Paid = 1
}
but by default I want my dropdownlist to have a value(custom text)
and this is what I tried
#Html.EnumDropDownListFor(m => m.SelectedLicense,"Select a license" ,new { #class="form-control"})
but now the problem is when i run it, my dropdownlist looks like this
So, the default text I want to show doesn't appear by default.
If a user selects "select a license" and tries to submit the form, it does show an error saying "select a license" but it doesn't show as default text.
Something i need to change?
Ps: The image is the screenshot of the page when it loads. By default it'll show Trial as selected option.

Try to change the Index of LicenseTypes start from 1 not 0 like below:
public enum LicenseTypes
{
Trial = 1,
Paid = 2
}
Then you can use Range attribute to validate the selected license type like below:
public class YourViewModel
{
//Other properties
[Range(1,int.MaxValue,ErrorMessage = "Select a correct license")]
public LicenseTypes LicenseTypes { get; set; }
}
Finally, in your view:
#Html.EnumDropDownListFor(m => m.LicenseTypes,"Select a license",new { #class = "form-control"})
#Html.ValidationMessageFor(m => m.LicenseTypes)

By the time your EnumDropDownListFor is rendered SelectedLicense already has the default value for the type, which is 0.
Just change the type of your SelectedLicense property to a nullable enum, like so:
public LicenseTypes? SelectedLicense { get; set; }
This also allows you to continue using the Required attribute, which I think is significantly cleaner. The Required attribute will not allow a null response, so even though your model allows nulls, the form will not.

I have an enum:
public enum Sex
{
Male,
Female
}
In my model I have:
[DisplayName("Sex")]
[Required]
public Sex? Sex { get; set; }
An in the view:
#Html.EnumDropDownListFor(model => model.Sex, "Select sex", new { #class = "form-control", type = "text"})
By this I have a dropdown with default option "Select sex", but validation accepts only options provided by enum ("Male" and "Female").
In MVC3 (without EnumDropDownListFor) I used in model:
[DisplayName("Sex")]
[Required(AllowEmptyStrings=false)]
public Sex? Sex { get; set; }
Sex = null;
Sexes = Repository.GetAutoSelectList<Sex>("");
In view:
#Html.DropDownListFor(model => model.Sex, Model.Sexes, new { #class = "form-control", type = "text" })

The ViewModel class needs to have the default value set on the enum property for it to be the default selected
public
public class Test
{
public Cars MyCars { get; set; }
public enum Cars
{
[Display(Name = #"Car #1")]
Car1 = 1,
[Display(Name = #"Car #2")]
Car2 = 2,
[Display(Name = #"Car #3")]
Car3 = 3
}
}
Controller:
public class EnumController : Controller
{
// GET: Enum
public ActionResult Index()
{
var model = new Test {MyCars = Test.Cars.Car3}; // set default value
return View(model);
}
[HttpPost]
public ActionResult Index(Test model)
{
.....
}
}
View:
#Html.BeginForm()
{
<div class="panel bg-white">
<div class="panel-header fg-white">
Enums
</div>
<div class="panel-content">
<div class="input-control select size3">
#Html.EnumDropDownListFor(model => model.MyCars)
</div>
</div>
<input type="submit" class="button success large" />
</div>
}

Am I a bit late ?
Changing the values of the enum type is not very satisfying.
Neither is changing the model property to render it nullable and then add a [Required] attribute to prevent it to be nullable.
I propose to use the ViewBag to set the default selected value of the dropdown.
The line 4 of the controller just bellow is the only important one.
EDIT : Ah... newbies... My first idea was to use ModelState.SetModelValue because my newbie instinct prevented me to simply try to set the desired value in the ViewBag since the dropdown was binded to the model. I was sure to have a problem: it would bind to the model's property, not to the ViewBag's property. I was all wrong: ViewBag is OK. I corrected the code.
Here is an example.
Model:
namespace WebApplication1.Models {
public enum GoodMusic {
Metal,
HeavyMetal,
PowerMetal,
BlackMetal,
ThashMetal,
DeathMetal // . . .
}
public class Fan {
[Required(ErrorMessage = "Don't be shy!")]
public String Name { get; set; }
[Required(ErrorMessage = "There's enough good music here for you to chose!")]
public GoodMusic FavouriteMusic { get; set; }
}
}
Controller:
namespace WebApplication1.Controllers {
public class FanController : Controller {
public ActionResult Index() {
ViewBag.FavouriteMusic = string.Empty;
//ModelState.SetModelValue( "FavouriteMusic", new ValueProviderResult( string.Empty, string.Empty, System.Globalization.CultureInfo.InvariantCulture ) );
return View( "Index" );
}
[HttpPost, ActionName( "Index" )]
public ActionResult Register( Models.Fan newFan ) {
if( !ModelState.IsValid )
return View( "Index" );
ModelState.Clear();
ViewBag.Message = "OK - You may register another fan";
return Index();
}
}
}
View:
#model WebApplication1.Models.Fan
<h2>Hello, fan</h2>
#using( Html.BeginForm() ) {
<p>#Html.LabelFor( m => m.Name )</p>
<p>#Html.EditorFor( m => m.Name ) #Html.ValidationMessageFor( m => m.Name )</p>
<p>#Html.LabelFor( m => m.FavouriteMusic )</p>
<p>#Html.EnumDropDownListFor( m => m.FavouriteMusic, "Chose your favorite music from here..." ) #Html.ValidationMessageFor( m => m.FavouriteMusic )</p>
<input type="submit" value="Register" />
#ViewBag.Message
}
Without the "ModelState.SetModelValue or ViewBag.FavouriteMusic = string.Empty" line in the model Index action the default selected value would be "Metal" and not "Select your music..."

Related

ViewModel Properties in Query String are not bound when arriving at Controller

I'm working in an ASP.net MVC application, and I have a table of products as shown in the screenshot:
I would like the ability to filter that table of products, and I'd like the filtering to happen via the query string params (as a GET) so that the URL can be shared.
The ViewModel for the page is like this:
public class InventoryReportViewModel
{
public SearchViewModel Search { get; set; } // 2 string props [Type and Term]
public IEnumerable<ProductViewModel> Products { get; set; }
public PaginationViewModel Pagination { get; set; } // 3 int props [currentPage, recordsPerPage, totalRecords]
}
I'm using Razor helpers to draw the filter inputs, like this:
#Html.EditorFor(m => m.Search.Term, new { htmlAttributes = new { #class = "form-control" } })
And also I've set up my form to use GET like so:
#using (Html.BeginForm("Inventory", "Report", FormMethod.Get))
{
// form elements
}
My ReportController.cs has the following method that is relevant to my question here:
public ActionResult Inventory(string SearchTerm, string SearchType, int page = 1)
{
var viewModel = _reportService.GetProducts(page, SearchTerm, SearchType);
return View(viewModel);
}
When I pass a Search term, and click the Filter Results button, I do arrive at my Controller method above, but the SearchTerm and SearchType are null.
I know how to "hack" this to work, for example, if I do this:
<input type="text" name="SearchTerm" class="form-control"/>
Then the search term I input would be picked up by the Controller, but is there no other way?
since you already made a viewmodel for Search
public SearchViewModel Search { get; set; }
you just need to pass it to the controller like this
public ActionResult Inventory(SearchViewModel Search, int page = 1
{
var viewModel = _reportService.GetProducts(page, Search.Term, Search.Type);
return View(viewModel);
}
you were getting null because the textboxes were named as Search.Term that is why it was not matching the parameters.
The form should be post
#using (Html.BeginForm("Inventory", "Report", FormMethod.Post))
{
// form elements
}
This can also be cleaner:
#Html.EditorFor(m => m.Search.Term, new { htmlAttributes = new { #class = "form-control" } })
to
#Html.EditorFor(m => m.Search.Term, new { #class = "form-control" } )
Another question,
In the razor view, do you have a model specified on the first line?

How can I capture the #Html.DropDownListFor selected value?

I am using MVC5, Razor, Entity Framework, C#. I am trying to pass a value of a dorpdown list using a link.
my model is
public class TestVM
{
public string TheID { get; set; }
}
I am loading an enum into a IEnumerable<SelectListItem>.
My enum is
public enum DiscountENUM
{
SaleCustomer,
SaleCustomerCategory,
SaleProduct,
SaleProductCategory,
SaleCustomerAndProduct,
SaleCustomerAndProductCategory,
SaleCustomerCategoryAndProductCategory,
PurchaseVendor,
PurchaseVendorAndProduct,
PurchaseVendorAndProductCategory,
PurchaseProduct,
PurchaseProductCategory,
Unknown
}
I am using the index method of the home controller
public ActionResult Index()
{
ViewBag.ListOfDiscounts = SelectListDiscountENUM();
TestVM d = new TestVM();
return View(d);
}
Where I load the ListOfDiscounts using:
private IEnumerable<SelectListItem> SelectListDiscountENUM()
{
List<SelectListItem> selectList = new List<SelectListItem>();
var listOfEnumValues = Enum.GetValues(typeof(DiscountENUM));
if (listOfEnumValues != null)
if (listOfEnumValues.Length > 0)
{
foreach (var item in listOfEnumValues)
{
SelectListItem sVM = new SelectListItem();
sVM.Value = item.ToString();
sVM.Text = Enum.GetName(typeof(DiscountENUM), item).ToString();
selectList.Add(sVM);
}
}
return selectList.OrderBy(x => x.Text).AsEnumerable();
}
My create method which is called from the view is
public ActionResult Create(TestVM d, string TheID)
{
return View();
}
My Index view is
#model ModelsClassLibrary.Models.DiscountNS.TestVM
<div>#Html.ActionLink("Create New", "Create", new { TheID = Model.TheID})</div>
<div>
#Html.DropDownListFor(x => x.TheID, #ViewBag.ListOfDiscounts as IEnumerable<SelectListItem>, "--- Select Discount Type ---", new { #class = "form-control" })
</div>
The problem is in the following line in the View
<div>#Html.ActionLink("Create New", "Create", new { TheID = Model.TheID })</div>
I have tried adding a model with the name of the field as "TheID"... no luck. Also, added a string field in the parameter, no luck. I looked at the FormControl object, and there was nothing in it either! I suspect something has to be added at the Route level in the helper, but I don't know what.
Model.TheID is always null. Even when I select an item in the DropDownListFor.
Does anyone have an idea how I can capture the select value of the DropDownListFor and send it into the Html.ActionLink TheID?

Set selected index of dropdown to zero after form submit in ASP.NET MVC

I am bit to new asp.net mvc and using aps.net mvc 5. I have create the below dropdown using html helpers in aps.net mvc. When i submit(post back) the form i want to set the selected index to zero. Here i am using a optionLabel "--select--". I want to set the selected value to that one ("--select--") after post back. How to achieve this. Please help. Thank you.
#Html.DropDownListFor(model => model.TestCategory, new SelectList(#ViewBag.TestCategories, "value", "text"), "-- Select --", new { #class = "form-control input-sm"})
Controller Code
[HttpGet]
public ActionResult Index()
{
var model = new LaboratoryViewModel {
medicaltestlist = new List<MedicalTest>()
};
PopTestCategory();
PopEmptyDropdown();
return View(model);
}
[HttpPost]
public ActionResult Index(LaboratoryViewModel labvm)
{
var test = PopMedicalTests().Where(x => x.TestSerial == Convert.ToInt32(labvm.TestCode)).FirstOrDefault();
if (labvm.medicaltestlist == null)
labvm.medicaltestlist = new List<MedicalTest>();
if(!labvm.medicaltestlist.Any(x=> x.TestSerial == test.TestSerial))
labvm.medicaltestlist.Add(test);
labvm.TestCategory = "";
PopTestCategory();
return View(labvm);
}
public void PopTestCategory()
{
var categorylist = new List<DropDownItem>
{
new DropDownItem{value="Medical",text="Medical"},
new DropDownItem{value="Animal",text="Animal"},
new DropDownItem{value="Food",text="Food"},
new DropDownItem{value="Water",text="Water"}
};
ViewBag.TestCategories = categorylist;
}
public class DropDownItem
{
public int id { get; set; }
public string value { get; set; }
public string text { get; set; }
}
You return the view in you post method so if you selected (say) Animal then that value will be selected when you return the view because the html helpers use the values from ModelState, not the model property. Setting labvm.TestCategory = ""; has no effect. The correct approach is to follow the PRG pattern and redirect to the GET method, however you can make this work by calling ModelState.Clear(); before setting resetting the value of TestCategory although this will clear all ModelState properties and errors and may have other side effects.
Side note: You DropDownItem class seems unnecessary. MVC already has a SelectListItem class designed to work with dropdownlists, and in any case you can replace all the code in your PopEmptyDropdown() method with
ViewBag.TestCategories = new SelectList(new List<string>() { "Medical", "Animal", "Food", "Water" });
and in the view
#Html.DropDownListFor(m => m.TestCategory, (SelectList)#ViewBag.TestCategories, "-- Select --", new { #class = "form-control input-sm"})
If you set the "value" attribute of the top item in the drop down list to something and then pass back a model containing that for the bound property it should work?

Getting selected Index from DropDownListFor MVC

I am trying to get the index of selected item from a dropdownlist in my mvc view. I populated the content of the ddl using enums and the code looks something like this:
#Html.DropDownListFor(x => x.SelectedStateId, Enum.GetNames(typeof(BTSWeb.Models.States)).Select(e => new SelectListItem { Text = e }),"--Market--",new { style = "width: 80px;font-size:85%;border-radius: 6.5px 6.5px 6.5px 6.5px"})
Just so you guys know the enum is defined in my model class:
public enum States { ANY, FL, TX, GA, NE };
My controller looks something like below:
public ActionResult Index()
{
Debug.WriteLine("hello");
var model = new DropDownModel();
return View(model);
}
[HttpPost]
public ActionResult Index(DropDownModel model)
{
// Get the selected value
int stateId = model.SelectedStateId;
int paymentTypeId = model.SelectedBillTypeId;
BillingToolInterface_1.check.a = stateId;
BillingToolInterface_1.check.b = paymentTypeId;
//System.Diagnostics.Debug.WriteLine("hello");
Debug.WriteLine(stateId.ToString());
return View();
}
public ActionResult About()
{
return View();
}
I am printing the value of selectedstateid on debug console after the submit button in my view is pressed. But it is displaying "0" for every selected value. It should display 1,2,3 and so on while corresponding value is selected in my state drop down.
PS: ddl -> dropdownlist
The problem is that when you create your select list, you're only assigning a text value.
#Html.DropDownListFor(x => x.SelectedStateId, Enum.GetNames(typeof(BTSWeb.Models.States)).Select(e => new SelectListItem { Text = e }),"--Market--",new { style = "width: 80px;font-size:85%;border-radius: 6.5px 6.5px 6.5px 6.5px"})
When you run that kind of code in your browser, you'll see:
When you modify your code like this:
#Html.DropDownListFor(x => x.States, Enum.GetNames(typeof(WebApplication1.Controllers.States)).Select(e => new SelectListItem { Text = e, Value = ((int)Enum.Parse(typeof(WebApplication1.Controllers.States), e)).ToString() }), "--Market--", new { style = "width: 80px;font-size:85%;border-radius: 6.5px 6.5px 6.5px 6.5px" })
Now, you're also passing the value of the enum:
You'll notice on how that rendered HTML of your select list changes? You have a value property right now.
I've written a demo project which explains it (I tried to stay as close as possible to your code).
First the enumeration:
public enum States { ANY, FL, TX, GA, NE };
Then the model:
public class DropDownModel
{
public States States = new States();
public int SelectedStateID { get; set; }
}
And the controller:
public class DefaultController : Controller
{
public ActionResult Index()
{
var model = new DropDownModel();
return View(model);
}
[HttpPost]
public ActionResult Index(DropDownModel model)
{
// Get the selected value
return View();
}
}
Now there's the view:
#using (Html.BeginForm("Index", "Default", FormMethod.Post))
{
#Html.DropDownListFor(x => x.SelectedStateID, Enum.GetNames(typeof(WebApplication1.Controllers.States)).Select(e => new SelectListItem { Text = e, Value = ((int)Enum.Parse(typeof(WebApplication1.Controllers.States), e)).ToString() }), "--Market--", new { style = "width: 80px;font-size:85%;border-radius: 6.5px 6.5px 6.5px 6.5px" })
<input type="submit" name="SaveButton" value="Save" />
}
And basiccly, that's all their is.
Now, run your application, and in the dropdownlist select the 3rd element (This should be 'TX').
When you look at the properties of your model passed into your controller again, you'll notfice the following:
You see that States has the INCORRECT value, but that the selected index property has 2. This is correct, because of the following:
In the declaration of your dropdownlist, you bind it to SelectedStateId.
Also the value of 2 is correct because an enumeration with no values starts at 0 for the first element and adds 1 for every following element.
When you want the state now, you can convert your integer value (2) to the enumeration value.
So, a long post but I hope it helped.

All Dropdown Items have ZERO index MVC3

I have populated a dropdown list with values from Database Table. The list gets populated with correct table data but all values have ZERO index in the list. Here is the code to fill dropdown list:
//Get
public ActionResult NewBooking()
{
var db = new VirtualTicketsDBEntities2();
IEnumerable<SelectListItem> items = db.Attractions
.ToList()
.Select(c => new SelectListItem
{
Value = c.A_ID.ToString(),
Text = c.Name
});
ViewBag.Attractions = items;
return View();
}
And on Dropdown View Page:
<div class="editor-label">
#Html.LabelFor(model => model.Attraction)
</div>
<div class="editor-field">
#Html.DropDownList("Attractions")
</div>
For example if table have 3 values A,B, and C. These values are appearing in dropdown list but when I get its selected index in POST request function, it always returns ZERO. Here is the POST submit function:
//Post
[HttpPost]
public ActionResult NewBooking(BookingView booking)
{
try
{
BookingManager bookingManagerObj = new BookingManager();
bookingManagerObj.Add(booking);
ViewBag.BookingSavedSucess = "Booking saved!";
return View("WelcomeConsumer","Home");
}
catch
{
return View(booking);
}
}
booking.Attraction is always ZERO even user selected greater than ZERO index item.
Any suggestions?
I would guess that it is because you are getting a collection of SelectListItems back and not an actual SelectList. Try something like:
<div class="editor-field">
#Html.DropDownListFor(model => model.Attraction, new SelectList(ViewBag.Attractions, "Value", "Text");
It's best not to use ViewBag, you should always use a ViewModel.
Say you have a ViewModel like this:
public class AttractionViewModel
{
public int AttractionId { get; set; }
public SelectList Attractions { get; set; }
}
and modify your view like this - I presume you already have a form in there, the relevant bit is the #Html.DropDownListFor(...) and making sure you have the full namespace to the ViewModel if you haven't already included it in the Views web.config file:
#model AttractionViewModel
#using(Html.BeginForm("NewBooking", "ControllerName"))
{
<div class="editor-label">
#Html.LabelFor(model => model.AttractionId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.AttractionId, Model.Attractions)
</div>
<input type="submit" value="Submit">
}
and modify your HttpGet like this:
//Get
public ActionResult NewBooking()
{
var db = new VirtualTicketsDBEntities2();
var items = db.Attractions.ToList();
var attractionIdDefault = 0;// default value if you have one
var vm = new AttractionViewModel {
AttractionId = attractionIdDefault,// set this if you have a default value
Attractions = new SelectList(items, "A_ID", "Name", attractionIdDefault)
}
return View(vm);
}
and create an HttpPost ActionResult like this:
// Post
public ActionResult NewBooking(AttractionViewModel vm)
{
var attractionId = vm.AttractionId; // You have passed back your selected attraction Id.
return View();
}
Then it should work.
I know that you have already selected your answer but here is an alternative way of doing what you did. When I started off with ASP.NET MVC I struggled with SelectListItem and found another way of populating my drop down list. I have stuck to this way ever since.
I always have a view model that I bind to my view. I never send through a domain model, always a view model. A view model is just a scaled down version of your domain model and can contain data from multiple domain models.
I have made some modifications to your code and tips, but like I mentioned, it's just an alternative to what you already have.
Your domain model could look like this. Try and give your property names some meaningful descriptions:
public class Attraction
{
public int Id { get; set; }
public string Name { get; set; }
}
You view model could look something like this:
public class BookingViewModel
{
public int AttractionId { get; set; }
public IEnumerable<Attraction> Attractions { get; set; }
// Add your other properties here
}
Do not have your data access methods in your controllers, rather have a service layer or repository expose this functionality:
public class BookingController : Controller
{
private readonly IAttractionRepository attractionRepository;
public BookingController(IAttractionRepository attractionRepository)
{
this.attractionRepository = attractionRepository;
}
public ActionResult NewBooking()
{
BookingViewModel viewModel = new BookingViewModel
{
Attractions = attractionRepository.GetAll()
};
return View(viewModel);
}
[HttpPost]
public ActionResult NewBooking(BookingViewModel viewModel)
{
// Check for null viewModel
if (!ModelState.IsValid)
{
viewModel.Attractions = attractionRepository.GetAll();
return View(viewModel);
}
// Do whatever else you need to do here
}
}
And then your view will populate your drop down like this:
#model YourProject.ViewModels.Attractionss.BookingViewModel
#Html.DropDownListFor(
x => x.AttractionId,
new SelectList(Model.Attractions, "Id", "Name", Model.AttractionId),
"-- Select --"
)
#Html.ValidationMessageFor(x => x.AttractionId)
I hope this helps.

Categories

Resources