Fun (?) with Linq Expressions in extension methods

后端 未结 1 1866
庸人自扰
庸人自扰 2020-12-03 12:38

I wrote an HtmlHelper expression I use a lot of the time to put title tags into my dropdown lists like so:

    public static HtmlString SelectFor

        
相关标签:
1条回答
  • 2020-12-03 13:25

    If you don't need the title attribute on individual options your code could be simplified to:

    public static HtmlString SelectFor<TModel, TProperty, TIdProperty, TDisplayProperty, TListItem>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression,
        IEnumerable<TListItem> enumeratedItems,
        Expression<Func<TListItem, TIdProperty>> idProperty,
        Expression<Func<TListItem, TDisplayProperty>> displayProperty,
        object htmlAttributes
    ) where TModel : class
    {
        var id = (idProperty.Body as MemberExpression).Member.Name;
        var display = (displayProperty.Body as MemberExpression).Member.Name;
        var selectList = new SelectList(enumeratedItems, id, display);
        var attributes = new RouteValueDictionary(htmlAttributes);
        return htmlHelper.DropDownListFor(expression, selectList, attributes);
    }
    

    and used like this:

    @Html.SelectFor(
        m => m.itemId, 
        Model.items, 
        id => id.itemId, 
        disp => disp.itemName, 
        null
    )
    

    And if you need the title attribute, well, you will have to implement everything that the DropDownList helper does manually which could be quite of a pain. Here's an example of only a small portion of all the functionality:

    public static class HtmlExtensions
    {
        private class MySelectListItem : SelectListItem
        {
            public string Title { get; set; }
        }
    
        public static HtmlString SelectFor<TModel, TProperty, TIdProperty, TDisplayProperty, TListItem>(
            this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TProperty>> expression,
            IEnumerable<TListItem> enumeratedItems,
            Expression<Func<TListItem, TIdProperty>> idProperty,
            Expression<Func<TListItem, TDisplayProperty>> displayProperty,
            Func<TListItem, string> titleProperty,
            object htmlAttributes
        ) where TModel : class
        {
            var name = ExpressionHelper.GetExpressionText(expression);
            var fullHtmlName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
    
            var select = new TagBuilder("select");
            var compiledDisplayProperty = displayProperty.Compile();
            var compiledIdProperty = idProperty.Compile();
            select.GenerateId(fullHtmlName);
            select.MergeAttributes(new RouteValueDictionary(htmlAttributes));
            select.Attributes["name"] = fullHtmlName;
            var selectedValue = htmlHelper.ViewData.Eval(fullHtmlName);
            var options = 
                from i in enumeratedItems
                select ListItemToOption(
                    ItemToSelectItem(i, selectedValue, compiledIdProperty, compiledDisplayProperty, titleProperty)
                );
            select.InnerHtml = string.Join(Environment.NewLine, options);
            return new HtmlString(select.ToString(TagRenderMode.Normal));
        }
    
        private static MySelectListItem ItemToSelectItem<TListItem, TIdProperty, TDisplayProperty>(TListItem i, object selectedValue, Func<TListItem, TIdProperty> idProperty, Func<TListItem, TDisplayProperty> displayProperty, Func<TListItem, string> titleProperty)
        {
            var value = Convert.ToString(idProperty(i));
            return new MySelectListItem
            {
                Value = value,
                Text = Convert.ToString(displayProperty(i)),
                Title = titleProperty(i),
                Selected = Convert.ToString(selectedValue) == value
            };
        }
    
        private static string ListItemToOption(MySelectListItem item)
        {
            var builder = new TagBuilder("option");
            builder.Attributes["value"] = item.Value;
            builder.Attributes["title"] = item.Title;
            builder.SetInnerText(item.Text);
            if (item.Selected)
            {
                builder.Attributes["selected"] = "selected";
            }
            return builder.ToString();
        }
    }
    

    and then use like this:

    @Html.SelectFor(
        m => m.itemId, 
        Model.items, 
        id => id.itemId, 
        disp => disp.itemName, 
        title => title.itemName + " " + title.itemDescription, 
        null
    )
    
    0 讨论(0)
提交回复
热议问题