4

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

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

我首先尝试通过首先传递一个表达式来使其工作,例如

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

一旦可行,我将使用参数并采用多个表达式来使用逗号分隔的列表。

我的视图代码看起来像这样

@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 是 forHtmlHelper<IEnumerable<TModel>>而使用DisplayFor它时需要是HtmlHelper<TModel>

由于这个错误,我可以通过将表头和数据分成两个单独的方法调用来实现这个工作,从我的角度来看,我可以调用表头方法,然后在实际视图中有一个 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));
}

这应该可以工作,因为TableHeadersForcan call DisplayNameExtensions.DisplayNameForwhich take an HtmlHelper<IEnumerable<TModel>>and TableRowsForcould take an HtmlHelper<TModel>and call DisplayExtensions.DisplayForwith anHtmlHelper<TModel>因此不兼容的 HtmlHelper 类型问题将消失。

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

我在 ILSpy 中查看了它是如何DisplayNameExtensions.DisplayNameFor处理HtmlHelper<IEnumerable<TModel>>的,因为它看起来好像以某种方式采用的扩展方法正在HtmlHelper<IEnuermable<TModel>>内部调用扩展方法,HtmlHelper<TModel>但是 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 委托时遇到的问题相同,他们必须为不同数量的参数编写不同的版本,例如 Action T1、T2、..、T16。这显然非常混乱,但我宁愿有好的调用语法也不愿避免许多方法重载。

4

1 回答 1

1

查看这篇文章以获得一些方向并根据您的需要进行修改。

首先你需要一个这样的 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>
于 2013-07-24T00:58:08.540 回答