我正在尝试编写一个客户 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));
}
这应该可以工作,因为TableHeadersFor
can call DisplayNameExtensions.DisplayNameFor
which take an HtmlHelper<IEnumerable<TModel>>
and TableRowsFor
could take an HtmlHelper<TModel>
and call DisplayExtensions.DisplayFor
with 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。这显然非常混乱,但我宁愿有好的调用语法也不愿避免许多方法重载。