2014-11-25 17 views
27

Ho una lista di enumerazioni che sto utilizzando per una pagina di gestione degli utenti. Sto utilizzando il nuovo HtmlHelper in MVC 5.1 che mi consente di creare un elenco a discesa per i valori Enum. Ora ho la necessità di rimuovere il valore in sospeso dalla lista, questo valore sarà sempre programmato e non dovrebbe mai essere impostato dall'utente.Escludi/Rimuovi valore da MVC 5.1 EnumDropDownListFor

Enum:

public enum UserStatus 
{ 
    Pending = 0, 
    Limited = 1, 
    Active = 2 
} 

Vista:

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

C'e 'qualche cosa, né ignorando il controllo corrente, o scrivere una HtmlHelper personalizzato che mi permettesse di specificare un enum, o di enumerazioni di escludere dalla lista risultante? O suggeriresti di fare qualcosa di lato client con jQuery per rimuovere il valore dall'elenco a discesa una volta che è stato generato?

Grazie!

+0

È possibile copiare il codice sorgente MVC per 'EnumDropDownListFor' [qui] (https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/Html/SelectExtensions.cs) e [qui] (https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/Html/EnumHelper.cs) e modificare la firma per includere un parametro che è una raccolta di valori esclusi, quindi in 'EnumHelper .GetSelectList() 'metodo, ignora gli elementi che si trovano nei valori esclusi. –

risposta

33

Si potrebbe costruire un elenco a discesa:

@{ // you can put the following in a back-end method and pass through ViewBag 
    var selectList = Enum.GetValues(typeof(UserStatus)) 
         .Cast<UserStatus>() 
         .Where(e => e != UserStatus.Pending) 
         .Select(e => new SelectListItem 
          { 
           Value = ((int)e).ToString(), 
           Text = e.ToString() 
          }); 
} 
@Html.DropDownListFor(m => m.Status, selectList) 
+0

Questo ha funzionato perfettamente, ma quando ho provato a spostare il codice nel back-end e a passarlo utilizzando il Viewbag, si è verificato un errore dicendo che non poteva contenere alcun contenuto costruito dinamicamente. Presumo che ciò sia dovuto al fatto che Viewbag viene creato dinamicamente in fase di runtime, spostando il codice su View ha risolto il problema e posso vedere che il valore corretto viene passato sul POST. Grazie! :) –

+4

Nel back-end facendo 'ViewBag.SelectList = selectList;' e in primo piano facendo '@ Html.DropDownListFor (m => m.Status, (IEnumerable ) ViewBag.SelectList)' dovrebbe funzionare (dalla memoria !) –

+0

La tua memoria era corretta! Quindi è stato il cast di fronte al Viewbag che mancava poi quando l'ho provato la prima volta? Presumo che questo dice al Razor Visualizza cosa aspettarsi una volta che l'oggetto Viewbag è stato creato? –

4

È possibile creare il menu a discesa autonomamente per looping through the values in the enum e includere solo lo <option> se non è in sospeso.

Ecco come dovrebbe funzionare, ma come potete vedere, non sono sicuro di cosa usereste per il valore o il testo del tag opzione.

<select> 
foreach (var status in Enum.GetValues(typeof(UserStatus))) 
{ 
    if(status != UserStatus.Pending) 
    { 
     <option value="status.???">@status.???</option> 
    } 
} 
</select> 
19

Modificato da @ di dav_i risposta.

Questo non è perfetto, ma è quello che sto usando. Di seguito è un'estensione a HtmlHelper. Il metodo di estensione sarà simile a EnumDropDownListFor da ASP.NET e utilizzerà DisplayAttribute se è stato applicato al valore Enum.

