1

I am showing an enum as dropdown list in my Edit view like this:

Inside Model

public enum PaymentType
{
    Self=1,
    Insurer=2,
    PrivateCompany=3

}
public PaymentType PaymentTypeSelected { get; set; }

Inside Controller

ViewBag.EnumList = Patient.PaymentType.Insurer.ToSelectList();
patient.PaymentTypeSelected=Patient.PaymentType.Insurer;

In View

@Html.DropDownListFor(m => m.PaymentTypeSelected, 
    ViewBag.EnumList as SelectList)

And an Extension Function

public static System.Web.Mvc.SelectList ToSelectList<TEnum>(this TEnum obj)
        where TEnum : struct, IComparable, IFormattable, IConvertible
{
    return new SelectList(Enum.GetValues(typeof(TEnum)).OfType<Enum>()
        .Select(x => new SelectListItem
        {
           Text = Enum.GetName(typeof(TEnum), x),
           Value = (Convert.ToInt32(x)).ToString()
        }), "Value", "Text");
    }

Generated HTML

<select data-val="true" 
        data-val-required="The PaymentTypeSelected field is required."
        id="ptype" name="PaymentTypeSelected">
    <option value="1">Self</option>
    <option value="2">Insurer</option>
    <option value="3">PrivateCompany</option>
</select>

