Showing posts with label MVC. Show all posts
Showing posts with label MVC. Show all posts

Wednesday, March 30, 2016

@Html.EnumDropDownListFor in MVC

For MVC v5.1 use Html.EnumDropDownListFor

        @Html.DropDownList("MyType", 
           Html.GetEnumSelectList(typeof(MyType)) , 
           "Select My Type", 
           new { @class = "form-control" })
    

For MVC v5 use EnumHelper

        @Html.DropDownList("MyType", 
           EnumHelper.GetSelectList(typeof(MyType)) , 
           "Select My Type", 
           new { @class = "form-control" })
    

For MVC >5 and lower

I rolled Rune's answer into an extension method:
        namespace MyApp.Common
        {
            public static class MyExtensions{
                public static SelectList ToSelectList(this TEnum enumObj)
                    where TEnum : struct, IComparable, IFormattable, IConvertible
                {
                    var values = from TEnum e in Enum.GetValues(typeof(TEnum))
                        select new { Id = e, Name = e.ToString() };
                    return new SelectList(values, "Id", "Name", enumObj);
                }
            }
        }
    
This allows you to write:
     ViewData["taskStatus"] = task.Status.ToSelectList();
    
by
using MyApp.Common

Alternatively:
Create a custom helper class and use Html.EnumDropDownListFor for all lower version MVC < 5.1 (i.e. 3.x, 4.x, 5.0). Here is the class code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Mvc.Html;
/// 
/// Class ENUM Helper
/// 
public static class EnumHelper
{
    /// 
    /// The single empty item
    /// 
    private static readonly SelectListItem[] SingleEmptyItem = new[] { new SelectListItem { Text = string.Empty, Value = string.Empty } };
    /// 
    /// Gets the ENUM description.
    /// 
    /// The type of the ENUM.
    /// The value.
    /// ENUM Description
    public static string GetEnumDescription(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();
        }
    }
    /// 
    /// ENUMS the drop down list for.
    /// 
    /// The type of the model.
    /// The type of the ENUM.
    /// The HTML helper.
    /// The expression.
    /// MVC Html String
    public static MvcHtmlString EnumDropDownListFor(this HtmlHelper htmlHelper, Expression> expression)
    {
        return EnumDropDownListFor(htmlHelper, expression, null);
    }
    /// 
    /// ENUMS the drop down list for.
    /// 
    /// The type of the model.
    /// The type of the ENUM.
    /// The HTML helper.
    /// The expression.
    /// The HTML attributes.
    /// MVC Html String
    public static MvcHtmlString EnumDropDownListFor(this HtmlHelper htmlHelper, Expression> expression, object htmlAttributes)
    {
        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        Type enumType = GetNonNullableModelType(metadata);
        IEnumerable values = Enum.GetValues(enumType).Cast();
        IEnumerable 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);
    }
    /// 
    /// Gets the type of the non null-able model.
    /// 
    /// The model metadata.
    /// real Model Type
    private static Type GetNonNullableModelType(ModelMetadata modelMetadata)
    {
        Type realModelType = modelMetadata.ModelType;
        Type underlyingType = Nullable.GetUnderlyingType(realModelType);
        if (underlyingType != null)
        {
            realModelType = underlyingType;
        }
        return realModelType;
    }
}

Usage:

   @Html.EnumDropDownListFor(model => model.EnumProperty, "Select Enum", new { @class = "form-control", type = "text"})

Monday, June 10, 2013

ASP.NET MVC display and editor templates

Display templates

MVC has a bunch of handy helpers that we can use to create our views efficiently and effectively. One such helper is the display templates that are used within views.
@Html.DisplayFor(e => e.Username)
 
 
The DisplayFor(Func<TModel, TValue> expression) function uses the type of the property in the expression to display the property value. 
<!-- DisplayFor on string UserName = "daniel.imms" -->
<span class="field-validation-valid" data-valmsg-for="UserName" data-valmsg-replace="true">daniel.imms</span>

Defining custom templates