/// <summary> 
/// Returns an HTML select element for each value in the enumeration that is 
/// represented by the specified expression and predicate. 
/// </summary> 
/// <typeparam name="TModel">The type of the model.</typeparam> 
/// <typeparam name="TEnum">The type of the value.</typeparam> 
/// <param name="htmlHelper">The HTML helper instance that this method extends.</param> 
/// <param name="expression">An expression that identifies the object that contains the properties to display.</param> 
/// <param name="optionLabel">The text for a default empty item. This parameter can be null.</param> 
/// <param name="predicate">A <see cref="Func{TEnum, bool}"/> to filter the items in the enums.</param> 
/// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.</param> 
/// <returns>An HTML select element for each value in the enumeration that is represented by the expression and the predicate.</returns> 
/// <exception cref="ArgumentNullException">If expression is null.</exception> 
/// <exception cref="ArgumentException">If TEnum is not Enum Type.</exception> 
public static MvcHtmlString EnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, Func<TEnum, bool> predicate, string optionLabel, object htmlAttributes) where TEnum : struct, IConvertible 
{ 
    if (expression == null) 
    { 
     throw new ArgumentNullException("expression"); 
    } 

    if (!typeof(TEnum).IsEnum) 
    { 
     throw new ArgumentException("TEnum"); 
    } 

    ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 
    IList<SelectListItem> selectList = Enum.GetValues(typeof(TEnum)) 
      .Cast<TEnum>() 
      .Where(e => predicate(e)) 
      .Select(e => new SelectListItem 
       { 
        Value = Convert.ToUInt64(e).ToString(), 
        Text = ((Enum)(object)e).GetDisplayName(), 
       }).ToList(); 
    if (!string.IsNullOrEmpty(optionLabel)) { 
     selectList.Insert(0, new SelectListItem { 
      Text = optionLabel, 
     }); 
    } 

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

/// <summary> 
/// Gets the name in <see cref="DisplayAttribute"/> of the Enum. 
/// </summary> 
/// <param name="enumeration">A <see cref="Enum"/> that the method is extended to.</param> 
/// <returns>A name string in the <see cref="DisplayAttribute"/> of the Enum.</returns> 
public static string GetDisplayName(this Enum enumeration) 
{ 
    Type enumType = enumeration.GetType(); 
    string enumName = Enum.GetName(enumType, enumeration); 
    string displayName = enumName; 
    try 
    { 
     MemberInfo member = enumType.GetMember(enumName)[0]; 

     object[] attributes = member.GetCustomAttributes(typeof(DisplayAttribute), false); 
     DisplayAttribute attribute = (DisplayAttribute)attributes[0]; 
     displayName = attribute.Name; 

     if (attribute.ResourceType != null) 
    { 
      displayName = attribute.GetName(); 
     } 
    } 
    catch { } 
    return displayName; 
} 

Ad esempio:

@Html.EnumDropDownListFor(
    model => model.UserStatus, 
    (userStatus) => { return userStatus != UserStatus.Active; }, 
    null, 
    htmlAttributes: new { @class = "form-control" }); 

questo modo si crea un elenco a discesa Enum senza la possibilità di attiva.

+0

Brillante - Questo sta andando nella mia libreria di frammenti :) btw non è necessario restituire la condizione che si potrebbe semplicemente mettere (userStatus) => userStatus! = UserStatus.Active – heymega

+0

L'unica cosa necessaria per l'uso in una vista è l'istruzione 'using' per la classe in cui è definita. Perfezionare. :-) – podvlada

0

Di seguito è un'estensione per HtmlHelper. È molto simile all'estensione EnumDropDownListFor di ASP.NET, ma ordina SortListItem dal nome visualizzato dell'oggetto. Ha un nome suggestivo: SortedEnumDropDownListFor per non essere in conflitto con l'estensione originale.

/// <summary> 
    /// 
    /// </summary> 
    /// <typeparam name="TModel">The type of the model.</typeparam> 
    /// <typeparam name="TEnum">The type of the value.</typeparam> 
    /// <param name="htmlHelper">The HTML helper instance that this method extends.</param> 
    /// <param name="expression">An expression that identifies the object that contains the properties to display</param> 
    /// <param name="initalValue">The unselected item initial value</param> 
    /// <param name="htmlAttributes"></param> 
    /// <returns></returns> 
    public static MvcHtmlString SortedEnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, string initalValue, object htmlAttributes = null) 
    { 

     ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 

     Type enumType = GetNonNullableModelType(metadata); 
     Type baseEnumType = Enum.GetUnderlyingType(enumType); 
     List<SelectListItem> items = new List<SelectListItem>(); 

     foreach (FieldInfo field in enumType.GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public)) 
     { 
      string text = field.Name; 
      string value = Convert.ChangeType(field.GetValue(null), baseEnumType).ToString(); 
      bool selected = field.GetValue(null).Equals(metadata.Model); 

      foreach (DisplayAttribute displayAttribute in field.GetCustomAttributes(true).OfType<DisplayAttribute>()) 
      { 
       text = displayAttribute.GetName(); 
      } 

      items.Add(new SelectListItem 
      { 
       Text = text, 
       Value = value, 
       Selected = selected 
      }); 
     } 

     items = new List<SelectListItem>(items.OrderBy(s => s.Text)); 
     items.Insert(0, new SelectListItem { Text = initalValue, Value = "" }); 

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

    }   

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

     if (underlyingType != null) 
     { 
      realModelType = underlyingType; 
     } 

     return realModelType; 
    } 

Se non si vuole perdere tempo con la voce intitia non selezionata, basta costruire un sovraccarico come questo:

public static MvcHtmlString SortedEnumDropDownListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes = null) 
     { 
      MvcHtmlString helper = SortedEnumDropDownListFor(htmlHelper, expression, string.Empty, htmlAttributes); 
      return helper; 
     } 

E tu sei a posto. Spero possa essere d'aiuto.