pisałem wyrażenie HtmlHelper używam dużo czasu, aby umieścić tagi tytułowe w moich listach rozwijanych tak:Zabawa z LINQ zgłaszanie metod przedłużających
public static HtmlString SelectFor<TModel, TProperty, TListItem>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<TListItem> enumeratedItems,
string idPropertyName,
string displayPropertyName,
string titlePropertyName,
object htmlAttributes) where TModel : class
{
//initialize values
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var propertyName = metaData.PropertyName;
var propertyValue = htmlHelper.ViewData.Eval(propertyName).ToStringOrEmpty();
var enumeratedType = typeof(TListItem);
//build the select tag
var returnText = string.Format("<select id=\"{0}\" name=\"{0}\"", HttpUtility.HtmlEncode(propertyName));
if (htmlAttributes != null)
{
foreach (var kvp in htmlAttributes.GetType().GetProperties()
.ToDictionary(p => p.Name, p => p.GetValue(htmlAttributes, null)))
{
returnText += string.Format(" {0}=\"{1}\"", HttpUtility.HtmlEncode(kvp.Key),
HttpUtility.HtmlEncode(kvp.Value.ToStringOrEmpty()));
}
}
returnText += ">\n";
//build the options tags
foreach (TListItem listItem in enumeratedItems)
{
var idValue = enumeratedType.GetProperties()
.FirstOrDefault(p => p.Name == idPropertyName)
.GetValue(listItem, null).ToStringOrEmpty();
var titleValue = enumeratedType.GetProperties()
.FirstOrDefault(p => p.Name == titlePropertyName)
.GetValue(listItem, null).ToStringOrEmpty();
var displayValue = enumeratedType.GetProperties()
.FirstOrDefault(p => p.Name == displayPropertyName)
.GetValue(listItem, null).ToStringOrEmpty();
returnText += string.Format("<option value=\"{0}\" title=\"{1}\"",
HttpUtility.HtmlEncode(idValue), HttpUtility.HtmlEncode(titleValue));
if (idValue == propertyValue)
{
returnText += " selected=\"selected\"";
}
returnText += string.Format(">{0}</option>\n", displayValue);
}
//close the select tag
returnText += "</select>";
return new HtmlString(returnText);
}
... to działa maśle, ale są chwile, kiedy chcę iść dalej. Chciałbym dostosować identyfikator, wyświetlacz i tytuły tej bestii bez konieczności wypisywania html. Na przykład, jeśli mam kilka klas w modelu tak:
public class item
{
public int itemId { get; set; }
public string itemName { get; set; }
public string itemDescription { get; set; }
}
public class model
{
public IEnumerable<item> items { get; set; }
public int itemId { get; set; }
}
Moim zdaniem mogę napisać:
@Html.SelectFor(m => m.itemId, Model.items, "itemId", "itemName", "itemDescription", null)
... i będę miał piękny rozwijaną z atrybutów tytuł etc To jest świetne, o ile wymienione elementy mają właściwości dokładnie tak, jak chciałbym je wyświetlić. Ale to, co naprawdę chciałbym zrobić coś takiego jak:
@Html.SelectFor(m => m.itemId, Model.items, id=>id.itemId, disp=>disp.itemName, title=>title.itemName + " " + title.itemDescription, null)
... i mają w tym przypadku, atrybut tytuł na opcjach być połączeniem właściwości itemName
a właściwość itemDescription
. Wyznaję meta-poziom wyrażeń lambda, a funkcje Linq sprawiają, że mam trochę zawrotów głowy. Czy ktoś może wskazać mi właściwy kierunek?
wynik końcowy Dla tych, którzy są ciekawi, następujący kod daje mi pełną kontrolę nad listy Wybierz identyfikator, właściwości Title, a displayText przy użyciu wyrażeń lambda:
public static HtmlString SelectFor<TModel, TProperty, TListItem>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> forExpression,
IEnumerable<TListItem> enumeratedItems,
Attribute<TListItem> idExpression,
Attribute<TListItem> displayExpression,
Attribute<TListItem> titleExpression,
object htmlAttributes,
bool blankFirstLine) where TModel : class
{
//initialize values
var metaData = ModelMetadata.FromLambdaExpression(forExpression, htmlHelper.ViewData);
var propertyName = metaData.PropertyName;
var propertyValue = htmlHelper.ViewData.Eval(propertyName).ToStringOrEmpty();
var enumeratedType = typeof(TListItem);
//build the select tag
var returnText = string.Format("<select id=\"{0}\" name=\"{0}\"", HttpUtility.HtmlEncode(propertyName));
if (htmlAttributes != null)
{
foreach (var kvp in htmlAttributes.GetType().GetProperties()
.ToDictionary(p => p.Name, p => p.GetValue(htmlAttributes, null)))
{
returnText += string.Format(" {0}=\"{1}\"", HttpUtility.HtmlEncode(kvp.Key),
HttpUtility.HtmlEncode(kvp.Value.ToStringOrEmpty()));
}
}
returnText += ">\n";
if (blankFirstLine)
{
returnText += "<option value=\"\"></option>";
}
//build the options tags
foreach (TListItem listItem in enumeratedItems)
{
var idValue = idExpression(listItem).ToStringOrEmpty();
var displayValue = displayExpression(listItem).ToStringOrEmpty();
var titleValue = titleExpression(listItem).ToStringOrEmpty();
returnText += string.Format("<option value=\"{0}\" title=\"{1}\"",
HttpUtility.HtmlEncode(idValue), HttpUtility.HtmlEncode(titleValue));
if (idValue == propertyValue)
{
returnText += " selected=\"selected\"";
}
returnText += string.Format(">{0}</option>\n", displayValue);
}
//close the select tag
returnText += "</select>";
return new HtmlString(returnText);
}
public delegate object Attribute<T>(T listItem);
UWAGA: ToStringOrEmpty() Rozszerzenie to jest moja metoda, która przyjmuje obiekt i robi ToString() na nim, chyba że jest pusty, w takim przypadku przywraca pusty łańcuch. –