2012-01-11 2 views
4

컨텍스트 : 우리는 C# MVC3에서 개발 중이므로 웹 페이지의 테이블을 처리하도록 설계된 클래스가 필요했습니다. (페이지 매김/검색/etc ...).람다 식 지정 소스 객체

다른 모든 개체를 유지하고 현재 페이지/현재 검색 등 ... (기타 정보)를 알고있는 것 테이블 개체를

:

그래서 우리는 마침내 다음 수업을 위해 최선을 할 수 있다는 것을 발견
public class Table<T> where T : IPrivateObject 
{ 
    ... 

    public ICollection<Column<T>> Columns { get; set; } 
    public ICollection<Row<T>> Rows { get; set; } 
    public ICollection<RowMenu<T>> Menus { get; set; } 

    public ICollection<T> Items { get; set; } 

    public Table(
     ICollection<T> inputItems, 
     ICollection<Column<T>> columns, 
     ICollection<RowMenuItem<T>> rowMenuItems, 
     ...) 
     { 
      ... 
      this.Columns = columns; 
     } 

속성이 표시되어야하는 알고 열 객체와 헤더 값

public class Column<T> where T : IPrivateObject 
{ 
    public string Value { get; set; } 
    public Expression<Func<T, object>> Property { get; set; } 

    public Column(Expression<Func<T, object>> property, string value) 
    { 
     this.Property = property; 
     this.Value = value; 
    } 
} 

다른 수업은 정말 재미 없습니다 그래서 나는 그들을 여기에 게시하지 않을 것이다.

컨트롤러에서, 우리는 그런 식으로이 클래스를 사용 : 우리는 지금 제대로 테이블을 표시하는 도우미를 작성하는

public ActionResult Index(string search = null, string sort = null, int order = 1, int take = 10, int page = 1) 
    { 
     ICollection<Person> people = prismaManager.PersonManager.Search(search); 
     ICollection<Column<Person>> columns= new List<Column<Person>>(); 
     columns.Add(new Column<Person>(Person => Person, "Person")); 
     columns.Add(new Column<Person>(Person => Person.LastMembershipApproval, "Last Membership approval")); 
     Table<Person> table = people.ToTable(columns); 
    } 

. 헤더에 대해서는 잘 작동하지만 @Html.DisplayFor() 도우미를 사용하려면 표현식에 문제가 있습니다. 이를 위해

private static string TableRows<T>(HtmlHelper<Table<T>> helper, Table<T> table) where T : IPrivateObject 
    { 
     StringBuilder sb = new StringBuilder(); 
     foreach (var item in table.Items) 
     { 
      sb.AppendLine("<tr>"); 
      foreach (var column in table.Columns) 
      { 
       sb.AppendLine("<td>"); 
       sb.AppendLine(helper.DisplayFor(obj => ???).ToString()); // How should I use the Expression that is stored in the column but for the current element ? 
       sb.AppendLine("</td>"); 
      } 
      sb.AppendLine("</tr>"); 
     } 
     return sb.ToString(); 
    } 

는 일을 우리는 현재 항목으로 열에 저장된 표현에서 "사람"매개 변수의 값을 설정해야합니다

이것은 우리가 현재 콘텐츠를 가지고있는 것입니다.

new Column<Person>(Person => Person, "Person")); 

어떻게해야합니까? 우리는 (가능한 경우) 표현식을 수정하여 값을 설정해야합니까? 이전 식을 기본 식으로 사용하여 새 식을 다시 만들어야합니까?

저는 3 일 동안 검색 중이며 어떤 답변도 찾을 수 없습니다.

도움 주셔서 감사합니다.

UPDATE :

문제 헬퍼 형 Html 헬퍼>하지 Html 헬퍼 인 것이 (@Groo & @Darin 디미트로프 말했듯)이다. HtmlHelper에서 HtmlHelper를 가져 오는 방법에 대한 아이디어가 있습니까?>

UPDATE :

Person 클래스는 다음과 같습니다 :

public class Person : IPrivateObject 
{ 
    public int Id { get; set; } 
    public int? AddrId { get; set; } 

