2

I am looking for a way to bind an enum in my Model to a dropdownlist. I found this post and used the code from the 2nd answer and it works great in creating the dropdownlist. However, when I submit my form, it always passes the model back with the first value of the enumeration.

Enumeration (this is contained in my Model):

public LayoutType Layout;
public enum LayoutType
{
    Undefined = 0,
    OneColumn = 1,
    TwoColumn = 2,
    ThreeColumn = 3
}

Html Helper Methods:

private static Type GetNonNullableModelType(ModelMetadata modelMetadata)
    {
        Type realModelType = modelMetadata.ModelType;

        Type underlyingType = Nullable.GetUnderlyingType(realModelType);
        if (underlyingType != null)
        {
            realModelType = underlyingType;
        }
        return realModelType;
    }

    private static readonly SelectListItem[] SingleEmptyItem = new[] { new SelectListItem { Text = "", Value = "" } };

    public static string GetEnumDescription<TEnum>(TEnum value)
    {
        FieldInfo fi = value.GetType().GetField(value.ToString());

        DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

        if ((attributes != null) && (attributes.Length > 0))
            return attributes[0].Description;
        else
            return value.ToString();
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression)
    {
        return EnumDropDownListFor(htmlHelper, expression, null);
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
    {
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        Type enumType = GetNonNullableModelType(metadata);
        IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>();

        IEnumerable<SelectListItem> items = from value in values
                                            select new SelectListItem
                                            {
                                                Text = GetEnumDescription(value),
                                                Value = value.ToString(),
                                                Selected = value.Equals(metadata.Model)
                                            };

        // If the enum is nullable, add an 'empty' item to the collection
        if (metadata.IsNullableValueType)
            items = SingleEmptyItem.Concat(items);

        return htmlHelper.DropDownListFor(expression, items, htmlAttributes);
    }

View:

@Html.EnumDropDownListFor(model => model.Layout)

On rendering the view, the dropdownlist is fully populated as expected and the right value is selected. But when I submit a POST and pass the value back to my controller, my value for Model.Layout is always "Undefined". Any help would be greatly appreciated! Thanks,

Community
  • 1
  • 1
ntsue
  • 2,325
  • 8
  • 34
  • 53
  • Why couldn't you have just created a `SelectList` based on the enum? Or do away with the enum and just create the `SelectList`? That way you can just use the out of the box helpers and model binding would be stress free :) – Mathew Thompson May 16 '12 at 20:13
  • 1
    Have you tried this? http://stackoverflow.com/questions/4656758/mvc3-razor-dropdownlistfor-enums (your code looks different) – Leon Cullens May 16 '12 at 20:17

2 Answers2

1

If you are using VB.Net - MVC4- Razor This answer is almost same as Frigik( Thanks Frigik)

In your model create two fields one is the field which will hold the SelectedItem(here it is TagType of string) and the second is the drop down values (TagTypeList)

Model Class- Tag.vb

Imports System.Web.Mvc

Private _tagType As String
Private _tagTypeList As List(Of SelectListItem)

Public Property TagType() As String
        Get
            Return _tagType
        End Get
        Set(ByVal value As String)
            _tagType = value
        End Set
End Property

Public Property TagTypeList() As List(Of SelectListItem)
        Get
            Return _tagTypeList
        End Get
        Set(value As List(Of SelectListItem))
            _tagTypeList = value
        End Set
    End Property

'In the constructor

Sub New()
        TagTypeList = CommonUtility.LoadDropDownByName("TAGTYPE")
End Sub

In CommonUtility class- CommonUtility.vb

Imports System.Web.Mvc
Imports System.Collections.Generic


Public Shared Function LoadDropDownByName(ByVal DropDownName As String) As List(Of SelectListItem)
        Dim dt As DataTable
        Dim ds As DataSet
        Dim results As List(Of SelectListItem) = Nothing
        Try
            ds = obj.LoadDropDown(DropDownName)   'Pass the dropdown name here and get the values from DB table which is - select ddlId, ddlDesc from <table name>
            If Not ds Is Nothing Then
                dt = ds.Tables(0)
                If (dt.Rows.Count > 0) Then
                    results = (From p In dt Select New SelectListItem With {.Text = p.Item("ddlDesc").ToString(), .Value = p.Item("ddlId").ToString()}).ToList()
                End If
            End If
        Catch ex As Exception
        End Try
        Return results
    End Function

In the view

@Html.DropDownListFor(Function(x) x.TagType, Model.TagTypeList, "", New With {.id = "ddlTagType",.class = "dropDown", .style = "width: 140px"})

Here the 3rd parameter(optional) as empty which will insert the 1st item as empty in the drop down. The 1st parameter is the selectedItem which helps to populate once the value is already present in DB table.

Hope this helps who are using VB.Net

Jyo
  • 167
  • 1
  • 4
0

In your model create two fields one should hold selected item (SelectedItem) second drop down values (GroupNames)

  public string SelectedItem { get; set; }

  [Required]
  public IEnumerable<SelectListItem> GroupNames { get; set; }

Then in constructor fill in GroupNames for example

GroupNames = layer.GetAll().Select(p => new SelectListItem
                                                        {
                                                            Value = p.Id.ToString(CultureInfo.InvariantCulture),
                                                            Text = p.Name,
                                                            Selected = p.Id == someEntity.someFK
                                                        });
            SelectedItem = GroupNames.Where(p => p.Selected).Select(p => p.Value).Single();

Then in view render Drop down like that :

 @Html.DropDownListFor(x => x.SelectedItem, Model.GroupNames)

That is all selected value should come with model in SelectedItem field.

Frigik
  • 449
  • 1
  • 3
  • 13