Creare un EditorTemplate per gli enum con MVC 2

Intro

Nelle mie applicazioni faccio largo uso di enums (e chi non lo fa?), mi sono quindi creato una serie di helpers / providers per la gestione della UI bindata con gli enum.

In WebForms utilizzo:

  • IEnumDescriptionProvider: servizio che estrapola le descrizioni.
    • LocalizedEnumDescriptionProvider per le descrizioni localizzate
    • AttributeEnumDescriptionProvider per leggere degli attributi custom
    • AutomaticCaseEnumDescriptionProvider per la conversione automatica al cambio di case (UnValore –> Un Valore)
    • DefaultEnumDescriptionProvider che fa il semplice ToString()
  • IDropDownItemsProvider per creare le liste di valori da passare alle dropdown.
    • DefaultEnumDropDownItemsProvider che ha per chiave il valore numerico dell’enum e per testo la descrizione estrapolata tramite i provider.

 

Visto che MVC2 è estendibile ho provato a riportare le funzionalità di base su questa piattaforma.

Questo è il model

public class Movie
{
    [Required]
    public string Title { get; set; }
    public Rating Rating { get; set; }
}

e questo l’enum

public enum Rating 
{
    None,
    Horrible,
    Bad,
    Poor,
    BelowAverage,
    Average,
    AboveAverage,
    Good,
    VeryGood,
    Superb,
    Excellent
}
Sfruttando il nuovo motore di template possiamo scrivere la form di inserimento dati come
<% using (Html.BeginForm()){%>
   <%= Html.EditorForModel() %>
   <input type="submit" value="Create" />
<%}%>

Il risultato è il seguente:

image

Decisamente poco user friendly.

Step 1 – gestire le descrizioni tramite attributo

MVC2 utilizza DataAnnotations e l’attributo DisplayNameAttribute per poter impostare una label custom sui controlli. Purtroppo l’attributo DisplayNameAttribute non è utilizzabile direttamente sul singolo valore dell’enum. Bypassiamo il problema creando la nostra versione e cambiando l’AttributeUsage

[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field)]
public class EnumDisplayNameAttribute : DisplayNameAttribute
{
    public EnumDisplayNameAttribute(string displayName)
        :base(displayName)
    {
    }
}

In questo modo è possibile decorare l’enum

[EnumDisplayName("** Rating")]
public enum Rating 
{
    None,
    Horrible,
    Bad,
    Poor,
    [EnumDisplayName("Below Average")]
    BelowAverage,
    Average,
    [EnumDisplayName("Above Average")]
    AboveAverage,
    Good,
    [EnumDisplayName("Very Good")]
    VeryGood,
    Superb,
    [EnumDisplayName("Just... WOW!")]
    Excellent
}

Step 2 – estendere l’enum

Abbiamo bisogno di leggere il valore degli enum e di poter creare una lista di valori da passare al controllo DropDown. Implementiamo entrambe le funzionalità come Extension Methods

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace EnumTemplates.Helpers
{
    public static class EnumExtensions
    {
        public static string GetDisplayName(this Enum enumItem)
        {
            var mi = enumItem.GetType().GetMember(enumItem.ToString()).FirstOrDefault();
            if(mi != null)
            {
                var dn = (EnumDisplayNameAttribute)(mi.GetCustomAttributes(typeof(EnumDisplayNameAttribute), false)).FirstOrDefault();
                if (dn != null)
                    return dn.DisplayName;
            }

            return enumItem.ToString();
        }

        public static IEnumerable<SelectListItem> ToSelectList(this Enum enumItem)
        {
            var enumType = enumItem.GetType();

            return (from object value in Enum.GetValues(enumType)
                    select new SelectListItem
                               {
                                   Selected = value.ToString() == enumItem.ToString(), 
                                   Text = ((Enum)value).GetDisplayName(), 
                                   Value = ((int)value).ToString()
                               }).ToList();
        }
    }
}

Step 3 – Creare un template di default per gli Enum

Nella cartella Views\Shared\EditorTemplates creiamo un nuovo usercontrol Enum.ascx

image

il cui sorgente è il seguente

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Enum>" %>
<%@ Import Namespace="EnumTemplates.Helpers" %>
<%= Html.DropDownListFor(x => x,Model.ToSelectList()) %>

Step 4 – Abilitare il template per il nostro Enum

Piuttosto che decorare con l’attributo UIHint ogni property del nostro ViewModel (che è un approccio decisamente poco DRY) proviamo a decorare direttamente l’enum Ratings.

image

Anche in questo caso ci sono limitazioni sull’utilizzo dell’attributo, la soluzione la conosciamo già ;D

using System;
using System.ComponentModel.DataAnnotations;

namespace EnumTemplates.Helpers
{
    [AttributeUsage(AttributeTargets.Enum)]
    public class EnumUIHintAttribute : UIHintAttribute
    {
        public EnumUIHintAttribute(string uiHint)
            : base(uiHint)
        {
            
        }
    }
}

a questo punto possiamo definire il nostro enumerativo come

using EnumTemplates.Helpers;

namespace EnumTemplates.Models
{
    [EnumDisplayName("** Rating")]
    [EnumUIHint("Enum")]
    public enum Rating 
    {
        None,
        Horrible,
        Bad,
        Poor,
        [EnumDisplayName("Below Average")]
        BelowAverage,
        Average,
        [EnumDisplayName("Above Average")]
        AboveAverage,
        Good,
        [EnumDisplayName("Very Good")]
        VeryGood,
        Superb,
        [EnumDisplayName("Just... WOW!")]
        Excellent
    }
}

Step 5 – Up & Running

Step fondamentale…. F5 e test nel browser..

 

image 

e se diamo un’occhiata al sorgente HTML troviamo

image

decisamente meglio.

Sorgenti

3 commenti

Aspnetmvc, Attributes, Enums, Templates

[top]

Related Post

Andrea Balducci - IEnumerable.it