通用局部视图:如何将通用类设置为模型?

15

我正在尝试在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>>" %>

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

编辑:

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

感谢大家帮助我走上正确的道路。

4个回答

7
您可以让所有传递到此部分视图的模型类型都继承自一个基类/接口,该基类/接口建立了此部分视图将使用的基本行为,并接受该类/接口类型的任何对象,或者只是让视图接受任何类型的对象,然后使用反射来基于您的部分视图行为。
例如:
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会根据你所期望的行为而变化。这将允许你传递任何实现了此基础接口的内容到此部分以建立一般行为。


你能否举个简短的例子,说明如何实现反射解决方案? - Thomas Stock
添加了示例,并将控件继承属性更改为接受“ITrustGrid<IDisplayModel>”。希望这是您要寻找的内容。 - Adam Carr
谢谢,但我不喜欢这种解决方案,因为我必须使每个类实现IDisplayModel接口。 我更喜欢一种解决方案,在局部视图中添加一些内容即可使其正常工作。也许我可以在我的ITrustGrid接口中添加一个Type成员?目前我正在尝试这样做,但我对这种泛型技术还很陌生:) - Thomas Stock
你可以在视图中使用反射,并将你的视图模型ITrustGrid<Object>,然后使用反射来确定类型并相应地显示逻辑在你的部分内,但这会使视图比我想要的更加臃肿。祝好运。 - Adam Carr
啊,我想不出来了。尝试了各种方法,但是无法获取模型的类型。我尝试在我的接口中添加一个“Type”成员,但那也没用。 - Thomas Stock

5

不能像那样做。原因是.aspx会生成一个你无法控制并且无法添加泛型参数的类。我想最直接的方法是将其作为object传递。


嗯,我尝试过了,但是没能想出如何将它转换回有用的东西。 - Thomas Stock
1
你几乎不能将它转换为任何有用的东西,但你仍然可以将其用作动态对象。 - Dovydas Navickas

1

我同意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>
    <%
}
%>

当然,上面的代码只渲染了每个元素的一个单元格,你需要创建一个稍微复杂一些的委托来渲染多列(或者传入一个委托数组,每列一个委托)。
我认为这是其中一种最简洁的方法,尽管有点繁琐。

0

@ 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>

正如您所看到的,我的部分代码并不依赖于使用的类型。因此,我仍然认为有一个简单的解决方案。


由于您的部分不依赖于使用的类型,所以可以使用Object吗?ITrustGrid<Object>? - Adam Carr
当使用TrustGrid<EmployeeInfoDTO>调用部分视图时,我会收到以下错误信息:“传递到字典中的模型项的类型为'Helpers.TrustGrid1[LinqToSqExample.Model.EmployeeInfoDTO]',但此字典需要类型为'Helpers.ITrustGrid1[System.Object]'的模型项。” - Thomas Stock
当我使用 TrustGrid<object> 调用 partial 时,模型为 null。 - Thomas Stock
1
如果我在TrustGrid(具体类)上继承ITrustGrid<customtype>,我会得到与您上面描述的相同结果。如果我将TrustGrid的继承更改为ITrustGrid<Object>,那么它对我有效。 - Adam Carr
明天我会尝试一下。感谢你的关注。 - Thomas Stock
我已经修改了我的代码。TrustGrid<T>现在是TrustGridBuilder<T>,它有一个.GetTrustGrid方法,返回一个TrustGrid实例。TrustGrid实例有一个IEnumerable<string> Header和一个IEnumerable<IEnumerable<string>> Elements成员。 - Thomas Stock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接