2014-07-09 109 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