Html.EnumDropdownListFor: Showing a default text
Asked Answered
B

5

71

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 enter image description here 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.

Bogy answered 19/2, 2014 at 11:6 Comment(0)
A
90

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)
Argybargy answered 19/2, 2014 at 13:49 Comment(3)
that works but, then it will add Select a license as one of the enum value with id 0. I want to be able to show error message when user doesn't select one of the values from enum.Bogy
could do that but i'll leave it unaccepted for some time and see if someone can come with better solution, till then +1 for ur idea.Bogy
@probackpacker has the better answer, this option will not get caught in the validation handlingHypocorism
D
75

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.

Dekko answered 15/4, 2014 at 13:35 Comment(3)
I think this is the best answer for today, but is seems like a hack. The real fix should be within MVC to handle non-nullable Enums properly.Dieball
@jhilden, I think that MVC is handling the non-nullable Enum properly by doing exactly what you are telling it to do. In .NET enum variables are always initialized to the value 0. Let's suppose that you left the property type as a non-nullable Enum (LicenseTypes) and the form is posted with "Select a License" (the list item with no value) selected. Your SelectedLicense property would end up with the default value (Trial). If you just examined the model it would be indistinguishable from the user actually selecting the Trial value.Dekko
you are correct. I looked back at my other code and if we want an int to be required but have no default value then we do: [required] public int? NullableIntHere {get;set;}Dieball
N
16

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" })
Nutation answered 4/6, 2014 at 13:1 Comment(0)
G
11

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>
}
Giacopo answered 22/7, 2014 at 16:6 Comment(0)
L
0

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..."

Lindley answered 26/4, 2016 at 20:40 Comment(1)
@shim: It works with partial view called from the main view, but not in partial view called in AJAX. Partial views get a copy of the main view ViewBag. You do execute the Index action when a partial view is called by the main view. You do not when an AJAX request invokes a partial view. Index.cshtml:Lindley

© 2022 - 2024 — McMap. All rights reserved.