2014-07-09 26 views
0

我正在尝试制作一个自定义HtmlHelper,它将在现有HtmlHelpers(如Html.TextBoxForHtml.LabelFor)的帮助下生成多个HTML元素。我正在运行的主要问题是我无法创建表达式以供这些现有的帮助器使用,除非它们全部单独传递到我的自定义HtmlHelper中。我希望能够将整个模型传递给自定义HtmlHelper,并在帮助器中创建表达式并将它们传递给现有的表达式。我试图这样做,希望这些属性仍然有效,例如DisplayName的Required和Internationalization。这可能吗?自定义MVC HtmlHelper使用现有HtmlHelpers的表达式

我知道我可以通过将每个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) 

回答

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是你地址对象,所以你也不需要将它传递该的TModel在HTML佣工来自一个强类型的视图,所以如果你有一个观点,其中该@model类型是地址那么你可以使用,上述方法

视图会使用这样的:

@model Address 

@Html.AddressEditorForModel("prefix", true) 
+0

这工作得很好,谢谢!我不太清楚TModel是如何工作的,所以谢谢你的解释。有没有办法只允许它与传入的地址一起使用,而不是通用的TModel? – WebDevNewbie

+0

您可以在方法删除中放置一个泛型类型约束“where TModel:address”。这意味着你的方法只会在“Html”的intellisense中显示。当模型是Address时助手。有关泛型类型约束的更多信息,请参见此处: http://msdn.microsoft.com/en-gb/library/d5x73970.aspx – jcharlesworthuk

+0

完美!非常感谢你的帮助:) – WebDevNewbie