    [DisplayName("First Name")] 
    [StringLength(100)] 
    [Required] 
    public string FirstName { get; set; } 

    [DisplayName("Last Name")] 
    [StringLength(100)] 
    [Required] 
    public string LastName { get; set; } 

    [DisplayName("Initials")] 
    [StringLength(6)] 
    public string Initials { get; set; } 

    [DisplayName("Last membership approval")] 
    public Nullable<DateTime> LastMembershipApproval { get; set; } 

    [DisplayName("Full name")] 
    public string FullName 
    { 
     get 
     { 
      return FirstName + " " + LastName; 
     } 
    } 
    public override string ToString() 
    { 
     return FullName; 
    } 
} 

답변

4

여기 계속 진행할 수있는 방법입니다. 당신이 필요로되는 단지 HtmlHelper<T>의 인스턴스를 다음

public class ViewDataContainer : IViewDataContainer 
{ 
    public ViewDataContainer(ViewDataDictionary viewData) 
    { 
     ViewData = viewData; 
    } 

    public ViewDataDictionary ViewData { get; set; } 
} 

과 :처럼 간단 할 수있는 사용자 지정보기 데이터 컨테이너 구현을 작성하여 시작

private static string TableRows<T>(HtmlHelper<Table<T>> helper, Table<T> table) where T : IPrivateObject 
{ 
    var sb = new StringBuilder(); 
    sb.AppendLine("<table>"); 
    foreach (var item in table.Items) 
    { 
     sb.AppendLine("<tr>"); 
     foreach (var column in table.Columns) 
     { 
      var viewData = new ViewDataDictionary<T>(item); 
      var viewContext = new ViewContext(
       helper.ViewContext.Controller.ControllerContext, 
       helper.ViewContext.View, 
       new ViewDataDictionary<T>(item), 
       helper.ViewContext.Controller.TempData, 
       helper.ViewContext.Writer 
      ); 
      var viewDataContainer = new ViewDataContainer(viewData); 
      var itemHelper = new HtmlHelper<T>(viewContext, viewDataContainer); 

      sb.AppendLine("<td>"); 
      sb.AppendLine(itemHelper.DisplayFor(column.Property)); 
      sb.AppendLine("</td>"); 
     } 
     sb.AppendLine("</tr>"); 
    } 
    sb.AppendLine("</table>"); 
    return sb.ToString();  
} 

UPDATE :

열의식이 유형이 Expression<Func<T, object>>이고 값 형식 속성을 가리키고 있기 때문에 이전 예제에서는 값 형식을 처리하지 않았습니다. e 값은 박스로 표시되며 ASP.NET MVC에서는 템플릿 도우미와 함께 이러한 표현식을 사용할 수 없습니다. 이 문제를 해결하려면 값이 박스형인지 테스트하고 실제 형식을 추출하십시오.

sb.AppendLine("<td>"); 
var unary = column.Property.Body as UnaryExpression; 
if (unary != null && unary.NodeType == ExpressionType.Convert) 
{ 
    var lambda = Expression.Lambda(unary.Operand, column.Property.Parameters[0]); 
    sb.AppendLine(itemHelper.Display(ExpressionHelper.GetExpressionText(lambda)).ToHtmlString()); 
} 
else 
{ 
    sb.AppendLine(itemHelper.DisplayFor(column.Property).ToHtmlString()); 
} 
sb.AppendLine("</td>"); 
+0

도움을 주셔서 감사합니다. 이는 Person => Person 표현식에서 잘 작동합니다. 그러나 Person의 속성에는 해당되지 않습니다. 템플릿은 필드 액세스, 속성 액세스, 단일 차원 배열 인덱스 또는 단일 매개 변수 사용자 지정 인덱서 식에서만 사용할 수 있습니다. 예외는 있습니다. "Person => Person.LastMembershipApproval"이라는 표현식이 바뀔 때 ... 평가할 이유가 무엇입니까? – Whoami

+0

@Whoami, 이상하게도, 테스트했을 때 작동했습니다. 문제를 보여주는 샘플 프로젝트를 보내 주면 도움이 될 것입니다. –

+0