We can override the default templates by placing our custom display templates into the path Views/Shared/DisplayTemplates/<type>.cshtml. They are structured like any MVC partial view. An example usage could be adding a dollar sign to the front of a decimal's value. 

Model: 
public class TestModel
{
    public decimal Money { get; set; }
}
Views/Shared/DisplayTemplates/decimal.cshtml: 
@model decimal

@{
    IFormatProvider formatProvider = 
        new System.Globalization.CultureInfo("en-US");
    <span class="currency">@Model.ToString("C", formatProvider)</span>
}
View: 
@model TestModel

@Html.DisplayFor(e => e.Money)
Output: 
<span class="currency">$3.50</span>
 
 

UIHint attribute

To use a custom display template that isn't based on the name of the type, we can set a UIHint attribute on the property. So we could make a 'Currency' display template instead of assuming that all decimals are dollar amounts. To do this we would simply rename ourdecimal.cshtml file above to Currency.cshtml and apply the UIHint attribute to the model property like so: 
public class TestModel
{
    [UIHint("Currency")]
    public decimal Money { get; set; }
}

Editor templates

Editor templates can be overridden in the same way using the EditorFor function and placing the custom templates inViews/Shared/EditorTemplates/<type>.cshtml.

Passing additional data

It may be necessary to provide more than just a property to the custom template. For example if we want to display a list of radio buttons with one of them selected. This could be achieved by passing in the list of options as 'view data' and having the property as anint? which would represents the index of the selected option. MVC provides an overload that allows us to pass in this additional view data to the custom template, EditorFor(Func<TModel, TValue> expression, Object additionalViewData).

Model: 
public class UserModel
{
    [UIHint("RadioButtonList")]
    public int? UserRole { get; set; }
}
Controller:
public class TestController : Controller
{
    public ActionResult Test()
    {
        return View(new UserModel() { UserRole = 2 });
    }
}
View: 
@model UserModel

@{
    // In a real system we would get this list from the database
    List<SelectListItem> list = new List<SelectListItem>();
    list.Add(new SelectListItem() { Text = "Admin", Value = "0" });
    list.Add(new SelectListItem() { Text = "Project manager", Value = "1" });
    list.Add(new SelectListItem() { Text = "User", Value = "2" });
}

@Html.EditorFor(e => e.UserRole, new { List = list })
Views/Shared/EditorTemplates/RadioButtonList.cshtml: 
@model int?

@using System.Collections
@using System.Web.Mvc;

@{
    var list = (List<SelectListItem>)ViewData["List"];
}

<ul class="radio-list">
    @foreach (var item in list)
    {
        <li>
            @{
                var radioId = 
                    ViewData.TemplateInfo.GetFullHtmlFieldId(item.Value);
                var checkedClass = (item.Value == Model.ToString() 
                                    ? "checked" 
                                    : string.Empty);
                <input type="radio" 
                       id="@radioId" 
                       name="@ViewData.TemplateInfo.HtmlFieldPrefix" 
                       value="@item.Value" 
                       checked="@checkedClass" />
                <label for="@radioId">@item.Text</label>
            }
        </li>
    }
</ul>
Output: 
<ul class="radio-list">
    <li>
        <input type="radio" 
               id="UserRole_0" 
               name="UserRole" 
               value="0" 
               checked="" />
        <label for="UserRole_0">Admin</label>
    </li>
    <li>
        <input type="radio" 
               id="UserRole_1" 
               name="UserRole" 
               value="1" 
               checked="" />
        <label for="UserRole_1">Project manager</label>
    </li>
    <li>
        <input type="radio" 
               id="UserRole_2" 
               name="UserRole" 
               value="2" 
               checked="checked" />
        <label for="UserRole_2">User</label>
    </li>
</ul>

Usage examples

  • Format currency
  • Format dates in a particular way
  • Format credit card numbers
  • Display a list of radio buttons or check boxes
  • Add custom classes/IDs/structure to the output markup

Update: 
Another article here on my series.

Find a cool free stuff everyday

Giveaway of the Day

Hiren Bharadwa's Posts

DotNetJalps