使用 DevExpress ASP.NET MVC 扩展实现主从明细
强制使用 DevExpress ASP.NET MVC V2010 vol 2 扩展实现主从明细
引言
主从明细网格是用户界面中根深蒂固的明星功能。不幸的是,如果您使用 ASP.NET MVC 2 和 Developer Express ASP.NET MVC 扩展的当前版本 V2010 vol 2 来开发 Web 应用程序(参见参考文献 1 和 2),您将无能为力——至少在您找到这篇短文之前是这样。
我们相信有一天 Developer Express 会提供一个绝佳的解决方案来满足这个需求。供应商意识到了这个需求,他们承诺未来会提供这个功能:“不幸的是,MVCxGridView
不支持这样的功能。我决定将这个问题转换为一篇建议文章并转发给我们的开发人员,以便他们进行审查。我们将在 2010 年 5 月 28 日(参见参考文献 3)得到 Vest 的答复,届时我们将告知您结果。”
目前,您需要阅读本文的其余部分,并在代码中做一些额外的工作,才能在 MVC Web 项目的 DevExpress GridView 中实现主从明细。
作者注(2011 年 5 月 31 日):事情就这样发生了。Developer Express 宣布,即将发布的 Developer Express ASP.NET MVC 扩展 V2011 vol 1 的一部分将包含 ASP.NET MVC GridView - 主从明细网格布局(参见参考文献 5)。该版本肯定会使读者免于使用本文描述的技巧。
背景
我们假设您熟悉 ASP.NET MVC 2 开发,并且您已经
- 安装了 Microsoft Visual Studio 2010(我们不知道任何与 VS 2008 不兼容的原因。我们没有机会测试。)
- 拥有 DevExpress 的试用版或包含 ASP.NET MVC 扩展的任何软件包的许可版本。我们使用 Developer Express V2010 vol 2 进行过测试。
先解决问题,再讨论细节
如果您和我一样,亲爱的读者,您会欣赏简洁的表达。因此,无需过多铺垫,我将引导您完成需要执行的步骤。稍后将有足够的空间讨论背景信息。
假设您已经有一个带有某些模型和控制器的 ASP.NET MVC 2 解决方案。如您所知,要使用 DevExpress GridView
,您需要将所有核心内容放入一个部分页面中,然后将其渲染到列表操作页面(为了简单起见,我们称之为 Index)。
<%@ Page Title="Cool Master-Detail GridView" Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<MyParentTable>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Cool Master-Detail GridView
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% Html.RenderPartial("IndexPartial"); %>
</asp:Content>
您可能已经注意到,我们将部分视图命名为 IndexPartial
,现在我们准备创建它。您可能已经创建了该视图页面,也可能准备开始编写。也许您想使用脚手架来生成视图。好吧,我听到了;脚手架非常适合普通列表,但对于 DevExpress GridView
却不可用,对吗?请听我说,请前往 CodePlex(参见参考文献 4),下载您尊敬的作者为您发布的 MVC Templates for DevExpress(注意:我们不将模板作为本文的下载内容,而是引用 CodePlex,因为模板不是本文的主要主题,而且 CodePlex 拥有出色的版本控制功能,因此您可以始终获取最新的模板。)
现在您已经有了提供主网格的 IndexPartial.ascx 页面。我假设您的控制器中已经有了 Index
和 IndexPartial
操作。您的代码可能与我的类似
// GET: /Index/
public ActionResult Index()
{
IQueryable main = db.GetMasterData();
return View(main);
}
// to support GridView sorting and other peculiarities
public ActionResult IndexPartial()
{
IQueryable main = db.GetMasterData();
return View(main);
}
注意:我们确实注意到两个操作中的代码完全相同。本文完成后,我们将处理 DRY(Don't Repeat Yourself)原则。
现在,让我们开始准备详细网格,首先是控制器操作。假设主网格和详细网格将使用 MasterId
(主网格的主键)连接。
[ChildActionOnly]
public ActionResult DetailIndexPartial(int MasterId)
{
var details = db.GetDetailData(MasterId);
return View();
}
正如我们敏锐的读者肯定注意到的,我们将详细信息页面存储在其自己的部分视图中。我们可以使用与创建主网格部分视图相同的脚手架模板来创建 DetailIndexPartial
视图。
现在我们准备施展魔法了!
施展魔法 - 将详细信息渲染添加到 IndexPartial.ascx 视图中
1. <%@ Control Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<MyParentTable>>" %>
2.
3. <%
4. Html.DevExpress().GridView(
5. settings =>
6. {
7. settings.Name = "gvMainTable";
8. settings.CallbackRouteValues =
new { Controller = "Home", Action = "IndexPartial" };
9. settings.Width = Unit.Percentage(100);
10. settings.Columns.Add("MasterId").Caption =
"Primary key -- not for user eyes!";
11. settings.Columns.Add("Name");
12. settings.Columns.Add("Country");
13. settings.Columns.Add("Notes");
14. settings.Columns.Add("Active");
15. settings.Columns.Add("Edited");
16. settings.Columns.Add("EditedBy");
17. settings.Settings.ShowPreview = true;
18. settings.SetPreviewRowTemplateContent(c =>
19. Html.RenderAction("DetailIndexPartial",
new { MasterId = DataBinder.Eval(c.DataItem, "MasterId") }));
20. var commandColumn = settings.Columns.Add("", "");
21. commandColumn.SetDataItemTemplateContent(c =>
22. {%>
23. <%= Html.ActionLink("Edit", "IndexEdit", new
{ Id = DataBinder.Eval(c.DataItem, "Id") })%>
24. <%= Html.ActionLink("Delete", "IndexDelete", new
{ Id = DataBinder.Eval(c.DataItem, "Id") },
25. new { onclick = "return confirm
('Do you really want to delete this record?')" })%>
26. <%});
27. commandColumn.Settings.AllowDragDrop = DefaultBoolean.False;
28. commandColumn.Settings.AllowSort = DefaultBoolean.False;
29. commandColumn.Width = 70;
30. })
31. .Bind(Model)
32. .Render();
33.%>
34.
35. <p>
36. <%: Html.ActionLink("Create New", "Create") %>
37. </p>
我们添加了 3 行:17-19,其余部分来自脚手架。我们还为第 1 列(第 10 行)添加了标题,提醒您删除此列(删除第 10 行)可能是一个好主意。
运行项目,欣赏主从明细页面。
我喜欢!我能多要一些吗?
多于一个级别的表嵌套通常不被认为是最佳用户界面设计实践。然而,我们经常被要求这样做,有时这样做甚至是有意义的。那么,我们能否重复我们刚才所做的?是的,我们可以(无双关语)。我们将向 DetailIndexPartial
操作添加一行(注意第 5 行)。
1. [ChildActionOnly]
2. public ActionResult DetailIndexPartial(int MasterId)
3. {
4. var details = db.GetDetailData(MasterId);
5. ViewData["MasterId"] = MasterId;
6. return View();
7. }
热切的读者已经猜到了,我们需要在控制器中创建一个 DetailAnotherIndexPartial
操作,使用第三个网格来脚手架部分视图,并将其渲染插入到 IndexPartial
视图中(与上面示例中的第 17-19 行相同,第 19 行将引用 DetailIndexPartial
并向其传递 DetailId
)。我们需要修改另外两行。我们修改第 7 行
settings.Name = "gvDetailTable" + ViewData["MasterId"];
我们也修改第 36 行
<%: Html.ActionLink("Create New", "CreateDetail",
new { MasterId = ViewData["MasterId"] })%>
再次运行项目,欣赏所有 3 个嵌套级别。
我们做了什么?
GridView
的预览行用于在主表中插入嵌套表。我们在第 17 行启用预览。我们在第 18 行模板化预览的内容,最后在第 19 行使用 RenderAction
HTML 辅助方法插入详细表。
要启用多级嵌套,我们需要将主键添加到第一个详细信息的模型数据中。我们在 DetailAnotherIndexPartial
操作的第 5 行完成了这一点。我们使用此值来确保每个嵌套表都有一个唯一的名称。这在视图的第 7 行完成。
在主从明细表的每个嵌套级别都拥有“创建新项
”功能是很少见的。我们之所以拥有它,是因为 ListPartialWithDexGridView
模板插入了它。删除操作链接(第 35-37 行)是一个简单的修复。或者,为了确保它正常工作,我们在修改第 36 行时向您展示了如何使用带有主表键的 ViewData
。当然,您需要通过 CreateDetail
操作和视图来支持该功能。
我们没做什么?
主从明细网格具有那些精巧的 + 和 - 图标,您可以单击它们来显示或隐藏详细信息。有很多方法可以实现此行为。我们将尝试一次,以防时间允许。如果您在我之前完成了表的详细信息折叠,您介意给我发封电子邮件吗?
参考文献
- ASP.NET MVC 概述 – GridView 在
http://documentation.devexpress.com/#AspNet/CustomDocument8998 - 如何在 MVC Web 应用程序中使用 DevExpress 扩展入门,位于 http://www.devexpress.com/Support/Center/p/K18376.aspx
- MVC 中的主从明细视图 – 问题报告,位于
http://www.devexpress.com/Support/Center/p/Q260747.aspx - 适用于 DevExpress 的 MVC 模板,位于
http://devxmvctempates.codeplex.com/ - ASP.NET MVC GridView - 主从明细网格布局公告
Mehul Hary 的博客
历史
- 2011 年 1 月 30 日:初始帖子
- 2011 年 5 月 31 日:文章更新,请参阅上面的作者注