Quando la composizione di un certo insieme di elementi è nota a priori già in fase di compilazione (per esempio, i giorni della settimana), può essere preferibile rappresentare questo elenco con un enumerato, piuttosto che introdurre tabelle addizionali sulla base dati. Se questo approccio è molto comodo nella scrittura del codice, può invece presentare delle problematiche nel momento in cui vogliamo sfruttare i possibili valori per popolare una dropdown list. In questo caso, infatti, dobbiamo trasformare i membri dell'enumerato in un elenco di SelectListItem.
Il modo più comodo per farlo è quello di realizzare un'html helper, così che possiamo recuperarne la logica tutte le volte che ne abbiamo bisogno:
public static IHtmlString DropDownListFor<TModel, TEnum>( this HtmlHelper<TModel> html, Expression<Func<TModel, TEnum>> expression) where TEnum : struct { if (!typeof(TEnum).IsEnum) throw new ArgumentException("Il tipo deve essere un enum"); // recuperiamo il valore corrente del model ModelMetadata model = ModelMetadata .FromLambdaExpression(expression, html.ViewData); var currentValue = model.Model; var items = Enum.GetValues(typeof(TEnum)).OfType<object>() .Select(x => new SelectListItem() { Value = x.ToString(), Text = GetDescription(x), Selected = currentValue != null && currentValue.Equals(x) }); return html.DropDownListFor(expression, items); }
Il metodo in alto ha la forma tipica degli html helper di ASP.NET MVC e accetta una expression che, dal model, permette di individuare una proprietà di tipo Enum; in realtà il vincolo è solo sulla sua natura di struct e, siccome non è possibile scrivere qualcosa di più restrittivo in c#, l'unica opzione è quella poi di effettuare una verifica a runtime.
Lo scopo del codice è sostanzialmente quello di eseguire una query LINQ che trasformi l'elenco dei possibili valori dell'enumerato in una lista di SelectListItem, che possono poi essere forniti all'helper DropDownListFor standard di ASP.NET MVC.
La clausola di Select, però, ha bisogno del valore corrente della proprietà del model, in modo da poter marcare il corrispondente elemento come Selected. Il modo più semplice per recuperare questo dato è sfruttare il ModelMetadata, come mostrato all'inizio del metodo, la cui proprietà Model contiene il dato che cerchiamo.
Un'ultima nota riguarda il valore che assegnamo alla proprietà Text: l'obiettivo è quello di poter marcare i vari elementi dell'enum con l'attributo DisplayAttribute in modo da supportare anche descrizioni personalizzate:
public enum SampleEnum { [Display(Name = "Primo elemento")] First, [Display(Name = "Secondo elemento")] Second, [Display(Name = "Terzo elemento")] Third }
Per questa ragione, l'helper originale sfrutta un metodo GetDescription che, tramite reflection, si occupa di verificare se questo attributo è presente nei vari valori dell'enumerato ed eventualmente lo sfrutta per recuperarne la descrizione:
private static string GetDescription(object value) { if (value == null) return string.Empty; string result = value.ToString(); var display = value.GetType() .GetMember(value.ToString()).First() .GetCustomAttributes(false) .OfType<DisplayAttribute>() .FirstOrDefault(); if (display != null) { result = display.GetName(); } return result; }
L'utilizzo finale è davvero molto semplice:
@Html.DropDownListFor(x => x.EnumMyProperty);
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Gestione degli stili CSS con le regole @layer
Popolare una classe a partire dal testo, con Semantic Kernel e ASP.NET Core Web API
Le novità di Angular: i miglioramenti alla CLI
Sfruttare lo stream rendering per le pagine statiche di Blazor 8
Usare il colore CSS per migliorare lo stile della pagina
Implementare l'infinite scroll con QuickGrid in Blazor Server
Eseguire script pre e post esecuzione di un workflow di GitHub
Autenticarsi in modo sicuro su Azure tramite GitHub Actions
Miglioramenti agli screen reader e al contrasto in Angular
Sfruttare MQTT in cloud e in edge con Azure Event Grid
Code scanning e advanced security con Azure DevOps
Registrare servizi multipli tramite chiavi in ASP.NET Core 8