我正在试图对一个复杂的控件进行泛化,这个控件经常在我的网站中使用但具有不同的字段。控件中的功能始终相同,只是更改的基础字段。
为了实现显示不同字段的方法,我试图创建一个HTMLHelper扩展,它接受Expression<Func<TModel,TProperty>>
作为参数,该参数将包含在控件中显示所需的类的属性。例如:
观点:
@model Project.Core.Page
@Html.MyHelper(p => new { p.Author.Name, p.Author.Location, p.Author.Age });
这是我遇到问题的扩展 - 如何迭代lambda中提供的参数,为每个提供TextBoxFor()
,或者甚至手动创建input
元素并填充它lambda参数的value
和name
?
psuedo中的扩展:
public static MvcHtmlString MyHelper<TModel,TProperty>(
this HtmlHelper<TModel> helper,
Expression<Func<TModel,TProperty>> expression) {
foreach (var parameter in expression.???) {
// helper.TextBoxFor(???)
// TagBuilder("input").Attributes("name", expression.???)
}
}
我觉得我已经长时间盯着这个了,而且我也觉得有一个更简单的方法,我忽略了实现这个目标。
非常感谢任何帮助。如果您需要更多详细信息,或者我错过了重要信息,请告诉我。
答案 0 :(得分:2)
您创建的表达式将相对复杂 - 它需要获取所有属性,然后调用匿名类型构造函数。 “反汇编”可能会让人感到痛苦......虽然如果你还想尝试,我建议只留下一个空方法实现并查看调试器,看看表达式是什么样的。
如果您愿意接受一种稍微丑陋的调用代码形式,那么实现它将会更简单
:@Html.MyHelper(p => p.Author.Name, p => p.Author.Location, p => p.Author.Age);
您可以使用params Expression<TModel, object>
或者您可以使用不同数量的参数声明多次重载,例如
// Overload for one property
MyHelper<TModel, TProperty1>(this ..., Expression<Func<TModel, TProperty1>> func1)
// Overload for two properties
MyHelper<TModel, TProperty1, TProperty2>(this ...,
Expression<Func<TModel, TProperty1>> func1,
Expression<Func<TModel, TProperty2>> func2)
等
答案 1 :(得分:2)
如果您采取以下措施:
MemberExpressions
,并且不包含对模型或其子项上的方法的调用然后,您可以使用以下方法实现您想要的目标:
修改强>
在意识到我的第一个示例无法处理具有复杂属性的对象后,我更新了代码以使用辅助方法来访问属性值。此方法使用递归遍历属性链以返回适当的值。
public static MvcHtmlString MyHelper<TModel,object>(
this HtmlHelper<TModel> helper,
Expression<Func<TModel,object>> expression) {
var newExpression = expression.Body as NewExpression;
TModel model = helper.ViewData.Model;
foreach (MemberExpression a in newExpression.Arguments) {
var propertyName = a.Member.Name;
var propertyValue = GetPropertyValue<TModel>(model, a);
// Do whatever you need to with the property name and value;
}
}
private static object GetPropertyValue<T>(T instance, MemberExpression me) {
object target;
if (me.Expression.NodeType == ExpressionType.Parameter) {
// If the current MemberExpression is at the root object, set that as the target.
target = instance;
}
else {
target = GetPropertyValue<T>(instance, me.Expression as MemberExpression);
}
// Return the value from current MemberExpression against the current target
return target.GetType().GetProperty(me.Member.Name).GetValue(target, null);
}
注意:我没有在IDE中直接将其作为MVC扩展方法实现,因此可能需要略微改变语法。
答案 2 :(得分:1)
也许构建器风格的API可能会简化一些事情:
@(Html.MyHelper(p)
.Add(p => p.Author.Age)
.Add(p => p.Author.LastName, "Last Name")
.Build())
请注意,这允许您在需要时添加可选参数。
代码看起来像这样
public static class Test
{
public static Helper<TModel> MyHelper<TModel>(this HtmlHelper helper, TModel model)
{
return new Helper<TModel>(helper, model);
}
}
public class Helper<TModel>
{
private readonly HtmlHelper helper;
private readonly TModel model;
public Helper(HtmlHelper helper, TModel model)
{
this.helper = helper;
this.model = model;
}
public Helper<TModel> Add<TProperty>(Expression<Func<TModel, TProperty>> expression)
{
// TODO
return this;
}
public MvcHtmlString Build()
{
return new MvcHtmlString("TODO");
}
}
答案 3 :(得分:1)
考虑一下:
创建App_Code文件夹
放入剃须刀助手文件Templates.cshtml。
如下所示:
@helper Print(string myvalue,string special="")
{
<pre> <input id="id" type="text" value ="@myvalue" data-val="@special"/> </pre>
}
这样您就不必在C#文件中编写HTML。它非常方便。
Authors.cshtml如下所示:
@model IEnumerable<MvcApplication1.Models.Author>
@{
ViewBag.Title = "Authors";
}
<h2>Authors</h2>
@foreach(var auth in Model)
{
@Templates.Print(auth.Name);
}
books.cshtml如下所示:
@model IEnumerable<MvcApplication1.Models.Book>
@{
ViewBag.Title = "Books";
}
<h2>Books</h2>
@foreach(var book in Model)
{
@Templates.Print(book.Title,book.ISBN);
}
根据模型类的需要插入所有特殊属性。如果它太复杂,请查看dynamic和expando对象。这取决于您的模型/视图模型的复杂程度。