Отмеченные элементы SelectList - PullRequest
0 голосов
/ 28 марта 2012

У меня есть список категорий (сущностей), у каждой из которых есть родители.Вот модель для категории:

  public class Category
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int CategoryId { get; set; }

        public int? ParentId { get; set; }
        public string Name { get; set; }

        public virtual Category Parent { get; set; }
    }

Мне нужно заполнить список SelectList таким образом, чтобы родители были на первом месте, а дети уходили с отступом Как:

  • Категория 1
    • Подкатегория 1
    • Подкатегория 2
  • Категория 2

Я должен использовать этот список выбора, чтобы добавить выпадающий список в меню редактирования иобновить формы.Как мне это сделать?сторона клиента?сервер?

Спасибо за любую помощь, которую вы можете предложить.

Ответы [ 2 ]

3 голосов
/ 28 марта 2012

Это заставило меня задуматься о реализации этого без зависимости 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> для определения отношений.

Не забудьте добавить необходимые проверки!

1 голос
/ 28 марта 2012

Я бы сделал это на стороне сервера, используя optgroup, например, в синтаксисе razor.

<select>
@foreach(var parent in categories.Where(x => !x.ParentId.HasValue)
{
    <optgroup label="@parent.Name">
    @foreach(var child in categories.Where(x => x.ParentId.Equals(parent.CategoryId))
    {
        <option value="@child.CategoryId">@child.Name</option>
    }
    </optgroup>
}
</select>

Я бы также сделал это методами расширения, чтобы он использовал другие доступные вспомогательные методы HTML.

Редактировать


Метод расширения может принимать коллекцию элементов, которые реализуют простой интерфейс, подобный этому:

public interface IGroupable
{
    int Id { get; set; }

    string Name { get; set; }

    int? ParentId { get; set; }
}

public class Category : IGroupable
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int CategoryId { get; set; }

    public int? ParentId { get; set; }    //implements IGroupable.ParentId
    public string Name { get; set; }    //implements IGroupable.Name

    public virtual Category Parent { get; set; }

    #region [IGroupable Specific  Implementation]

    public int Id { get { return this.CategoryId; } }

    #endregion
}

public static class ExtensionMethods
{
    public static MvcHtmlString DropDownGroupList(this HtmlHelper helper, IEnumerable<IGroupable> items)
    {
        var html = new StringBuilder("<select>");

        foreach (var item in items.Where(x => !x.ParentId.HasValue))
        {
            html.Append(string.Format("<optgroup label=\"{0}\">", item.Name));

            foreach(var child in items.Where(x => x.ParentId.Equals(item.Id)))
            {
                html.Append(string.Format("<option value=\"{0}\">{1}</option>", child.Id, child.Name));
            }

            html.Append("</optgroup>");
        }

        html.Append("</select>");

        return new MvcHtmlString(html.ToString());
    }
}
...