如何重载MVC HtmlHelper扩展方法

时间:2017-07-10 01:25:37

标签: c# asp.net asp.net-mvc asp.net-mvc-5

我正在尝试注册自定义MVC HtmlHelper扩展。

我已经为我的扩展方法创建了适当的静态类和静态方法,但是如何在我的视图中注册/导入/使用该命名空间,因此它显示为某些给定方法的扩展方法,@Html.SomeMethod

在Asp.Net WebForms中,我只需添加:

<%@ Import Namespace="MyExtensionNamespace.MyExtensionClassName" %>

我如何对MVC做同样的事情,以便我的Html Helper的扩展方法能够正确解析,我的方法会出现在IntelliSense中?

要清楚,我只是尝试添加现有方法的扩展,以便它们接受不同的参数作为参数,例如System.Reflection.PropertyInfo

例如,我想要System.Reflection.PropertyInfo@Html.Label

的扩展方法
    @{
    System.Reflection.PropertyInfo[] props = typeof(MyClaimDto).GetProperties();
    foreach (var prop in props)
    {
        if (prop.PropertyType != typeof(MyNamespace.DynamicDictionary))
        {
            <div class="form-group">
                @Html.LabelFor(prop, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.Editor(prop.Name, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.Hidden("Utility." + prop.PropertyType.FullName, new { @class = "form-control" } )
                    @Html.Hidden("PlaceHolder." + prop.Name, new { @class = "form-control" } )
                    @Html.ValidationMessage(prop.Name, "", new { @class = "text-danger" })
                </div>
            </div>
        }
    }


}

但VS告诉我,我的电话中仍然有一个错误,说无法推断类型参数:

enter image description here

以下是我的扩展程序的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Reflection;
using System.Linq.Expressions;
namespace EFWCF.Extensions
{
    public static class LabelExtensions
    {
        public static MvcHtmlString LabelFor<PropertyInfo, TValue>(this HtmlHelper<PropertyInfo> html, Expression<Func<PropertyInfo, TValue>> expression)
        {
            var type = expression.Type;
            MvcHtmlString result = new MvcHtmlString(type.FullName);
            return result;
        }
    }
}

如果我给该方法一个不同的名称,它将解决:

public static MvcHtmlString PropertyLabelFor<PropertyInfo, TValue>(this HtmlHelper<PropertyInfo> html)
{
    var type = expression.Type;
    MvcHtmlString result = new MvcHtmlString(type.FullName);
    return result;
}

 @Html.PropertyLabelFor(prop)

但我希望能够为其他类型调用现有的@Html方法。

2 个答案:

答案 0 :(得分:2)

您可以通过以下两种方式引用命名空间:

直接在视图中:

@using YourProject.HtmlHelpers;

或者您可以在Views/web.config文件中添加对该命名空间的引用,这样就无需将using添加到视图的顶部:

<system.web.webPages.razor>
  <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  <pages pageBaseType="System.Web.Mvc.WebViewPage">
    <namespaces>
      <add namespace="System.Web.Mvc" />
      <add namespace="System.Web.Mvc.Ajax" />
      <add namespace="System.Web.Mvc.Html" />
      <add namespace="System.Web.Routing" />
      <!-- Reference here -->
      <add namespace="YourProject.HtmlHelpers" />
    </namespaces>
  </pages>
</system.web.webPages.razor>

答案 1 :(得分:1)

无法从用法中推断出类型参数意味着在将标识符指定为方法类型参数时发生了泛型类型不匹配。此处,您的自定义扩展程序方法将System.Reflection.PropertyInfo定义为TModel

的替代
public static MvcHtmlString LabelFor<PropertyInfo, TValue>(this HtmlHelper<PropertyInfo> html, Expression<Func<PropertyInfo, TValue>> expression)
{
    var type = expression.Type;
    MvcHtmlString result = new MvcHtmlString(type.FullName);
    return result;
}

这是一个标准的HtmlHelper.LabelFor方法:

public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
   ...
}

根据我的观察,Razor似乎使用HtmlHelper.LabelFor而不是使用其自定义扩展方法重载,因此在视图页面中您尝试将PropertyInfo指定为expression标准LabelFor上的参数,它期望一个lambda表达式,导致CS0411错误。

要解决此问题,请将自定义扩展方法名称更改为另一个有效名称(避免命名冲突),或者为标准LabelFor创建一个重载,并对System.Reflection.PropertyInfo进行类型检查,如下例所示:

// note that you still require to insert PropertyInfo as a lambda expression here
public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
    var modelType = typeof(TModel);
    var propertyInfo = typeof(System.Reflection.PropertyInfo);
    if (modelType.IsAssignableFrom(propertyInfo))
    {
        var type = expression.Type;
        var tag = new TagHelper("label");
        var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
        tag.MergeAttributes(attributes);
        tag.SetInnerText(type.FullName); // set inner text between label tag
        return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
    }
    else
    {
        // return another value, e.g. return MvcHtmlString.Empty
    }
}

注意:如果您使用的方法名称与LabelFor&amp;不想将lambda表达式用作PropertyInfo作业,只需删除Expression<Func<PropertyInfo, TValue>>并直接使用PropertyInfo,如下所示:

public static MvcHtmlString PropertyLabelFor<TModel>(this HtmlHelper<TModel> html, PropertyInfo propertyInfo, object htmlAttributes)
{
    var type = propertyInfo.GetType();
    var tag = new TagHelper("label");
    var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
    tag.MergeAttributes(attributes);
    tag.SetInnerText(type.FullName); // set inner text between label tag
    return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
}

然后,在Views目录中的web.config处使用助手命名空间的引用,这样您就不需要在视图页面中使用@using指令:

<system.web.webPages.razor>
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.UI.WebControls" />
        ...
        <add namespace="EFWCF.Extensions.LabelExtensions" /> <-- add this line
      </namespaces>
    </pages>
</system.web.webPages.razor>

其他参考资料:

How can I override the @Html.LabelFor template?

How to extend MVC3 Label and LabelFor HTML helpers?