2

This may be a duplicate, so I have indicated where my reading from this site has allowed me some progress...

I have a model defined as follows:

public enum RequestType
{
    [Display(Name = "Lovely Cold Beer")]
    Beer = 0,
    [Display(Name = "Warm Tea")]
    Tea = 1,
    [Display(Name = "Milky Coffee")]
    Coffee= 2
}

Based on the URL, I have a variable that will be used to automatically select the appropriate list item, e.g.

http://example.com/Request/Tea

will do this in the controller...

ViewBag.RequestType = RequestType.Tea.ToString();
return View("Index");

In my view I have a variable to read this value back, which then displays appropriate content:

if (ViewBag.RequestType != null)
{
    reqType = Enum.Parse(typeof(RequestType), ViewBag.RequestType);
}

In this view I create a drop down list using:

@Html.EnumDropDownListFor(model => model.RequestType, htmlAttributes: new { @onchange = "YadaYada();" })

This renders the list using the Display Name values defined for each Enum. What I need is to automatically select the appropriate list item when the page is rendered, that matches the value of reqType.

From my research I see that I can pass in the variable like so:

@Html.EnumDropDownListFor(model => model.RequestType, reqType.ToString(), htmlAttributes: new { @onchange = "YadaYada();" })

But this creates a new list item containing the enum value and not the display name, e.g.

Tea <-- This should not be created, instead 'Warm Tea' should be selected
Lovely Cold Beer
Warm Tea
Milky Coffee

My entire approach may be wrong as I'm new to MVC, but I'd welcome advice to fix it please! I don't understand why in the controller, using ToString on the enum value creates a different outcome to doing the same in the EnumDropDownListFor method.

EvilDr
  • 8,943
  • 14
  • 73
  • 133
  • You just need to set the value of the property. If your model property is `model.RequestType = RequestType.Tea`, then the 2nd option will be selected (model binding works by binding to the value of your property) –  Mar 04 '16 at 21:14
  • 1
    And as a side note, the second parameter in `@Html.EnumDropDownListFor(model => model.RequestType, reqType.ToString(), htmlAttributes ....)` add an `optionLabel` - i.e. its adds `` and its used is to create a `null` option useful for validation (normally its something like `"Please select"`). It has nothing to do with selecting an option. –  Mar 05 '16 at 11:25
  • @StephenMuecke thank you for bringing some clarity. As this is a contact page, I wasn't actually passing in a model to the view (only using the model in the `HttpPost` method that collects the data. Perhaps this is where I am going wrong? Should I instantiate a new model in the controller, set the `RequestType` property, then pass to the view? – EvilDr Mar 07 '16 at 09:12
  • 1
    Absolutely. You should always pass a model to the view (even if its just a default instance) –  Mar 07 '16 at 09:14
  • Works like a DREAM. I don't suppose you can post an answer that I can accept to help others in the future please? Thank you very much. – EvilDr Mar 07 '16 at 16:24
  • That's actually quite a common problem (one of many when dealing with passing data back to views), I wrote a detailed post that has a detailed, step-by-step instruction on how to use `EnumDropDownListFor`: http://nimblegecko.com/aspnetmvc-dropdowns-with-enums/ – Art Mar 10 '16 at 09:38

2 Answers2

1

The second parameter (reqType.ToString()) of your EnumDropDownListFor() method is using the overload which adds an optionLabel (an option with a null value used for validation). It does not set the value of the selected option.

Model binding features of MVC work by binding to your property and since the default value of your RequestType is "Beer" , then that option will be selected.

You need to set the value of the property in the model before you pass the model to the view, for example (assumes you have a specific route for /Request/{request})

public ActionResult Request(RequestType request)
{
    var model = new MyModel
    {
        RequestType = request
    };
    return View(model);
}
  • Just a quick question (as I am using your approach)... If (for some reason) the selected item needed to change from that value that was set in the controller, how do you initiate a change in the selected item? I'm used to using webforms where all the processing is done in code-behind. – EvilDr Mar 08 '16 at 09:03
  • Or, should *all* of this logic be done in the controller, well before it is passed to the view? – EvilDr Mar 08 '16 at 09:04
  • 1
    Do it in the controller before you render the view where possible, but if it needs to be done in the view (e.g. reacting to some client side event) then you can use javascript/jquery - e.g. `$)'#RequestType').val('Tea');` –  Mar 08 '16 at 09:08
  • Great, thank you again for your clarity on this answer. – EvilDr Mar 08 '16 at 09:21
0

You must use html extensions. Example,

 public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
    {
        if (expression.Body.NodeType == ExpressionType.Call)
        {
            MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
            string name = GetInputName(methodCallExpression);
            return name.Substring(expression.Parameters[0].Name.Length + 1);

        }
        return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
    }

    private static string GetInputName(MethodCallExpression expression)
    {
        // p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw...
        MethodCallExpression methodCallExpression = expression.Object as MethodCallExpression;
        if (methodCallExpression != null)
        {
            return GetInputName(methodCallExpression);
        }
        return expression.Object.ToString();
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) where TModel : class
    {
        string inputName = GetInputName(expression);
        var value = htmlHelper.ViewData.Model == null
            ? default(TProperty)
            : expression.Compile()(htmlHelper.ViewData.Model);

        return htmlHelper.DropDownList(inputName, ToSelectList(typeof(TProperty), value.ToString()), htmlAttributes);
    }
  • 2
    I've been reading MSDN for about two hours looking at stuff like this. An example of how to utilize that in my own code would be beneficial please... – EvilDr Mar 04 '16 at 16:11
  • You need HtmlExtensions, there are many example.http://blogs.msdn.com/b/stuartleeks/archive/2010/05/21/asp-net-mvc-creating-a-dropdownlist-helper-for-enums.aspx – Gurkan Minaz Mar 04 '16 at 16:23
  • 1
    You do not need any extension method. The built in `EnumDropDownListFor()` does this all out of the box. –  Mar 05 '16 at 00:07