Mulder2008
Mulder2008

Reputation: 181

DropDownListFor failed in partial view due to expression

I'm using Visual Studio 2015 community edition for ASP.net MVC project testing, Here is my simple model:

public class TestModel
{
    [Required]
    public string Name { get; set; }

    [UIHint("EnumList")]
    public TestState State { get; set; }
}

public enum TestState
{
    eNone,
    eNew,
    eFinished,
}

Here is a generic html - EnumList.cshtml

@model Enum

@Html.DropDownListFor(m => m, Enum.GetValues(Model.GetType()).OfType<Enum>()
    .Select(m =>
    {
        string enumVal = Enum.GetName(Model.GetType(), m);
        return new SelectListItem()
        {
            Selected = (Model.ToString() == enumVal),
            Text = enumVal,
            Value = enumVal
        };
    })
)

it works fine if below is used in Test view which accepts TestModel as model.

@Html.EditorForModel()

However when I try to use above EnumList.cshtml via specific action as below:

@Html.Action("Enum", new { pState = Model.State });     <-- used in View html

// below action will be called by above method
[ChildActionOnly]
public PartialViewResult Enum(TestState? pState)
{
    return PartialView("~/Views/Shared/EditorTemplates/EnumList.cshtml", pState.HasValue ? pState.Value : TestState.eNone);
}

I got following errors:

Value cannot be null or empty. Parameter name: name

below is from the stack trace:

[ArgumentException: Value cannot be null or empty. Parameter name: name] System.Web.Mvc.Html.SelectExtensions.SelectInternal(HtmlHelper htmlHelper, ModelMetadata metadata, String optionLabel, String name, IEnumerable1 selectList, Boolean allowMultiple, IDictionary2 htmlAttributes) +589 System.Web.Mvc.Html.SelectExtensions.DropDownListFor(HtmlHelper1 htmlHelper, Expression1 expression, IEnumerable1 selectList, String optionLabel, IDictionary2 htmlAttributes) +95 System.Web.Mvc.Html.SelectExtensions.DropDownListFor(HtmlHelper1 htmlHelper, Expression1 expression, IEnumerable`1 selectList) +60 ASP._Page_Views_Shared_EditorTemplates_EnumList_cshtml.Execute() in c:\visual studio 2015\Projects\Test\Test1\Views\Shared\EditorTemplates\EnumList.cshtml:3 System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +197 System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +105

It works if above DropDownListFor() is called directly in Test view with changes like "m => m.State", so I believe this error to do with "m => m", but what is the correct expression then?

thanks in advance.

Upvotes: 0

Views: 411

Answers (1)

Alex Art.
Alex Art.

Reputation: 8781

First of all your usage of DropDownListFor is wrong (from the MSDN description of DropDownListFor helpers ):

Returns an HTML select element for each property in the object that is represented by the specified expression using the specified list items.

You are using it for the model itself and not for model properties!

Explanation why your approach works with view which accepts TestModel as model and doesn't work as a separate partial view:

By default editor templates generate id and name properties for it's inputs as a combination of parent prefix(see examples) with the current model property name. For example when you use TestModel as the main model for your view and the use @Html.EditorForModel(), the name and the id of you select will be State :

<select name="State" id="State">....

If for example your TestModel is a property of ParentTestModel:

public class ParentTestModel
{
    public TestModel TestModelProperty {get;set;}
}

Then your editor template will be rendered as

<select name="TestModelProperty.State" id="TestModelProperty_State">...

When you render a template as a partial view with Enum as model both

  1. parent prefix is null - in general partial views don't preserve those prefixes and in this particular case there is no prefix at all because its a top model and
  2. property name is null - because of m => m expression which basically doesn't select any property

so FullHtmlFieldName is null. So this code (internal implementation of SelectList helpers ) throws an exception:

private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
            string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
            IDictionary<string, object> htmlAttributes)
        {
            string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
            if (String.IsNullOrEmpty(fullName))
            {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
            } ...

Upvotes: 0

Related Questions