확인할 수 있도록 Person 클래스를 추가하겠습니다. ** 업데이트 ** 클래스가 추가되었습니다. – Whoami

1

당신은 (당신이 당신의 column.Property의 속성 식을) expression.Compile()를 사용하여 식을 컴파일해야합니다. 이것은 당신에게 대의원을 줄 것입니다. 거기에 객체를 전달하고 값을 얻을 수 있습니다. 사람이나 T를 도우미 메서드에 전달해야합니다.

+0

답장을 보내 주셔서 감사합니다.하지만이 방법은 효과가 없습니다. 다음과 같은 예외가 있기 때문에 DisplayFor 템플릿과 함께 사용할 수 없습니다. 템플릿은 필드 액세스, 속성 액세스, 단일 차원 배열 인덱스 또는 단일 매개 변수 사용자 지정 인덱서 식에만 사용할 수 있습니다. – Whoami

+0

T 타입의 새로운 HtmlHelper를 어떻게 형변환/인스턴스화하여 DisplayFor에 사용할 수 있는지 생각해보십시오. – Whoami

+0

오! 그래서 바인딩에 사용하고 싶습니까? 람다 식에서 속성 이름이 필요합니까? – ivowiblo

2

변경해야 할 몇 가지 사항이 있습니다.

  1. 처음으로 나를 놀라게 것은 테이블이 열 및 목록이 모두 있다는 것입니다. 디자인을 다음과 같이 변경해야합니다. 테이블에 행 목록이 있고 각 행에는 열 목록이 있습니다 (또는 그 반대).

    그러나이 설명은 그다지 적합하지 않습니다. "열"은 "열 정의"와 같은 것으로 데이터가 포함되어 있지 않다고 생각하지만이 경우에는 ICollection<T> 대신 ICollection<Row<T>>이 표시 될 필요가 없습니다.

  2. Expression<Func<T, object>> 대신 Func<T, object>과 같은 대리자를 저장하려고합니다.

  3. Property에는 최소한 개인 설정자 (또는 더 나은 읽기 전용 배경 필드가 있어야 함)가 있어야합니다. 이것은 코드의 다른 부분을 변경하기를 원하는 것이 아닙니다.

  4. 이름이 매우 혼란스러운 IMHO입니다. 나는 더 나은 속성 이름을 선택합니다. 내가 맞으면 ValueProperty을 각각 HeaderNameGetValue이라고 불러야합니다.

    public class Column<T> where T : IPrivateObject 
    { 
        private readonly string _name; 
        private readonly Func<T, object> _valueGetter; 
    
        /// <summary> 
        /// Gets the column name. 
        /// </summary> 
        public string Name 
        { 
         get { return _name; } 
        } 
    
        /// <summary> 
        /// Gets the value of this column from the 
        /// specified object. 
        /// </summary> 
        /// <param name="obj">The object.</param> 
        /// <returns></returns> 
        public object GetValueFrom(T obj) 
        { 
         return _valueGetter(obj); 
        } 
    
        public Column(string columnName, Func<T, object> valueGetter) 
        { 
         _name = columnName; 
         _valueGetter = valueGetter; 
        } 
    } 
    

    를 그리고 단순히 루프에서 이것을 사용 :

이 모두가 그렇게 말한다면, 나는 이런 식으로 뭔가에 Column을 바꿀 것

sb.AppendLine(column.GetValueFrom(item).ToString()); 
+0

빠른 답장을 보내 주셔서 감사합니다! 그러나 1. 나는 테이블이 칼럼에 의해 움직이기 때문에 동의하지 않는다. 행은 행에 특별한 것이있는 경우에만 여기에 있습니다 (예 :personId % 2 == 0 인 행은 빨간색으로 표시되어야합니다. 나머지는 동의하며 귀하의 회신에 감사드립니다. 감사합니다. – Whoami

+0

@Whoami : 답변을 업데이트했습니다. 마지막 줄이 네가 묻고있는 것 같아. – Groo

+0

그래, 그게 내가 물어 보는거야. 그 방법으로 시도해보고 DisplayFor 도우미와 함께 사용할 수 있는지 확인합니다. – Whoami