It all looks okay to me, but the dropdown does not show default selected value (in my case Insurer). Can any one point out what I am doing wrong here?

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
None
  • 5,582
  • 21
  • 85
  • 170
  • What is `ToSelectList()`? –  Mar 03 '15 at 04:51
  • 1
    I suspect the problem is that the `value` attributes of the options are 1, 2 and 3 respectively and the property value evaluates to "Insurer". Try setting the `Value` property of the `SelectListItem` to the same as the `Name` property. –  Mar 03 '15 at 04:57
  • @StephenMuecke tried not working – None Mar 03 '15 at 05:09
  • Can you copy the HTML generated for the dropdown? – Ray Suelzer Mar 03 '15 at 05:15
  • Works for me (although I just simplified it to `ViewBag.EnumList = new SelectList(Enum.GetNames(typeof(PaymentType)));` –  Mar 03 '15 at 05:18
  • Just a wild guess. Try casting those "ints" to strings. – Ray Suelzer Mar 03 '15 at 05:22
  • I mean => m.PaymentTypeSelected.ToString(); – Ray Suelzer Mar 03 '15 at 05:23
  • You still generating the options values as the "1", "2" and "3" value (they need to be "Self", "Insurer" and "PrivateCompany" –  Mar 03 '15 at 05:23
  • @StephenMuecke no I made that update for Ray Suelzer only. – None Mar 03 '15 at 05:24
  • @StephenMuecke I have tried it as you said with value and text same. but it still not preselect anything :( – None Mar 03 '15 at 05:25
  • @Athul : Y dont you send the selected attribute with the option you want to be preselected? – Tushar Gupta Mar 03 '15 at 05:26
  • Then there is something else relevant that you are telling us because `ViewBag.EnumList = new SelectList(Enum.GetNames(typeof(PaymentType)));` works fine. –  Mar 03 '15 at 05:30

4 Answers4

3

Here it is as a working DotNetFiddle.

In short, you need to make sure that both the Value property of each SelectListItem and the PaymentTypeSelected property are pulling from the same Enum conversion. You were doing int for the Value and string for the PaymentTypeSelected. Change your extension to this and you're golden.

public static class MyExtensions
{
    public static SelectList ToSelectList<TEnum>(this TEnum obj) 
        where TEnum : struct, IComparable, IFormattable, IConvertible
    {
        var items = Enum.GetValues(typeof (TEnum))
                .OfType<Enum>()
                .Select(x => new { Text = x.ToString() })
                .Select(x => new SelectListItem
                {
                    Text = x.Text, 
                    Value = x.Text
                });

        return new SelectList(items, "Value", "Text");
    }
}
Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
  • Don't know why, But THAT WORKED :D – None Mar 03 '15 at 05:44
  • 1
    Good answer, ToString should be sufficient though, don't think GetName is needed: http://stackoverflow.com/a/694361/84206 – AaronLS Mar 03 '15 at 05:46
  • @AaronLS Good call. Also, do you think the transparent identifier is overkill? While it's DRY it might be tough to read later. – Shaun Luttin Mar 03 '15 at 05:50
  • 1
    @ShaunLuttin I don't have a strong opinion either way, It's tucked away inside the extension method. Variations of this approach I've used do camel case splitting, and/or look for a Description attribute, when setting the `Text` field for nicer display, but that's a whole other tangent. – AaronLS Mar 03 '15 at 06:08
2

As Stephen pointed out, this won't work if you use the DropDownListFor, as the model binding prefers the m=>m.SomeProperty over the constructor parameter.

You can use the SelectList constructor that takes a 4th object parameter to indicate the selected value: https://msdn.microsoft.com/en-us/library/dd492553(v=vs.118).aspx

// in controller we'll pass the desired selected value to extension method:
ViewBag.EnumList = Patient.PaymentType.Insurer.ToSelectList(Patient.PaymentType.Insurer);

// update method to support this parameter
public static System.Web.Mvc.SelectList ToSelectList<TEnum>(this TEnum obj, object selectedValue)
        where TEnum : struct, IComparable, IFormattable, IConvertible // correct one
    {
        return new SelectList(
            Enum.GetValues(typeof(TEnum)).OfType<Enum>()
                 .Select(x =>
                     new SelectListItem
                     {
                         Text = Enum.GetName(typeof(TEnum), x),
                         Value = (Convert.ToInt32(x)).ToString()
                     }
                 ) 
            ,"Value", "Text"
            ,(int)selectedValue); // pass selected value to SelectList constructor

    }

I've made one edit to add (int), which is somewhat of a dirty cast since we're coming from an object. This of course can be improved upon.

AaronLS
  • 37,329
  • 20
  • 143
  • 202
  • This seems MUCH better than my answer. +1 – Ray Suelzer Mar 03 '15 at 05:29
  • The 4th parameter is ignored if your binding to a model property. –  Mar 03 '15 at 05:32
  • @StephenMuecke You are right. So much for MSDN documentation. – AaronLS Mar 03 '15 at 05:37
  • @AaronLS please add a code sample for how you want to bind this to html.dropdown in the view – None Mar 03 '15 at 05:38
  • 1
    @Athul My intention was that you'd leave the view the same, but Stephen pointed out the problem. I believe your problem is you cast your enums to ints `Value = (Convert.ToInt32(x))` but your model property is still an enum(thus it takes the ToString of the enum by default). If you look at the HTML that would be generated by `Value = x.ToString()` instead then you'd see by default DropDownList uses the string representation of enums: http://stackoverflow.com/a/694361/84206 – AaronLS Mar 03 '15 at 05:42
0

Try this...

 @Html.DropDownListFor(m => ((int)m.PaymentTypeSelected).ToString(), ViewBag.EnumList as SelectList)
Ray Suelzer
  • 4,026
  • 5
  • 39
  • 55
  • Totally untested, just going off of memory here. But cast the PaymentTypeSelected to an int and then to a string so that it will match the selectlist string values which are the enums casted to ints and then strings. – Ray Suelzer Mar 03 '15 at 05:26
  • This won't work .Cannot convert type 'string' to 'int' – None Mar 03 '15 at 05:29
  • @RaySuelzer I think this approach just requires `(int)m.PaymentTypeSelected` no ToString needed. – AaronLS Mar 03 '15 at 05:30
  • Athul.. It's because my boxing is wrong. Do this: `((int)m.PaymentTypeSelected).ToString();` – Ray Suelzer Mar 03 '15 at 05:32
0

A SelectList can takes 4 arguments, where the last argument selectedValue is an object of whatever type is in your list. So you can try like the following:

ViewBag.yourEnumList= new SelectList(enumList, "Value", "Text", selectedValue);

Or, You can apply this in your extension function

Sarmin Akter
  • 120
  • 7