Это заставило меня задуматься о реализации этого без зависимости interface
, что я все еще считаю разумным. Вот альтернативное решение, использующее метод расширения, который не требует реализации interface
.
Метод расширения
public static class ExtensionMethods
{
/// <summary>
/// Returns a single-selection select element containing the options specified in the items parameter.
/// </summary>
/// <typeparam name="T">The type of elements in the collection.</typeparam>
/// <param name="helper">The class being extended.</param>
/// <param name="items">The collection of items used to populate the drop down list.</param>
/// <param name="parentItemsPredicate">A function to determine which elements are considered as parents.</param>
/// <param name="parentChildAssociationPredicate">A function to determine the children of a given parent.</param>
/// <param name="dataValueField">The value for the element.</param>
/// <param name="dataTextField">The display text for the value.</param>
/// <returns></returns>
public static MvcHtmlString DropDownGroupList<T>(
this HtmlHelper helper,
IEnumerable<T> items,
Func<T, bool> parentItemsPredicate,
Func<T, T, bool> parentChildAssociationPredicate,
string dataValueField,
string dataTextField)
{
var html = new StringBuilder("<select>");
foreach (var item in items.Where(parentItemsPredicate))
{
html.Append(string.Format("<optgroup label=\"{0}\">", item.GetType().GetProperty(dataTextField).GetValue(item, null)));
foreach (var child in items.Where(x => parentChildAssociationPredicate(x, item)))
{
var childType = child.GetType();
html.Append(string.Format("<option value=\"{0}\">{1}</option>", childType.GetProperty(dataValueField).GetValue(child, null), childType.GetProperty(dataTextField).GetValue(child, null)));
}
html.Append("</optgroup>");
}
html.Append("</select>");
return new MvcHtmlString(html.ToString());
}
}
Использование в зависимости от вашего Category
class
@this.Html.DropDownGroupList(YourCollection, x => !x.ParentId.HasValue, (x, y) => { return x.ParentId.Equals(y.CategoryId); }, "CategoryId", "Name")
К тому времени, когда я закончил писать это сообщение, я не был уверен, что это было настолько ценно, но думал, что я все равно его опубликую.
Как видите, ваш class
должен знать идентификатор своего родителя, а отображаемое имя дочернего и родительского элементов должно использовать одно и то же свойство, как указано параметром dataTextField
. Поэтому, по сути, вашему class
нужны свойства: Id, ParentId и Name, и вы используете параметры Func<T, bool>
и Func<T, T, bool>
для определения отношений.
Не забудьте добавить необходимые проверки!