【问题标题】:Custom MVC HtmlHelper that uses existing HtmlHelpers with Expressions使用现有 HtmlHelper 和表达式的自定义 MVC HtmlHelper
【发布时间】:2014-07-09 16:06:30
【问题描述】:

我正在尝试制作一个自定义 HtmlHelper,它将在现有 HtmlHelper 的帮助下生成多个 HTML 元素,例如 Html.TextBoxForHtml.LabelFor。我遇到的主要问题是我无法创建这些现有助手使用的表达式,除非它们都单独传递到我的自定义 HtmlHelper 中。我希望能够将整个模型传递给自定义 HtmlHelper 并在帮助器内部创建表达式并将它们传递给现有的。我正在尝试这样做,希望这些属性仍然有效,例如 DisplayName 的必需和国际化。这可能吗?

我知道我可以通过表达式将每个 Address 属性传递给自定义 HtmlHelper 来做到这一点,但它不像在帮助程序中完成那样干净和易于使用。

地址模型

public class Address
{
    [Required(ErrorMessage = "Required"), DisplayName("First Name")]
    public String FirstName { get; set; }

    [Required(ErrorMessage = "Required"), DisplayName("Last Name")]
    public String LastName { get; set; }

    [Required(ErrorMessage = "Required"), DisplayName("Line 1")]
    public String Line1 { get; set; }

    [Required(ErrorMessage = "Required"), DisplayName("Line 2")]
    public String Line2 { get; set; }

    [Required(ErrorMessage = "Required"), DisplayName("City")]
    public String City { get; set; }

    [Required(ErrorMessage = "Required"), DisplayName("State")]
    public String State { get; set; }

    [Required(ErrorMessage = "Required"), DisplayName("Country")]
    public String Country { get; set; }

    [Required(ErrorMessage = "Required"), DisplayName("Postal Code")]
    public String PostalCode { get; set; }
}

自定义 HtmlHelper

public static class AddressExtension
{
    public static MvcHtmlString AddressEditorForModel<TModel>(this HtmlHelper<TModel> helper, Address address, String prefix, Boolean showLabels)
    {          
        // There will be more markup in here, this is just slimmed down..

        StringBuilder sb = new StringBuilder();
        sb.AppendLine(String.Format("<div id='{0}_AddressContainer' >", prefix));

        // First Name
        if (showLabels)
            sb.AppendLine(helper.DisplayFor(????).ToString());            
        sb.AppendLine(helper.EditorFor(????).ToString());

        // Last Name
        if (showLabels)
            sb.AppendLine(helper.DisplayFor(????).ToString());            
        sb.AppendLine(helper.EditorFor(????).ToString());

        // Line 1
        if (showLabels)
            sb.AppendLine(helper.DisplayFor(????).ToString());            
        sb.AppendLine(helper.EditorFor(????).ToString());

        // Line 2
        if (showLabels)
            sb.AppendLine(helper.DisplayFor(????).ToString());            
        sb.AppendLine(helper.EditorFor(????).ToString());

        // City
        if (showLabels)
            sb.AppendLine(helper.DisplayFor(????).ToString());            
        sb.AppendLine(helper.EditorFor(????).ToString());

        // State
        if (showLabels)
            sb.AppendLine(helper.DisplayFor(????).ToString());            
        sb.AppendLine(helper.EditorFor(????).ToString());

        // Country
        if (showLabels)
            sb.AppendLine(helper.DisplayFor(????).ToString());            
        sb.AppendLine(helper.EditorFor(????).ToString());

        // Postal Code
        if (showLabels)
            sb.AppendLine(helper.DisplayFor(????).ToString());            
        sb.AppendLine(helper.EditorFor(????).ToString());

        sb.AppendLine("</div>");

        return MvcHtmlString.Create(sb.ToString());
    }

查看(cshtml)

@Html.AddressEditorForModel(Model, "test", true)

【问题讨论】:

    标签: c# lambda asp.net-mvc-5 html-helper data-annotations


    【解决方案1】:

    您可以像这样使用反射和表达式树来做到这一点:

    public static MvcHtmlString AddressEditorForModel<TModel>(this HtmlHelper<TModel> helper, string prefix, bool showLabels)
    {          
        // There will be more markup in here, this is just slimmed down..
    
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(String.Format("<div id='{0}_AddressContainer' >", prefix));
    
             //Create a parameter expression for the model type
             ParameterExpression paramExpr = Expression.Parameter(typeof (TModel));
             //Loop through the properties you want to create a DisplayFor for
             foreach (var property in typeof (TModel).GetProperties())
             {
                 //Create a member access expression for accessing this property
                 MemberExpression propertyAccess = Expression.MakeMemberAccess(paramExpr, property);
                 //Create the lambda expression (eg. "x => x.Name")
                 var lambdaExpr = Expression.Lambda<Func<TModel, string>>(propertyAccess, paramExpr);
                 //Pass this lambda to the DisplayFor method
                 sb.AppendLine(helper.DisplayFor(lambdaExpr).ToString());
    
             }
    
    ...rest of your method
    

    但是,这假设 TModel 是您的 Address 对象,因此您也不需要传入它。HTML 中的 TModel helpers 来自强类型视图,所以如果你有一个 @model 类型为 Address 的视图,那么你可以使用上面的方法

    视图会这样使用它:

    @model Address
    
    @Html.AddressEditorForModel("prefix", true)
    

    【讨论】:

    • 这很好用,谢谢!我不太确定 TModel 是如何工作的,所以谢谢你的解释。有没有办法只允许它与传入的地址而不是通用 TModel 一起使用?
    • 您可以在方法声明上放置一个泛型类型约束“where TModel : address”。这意味着您的方法只会在智能感知中显示“Html”。模型为地址时的助手。有关泛型类型约束的更多信息,请参见此处:msdn.microsoft.com/en-gb/library/d5x73970.aspx
    • 完美!非常感谢您的所有帮助:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-05
    • 1970-01-01
    • 2014-04-27
    • 1970-01-01
    • 1970-01-01
    • 2010-11-23
    相关资源
    最近更新 更多