【问题标题】:Generic partial view: how to set a generic class as model?泛型部分视图:如何将泛型类设置为模型?
【发布时间】:2010-10-26 21:09:01
【问题描述】:

我正在尝试在 ASP.NET MVC 应用程序中构建通用网格视图。

让我用一些代码解释一下:

public interface ITrustGrid<T>
{
    IPagedList<T> Elements { get; set; }
    IList<IColumn<T>> Columns { get; set; }
    IList<string> Headers { get; }
}

这是一个类的接口,允许我在控制器中设置列和表达式。

我将实现传递给这样的局部视图:

<% Html.RenderPartial("SimpleTrustGridViewer", ViewData["employeeGrid"] as TrustGrid<EmployeeInfoDTO>); %>

问题是我不知道如何制作渲染网格通用的局部视图。

也就是说,我想转这个:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ITrustGrid<EmployeeInfoDTO>>" %>

变成这样:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ITrustGrid<T>>" %>

=> 如何以最简单的方式使我的局部视图通用?

编辑:

我通过使用具有公共 TrustGrid GetTrustGrid() 方法的 TrustGridBuilder 解决了这个问题,该方法返回非泛型 TrustGrid。 TrustGrid 包含字符串而不是 linq 的东西。所以我在 GetTrustGrid() 方法中执行 linq 并将字符串放入 TrustGrid 对象中。

感谢大家帮助我走上正轨。

