为IEnumerable <tmodel>编写MVC HtmlHelper以显示HTML表</tmodel>

时间:2013-07-11 15:30:43

标签: c# asp.net-mvc html-helper

我正在尝试编写一个客户HtmlHelper,它允许从传递IEnumerable <TModel>的视图中轻松创建Html表,其中TModel是模型对象类型。我希望视图中的调用语法是样式:

@Html.TableFor(model => model.AddedUserID, model => model.ClientID......., model model => AnothreFildIWantDisplayed));

我首先尝试使用一个表达式首先传递,例如

@Html.TableFor(model => model.AddedUserID)

一旦有效,我将使用params并使用多个表达式来使用逗号分隔列表。

我的观看代码如下所示

@using MyNameSpaceToMyHelper
@model IEnumerable<User>

@{
    ViewBag.Title = "Index";
}
<p>

</p>

@Html.TableFor(model => model.AddedUserID));

我编写HtmlHelper的尝试是:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.UI;

namespace MyNameSpaceToMyHelper
{
    public static class GridExtensions
    {
        public static MvcHtmlString TableFor<TModel, TValue>(this HtmlHelper<IEnumerable<TModel>> html, Expression<Func<TModel, TValue>> expression)
        {
            var writer = new HtmlTextWriter(new StringWriter());

            writer.RenderBeginTag(HtmlTextWriterTag.Table);

            writer.RenderBeginTag(HtmlTextWriterTag.Thead);

            writer.RenderBeginTag(HtmlTextWriterTag.Th);
            writer.RenderBeginTag(HtmlTextWriterTag.Td);

            //Colum Headers
            writer.Write(html.DisplayNameFor(expression));

            writer.RenderEndTag();
            writer.RenderEndTag();

            writer.RenderEndTag();//Close THead

            //Column Data
            //html.DisplayFor((Expression<Func<TModel, TValue>>)expression);
            foreach (ViewDataDictionary<TModel> vdm in html.ViewData.Values)
            {
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                writer.RenderBeginTag(HtmlTextWriterTag.Td);

                //DOESN'T COMPILE
                html.DisplayFor(expression);
                writer.RenderEndTag();
                writer.RenderEndTag();
            }


            writer.RenderEndTag(); //Close Header


            return new MvcHtmlString(writer.InnerWriter.ToString());
        }

   }

}

由于以下原因而无法编译:

html.DisplayFor(表达);

据我理解,这是因为传递给我的方法的HtmlHelper是HtmlHelper<IEnumerable<TModel>>,而使用DisplayFor则需要HtmlHelper<TModel>

由于这个错误,似乎可以通过将表头和数据拆分为两个单独的方法调用来实现这一点。从我的视图中我可以调用header方法然后在实际视图中调用一个for循环每行或每个东西的方法。像这样的东西

@using MyNameSpaceToMyHelper
@model IEnumerable<User>

@{
    ViewBag.Title = "Index";
}
<p>

</p>

@Html.TableHeadersFor(model => model.AddedUserID));
@foreach (var item in Model) {
       @Html.TableRowsFor(model => model.AddedUserID));
}

这应该有效,因为TableHeadersFor可以调用DisplayNameExtensions.DisplayNameForHtmlHelper<IEnumerable<TModel>>TableRowsFor可以HtmlHelper<TModel>,并DisplayExtensions.DisplayFor调用HtmlHelper<TModel> DisplayNameExtensions.DisplayNameFor因此不兼容的HtmlHelper类型问题就会消失。

但是,如果可能的话,我不希望这样做,因为我希望调用语法尽可能简单。我的问题的一个基本前提是尝试维护一个简单的调用语法,而无需在视图中编写for循环并复制用于选择列的表达式。这里的全部目的是设计一种可重复使用且易于其他人使用的东西

我在ILSpy中查看了HtmlHelper<IEnumerable<TModel>>的工作方式,因为它处理HtmlHelper<IEnuermable<TModel>>,因为它看起来像是一种以HtmlHelper<TModel>为内部调用{{1}的内部扩展方法的exstension方法但是ILSpy生成的代码甚至没有重新编译,因此我无法理解.Net是如何做到的。

这在一种扩展方法中是否可行,或者我是否会被迫拆分它以破坏调用语法?

更新1 我已经设法让以下工作做了大量的工作:

@Html.BeginTableFor(model => model.AddedUserID
                    , model => model.Active
                    , new { @class = "grid" })


@foreach (var item in Model) {

    @Html.DisplayRowFor(model => item.AddedUserID
                        , model => item.Active)
}

@Html.EndTableFor()

它并不像我想的那样简洁并涉及一堆工作,特别是因为BeginTableFor和DisplayRowFor的每个表达式都可以返回不同的类型,这意味着我基本上必须为任意数量的表达式实现方法重载(我已经完成了)最多100个,因此最多100列!)。这基本上是他们在.Net中使用Action和Func委托的问题,他们必须为不同数量的参数编写不同的版本,例如动作T1,T2,..,T16。它显然非常凌乱,但我宁愿使用漂亮的调用语法,也不要避免许多方法重载。

1 个答案:

答案 0 :(得分:1)

查看this article的某个方向,并根据您的需要进行修改。

首先,您需要这样的HTML扩展程序:

public static class RazorExtensions {
    public static HelperResult List<T>(this IEnumerable<T> items, 
      Func<T, HelperResult> template) {
        return new HelperResult(writer => {
            foreach (var item in items) {
                template(item).WriteTo(writer);
            }
        });
    }
}

然后在您的HTML中,您可以执行以下操作:

{
    var comics = new[] { 
        new ComicBook {Title = "Groo", Publisher = "Dark Horse Comics"},
        new ComicBook {Title = "Spiderman", Publisher = "Marvel"}
    };
}

<table>
@comics.List(
  @<tr>
    <td>@item.Title</td>
    <td>@item.Publisher</td>
  </tr>)
</table>