Dato un modello di vista come questo:Html.Labelfor utilizzare DisplayName dell'oggetto non proprietà
public class ParentViewModel
{
public object ChildViewModel { get; set; }
}
se uso Html.LabelFor
come questo:
@Html.LabelFor(model => model.ChildViewModel)
Vorrei avere un output simile a questo:
<label for="Model_ChildViewModel">ChildViewModel</label>
Ciò che in realtà desidero è che l'etichetta generata utilizzi l'attributo DisplayName
applicato a l'oggetto E.G.
[DisplayName("My Custom Label")]
public class ChildViewModel
{
}
con una potenza di:
<label for="Model_ChildViewModel">My Custom Label</label>
Sono consapevole che il metodo Html.LabelFor
prende un'espressione che prevede una proprietà e cercherà l'attributo DisplayName
su tale proprietà piuttosto che l'oggetto stesso.
ho creato un metodo di supporto Html per ottenere ciò che voglio che assomiglia a questo:
public static IHtmlString CreateLabel<TModel>(this HtmlHelper html, Func<TModel, object> func)
where TModel : class
{
TagBuilder tb = new TagBuilder("label");
var model = html.ViewData.Model as TModel;
if (model != null)
{
object obj = func(model);
if (obj != null)
{
var attribute = obj.GetType().GetCustomAttributes(
typeof(DisplayNameAttribute), true)
.FirstOrDefault() as DisplayNameAttribute;
if (attribute != null)
{
tb.InnerHtml = attribute.DisplayName;
return MvcHtmlString.Create(tb.ToString());
}
else
{
tb.InnerHtml = obj.ToString();
return MvcHtmlString.Create(tb.ToString());
}
}
}
tb.InnerHtml = html.ViewData.Model.ToString();
return MvcHtmlString.Create(tb.ToString());
}
Invece di prendere un'espressione, il mio aiuto prende un Func<TModel, object>
che restituisce l'oggetto che voglio per verificare la Attributo DisplayName
.
Il primo problema che ho avuto è stato quando ho provato a chiamare questo metodo in rasoio in questo modo:
@Html.CreateLabel(model => model.ChildObject)
ottengo il seguente errore:
The type arguments for method 'CreateLabel<TModel>(this HtmlHelper,
Func<TModel, object>) cannot be inferred from usage. Try specifying
the arguments explicitly.'
Così chiamo il metodo come questo, invece :
@{ Html.CreateLabel<ChildViewModel>(model => model.ChildObject); }
ma non viene eseguito il rendering. Se utilizzo il debugger per passare attraverso il mio metodo helper, viene generato il tag label ma non viene mostrato nulla quando viene eseguito il rendering della mia pagina.
Quindi le mie domande sono:
- Come posso risolvere questo problema per generare l'etichetta per mio punto di vista?
- Cosa devo fare in modo che il parametro generico possa essere dedotto?
- C'è un modo per scrivere l'helper Html per fare la stessa cosa ma usando un'espressione? Non ho esperienza nell'uso delle espressioni quindi non so da dove cominciare.
Aggiornamento
ho pensato di pubblicare il codice finale come ho fatto alcune piccole modifiche. Prima di tutto ho dato un'occhiata agli helper nel codice sorgente MVC e ho deciso di suddividere il metodo in tre metodi separati in linea con gli esempi forniti. Ho rimosso anche tutto il materiale TagBuilder
poiché tutto ciò di cui avevo veramente bisogno era di generare il testo da iniettare tra i tag <legend></legend>
. Il codice finale è sotto. Ancora una volta, grazie a Sylon per avermi aiutato in questo.
public static IHtmlString LegendTextFor<TModel, TObject>(this HtmlHelper<TModel> html, Expression<Func<TModel, TObject>> expression)
{
return LegendTextHelper(html,
ModelMetadata.FromLambdaExpression(expression, html.ViewData),
ExpressionHelper.GetExpressionText(expression),
expression.Compile().Invoke(html.ViewData.Model));
}
private static IHtmlString LegendTextHelper<TModel, TObject>(this HtmlHelper<TModel> html, ModelMetadata metadata, string htmlFieldName, TObject value)
{
string resolvedLabelText = metadata.DisplayName ?? value.GetDisplayName() ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(resolvedLabelText))
return MvcHtmlString.Empty;
return MvcHtmlString.Create(resolvedLabelText);
}
private static string GetDisplayName<T>(this T obj)
{
if (obj != null)
{
var attribute = obj.GetType()
.GetCustomAttributes(typeof(DisplayNameAttribute), false)
.Cast<DisplayNameAttribute>()
.FirstOrDefault();
return attribute != null ? attribute.DisplayName : null;
}
else
{
return null;
}
}
Grazie, funziona magnificamente. Sapevo che dovevo ottenere l'oggetto reale dall'espressione, ma non ero sicuro di come farlo, quindi questa riga 'expression.Compile(). Invoke (html.ViewData.Model)' è particolarmente utile. –