【问题讨论】:

    标签: c# asp.net-mvc generics


    【解决方案1】:

    您可以使您将传递给此部分的所有模型类型都从基类/接口继承,该基类/接口建立了此部分视图将使用的基本行为并接受该类/接口类型的任何对象,或者只是让视图接受任何类型的对象,然后使用反射来建立部分视图行为的基础。

    示例:

    public interface IDisplayModel
    {
        string DisplayText{get;set;}
        string ImageUrl{get;set;}
        string AltText{get;set;}
    }
    
    public interface ITrustGrid<T> where T : IDisplayModel
    {    
        IPagedList<T> Elements { get; set; }    
        IList<IColumn<T>> Columns { get; set; }    
        IList<string> Headers { get; }
    }
    
    <%@ Control Language="C#" 
        Inherits="System.Web.Mvc.ViewUserControl<ITrustGrid<IDisplayModel>>" %>
    

    当然,您的IDisplayModel 会根据您想要的行为而有所不同。然后,这将允许您将任何内容传递给实现此基本接口的此部分以建立一般行为。

    【讨论】:

    • 您能否举一个关于如何实现反射解决方案的简短示例?
    • 添加示例并将控制继承属性更改为接受“ITrustGrid”希望这是您正在寻找的。​​span>
    • 谢谢,但我不喜欢这个解决方案,我必须让每个类都实现 IDisplayModel 接口。我更喜欢一个解决方案,我可以在局部视图中添加一些东西以使其工作。也许我可以在我的 ITrustGrid 接口中添加一个 Type 成员?我现在正在尝试类似的东西,但我对这个通用的东西很陌生:)
    • 您可以在视图中使用反射并拥有您的视图模型 ITrustGrid 然后使用反射来确定类型并在您的局部中相应地显示逻辑,但这会使视图比我更喜欢的臃肿.祝你好运。
    • 啊,我想不通.. 一直在尝试各种事情,但无法获得模型的类型。我尝试在我的界面中添加一个“类型”成员,但这也不起作用..
    【解决方案2】:

    那样做是不可能的。原因是.aspx 将生成一个您对其没有太多控制权的类,并且您无法向其添加通用参数。我想最直接的方法是将其传递为object

    【讨论】:

    • 嗯我试过了,但无法弄清楚如何将其转换为有用的东西。
    • 你几乎不能将它转换成任何有用的东西,但你仍然可以将它用作动态对象。
    【解决方案3】:

    我同意 Mehrdad 的观点,据我所知,不可能做出通用的观点。在我的一个项目中,我使用了一个与您的接口非常相似的接口,然后将委托函数传递给处理每个项目的特定呈现的视图。

    例如,我会使用带有附加字段的非通用视图数据类:

    public interface ITrustGrid {
        IPagedList Elements { get; set; }
        IList<IColumn> Columns { get; set; }
        IList<string> Headers { get; }
    
        Func<object, string> ElementRenderer { get; }
    }
    

    在您的主视图中,您将准备视图数据:

    <%
    ITrustGrid data = (ITrustGrid)ViewData["employeeGrid"];
    data.ElementRenderer = new Func<object, string>(delegate(o) {
        var employee = (Employee)o;
        //render employee
        return html;
    });
    
    Html.RenderPartial("SimpleTrustGridViewer", data);
    %>
    

    在您的部分网格中,您将照常处理网格,然后调用委托来渲染每个单元格:

    <%
    foreach(var element in ViewData.Elements){
        %>
        <tr>
            <td><%=ViewData.ElementRenderer(element) %></td>
        </tr>
        <%
    }
    %>
    

    当然,上面的代码只为每个元素渲染一个单元格,您必须创建一个稍微复杂一点的委托来渲染多列(或者传入一个委托数组,每列一个)。

    我认为这将是最干净的方法之一,尽管有点麻烦。

    【讨论】:

      【解决方案4】:

      @Lck:

      我正在我的控制器中做类似的事情:

      var columns = new List<IColumn<EmployeeInfoDTO>>
                        {
                            new Column<EmployeeInfoDTO>("Full name", e => string.Format("{0} {1}", e.FirstName, e.Name)),
                            new Column<EmployeeInfoDTO>("Examination date", e => e.ExaminationDate.HasValue? string.Format("{0} days ago", currentDate.Subtract(e.ExaminationDate.Value).Days) : "Unknown")
                        };
      
      var employeeGrid = new TrustGrid<EmployeeInfoDTO> { Columns = columns, Elements = GetEmployees(currentPageIndex)};
      
      ViewData["employeeGrid"] = employeeGrid;
      

      所以在我的部分观点中,我可以这样做:

      <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ITrustGrid<EmployeeInfoDTO>>" %>
      <table>
          <thead>
              <tr>
                  <%
                      foreach (string header in Model.Headers)
                          Response.Write(Html.Th(header));
                  %>
              </tr>
          </thead>
          <tbody>
              <%
                  foreach (var element in Model.Elements)
                  {
                      Response.Write("<tr>");
                      foreach (var column in Model.Columns)
                          Response.Write(Html.Td(column.ValueExpression(element)));
                      Response.Write("</tr>");
                  }
              %>
          </tbody>
      </table>
      <div class="pager">
          <%= Html.Pager(Model.Elements.PageSize, Model.Elements.PageNumber, Model.Elements.TotalItemCount)%>
      </div>
      

      如您所见,我的部分代码中没有任何代码取决于所使用的类型。所以我仍然认为有一个简单的解决方案。

      【讨论】:

      • 既然你的partial不依赖于使用的类型,你可以只使用Object吗? ITrustGrid?
      • 我得到“传入字典的模型项的类型是'Helpers.TrustGrid1[LinqToSqExample.Model.EmployeeInfoDTO]' but this dictionary requires a model item of type 'Helpers.ITrustGrid1[System.Object]'。”使用 TrustGrid 调用局部视图时
      • 当我使用 TrustGrid 调用部分时,模型为空
      • 如果我从 TrustGrid (具体类)上的 ITrustGrid 继承,我会得到与上述相同的结果。如果我将我的 TrustGrid 继承更改为 ITrustGrid,那么它对我有用。
      • 嗯明天试试。感谢您查看。
      猜你喜欢
      • 2011-03-21
      • 1970-01-01
      • 1970-01-01
      • 2016-09-06
      • 2019-02-26
      • 2020-04-04
      • 2020-08-06
      • 1970-01-01
      • 2011-08-28
      相关资源
      最近更新 更多