Introduction
The Master-Detail grid is a deep-rooted celebrity of user interfaces. Unfortunately, if you do web applications in ASP.NET MVC 2 using the current version V2010 vol 2 of Developer Express MVC Extensions for ASP.NET MVC (see the references 1 and 2), you are out of luck – at least you were, before you found this little article.
We are sure one day there will be a great solution to this need available from Developer Express. The vendor is aware of the need, they promised the feature in the future: “Unfortunately, the MVCxGridView
doesn't support such a feature. I decided to convert this question to a suggestion article and forward it to our developers, so they can examine it. We'll let you know the results” said Vest on 5/28/2010 (see reference 3.)
For the time being, you will have to read the rest of this article and do a little bit more work in your code to have Master-Detail in your DevExpress GridView on MVC web projects.
Author's note (5/31/2011): It just happened. Developer Express announced that part of the new version of Developer Express MVC Extensions for ASP.NET MVC v2011 vol 1 (to be released soon) will be ASP.NET MVC GridView - Master-detail Grid Layout (see reference 5.) That release will certainly save readers from the need of tricks described in this article.
Background
We assume you are familiar with ASP.NET MVC 2 development and also that you have:
- Microsoft Visual Studio 2010 (we are not aware of any reason for non-compatibility with the VS 2008. We had no opportunity to test.)
- DevExpress trial or licensed version of any package that does include MVC Extensions for ASP.NET MVC. We tested with Developer Express V2010 vol 2.
Solution First, Discussion Later
If you beloved reader are like me, you would appreciate the brevity of the expression. Hence, without much foreplay, I will guide you through the steps you shall do. There will be enough space to discuss the background information later.
Let’s assume you already have an ASP.NET MVC 2 solution with some model and controller. As you stay well advised, to use the DevExpress GridView
, you put all the meat into partial page that you render into List action page (let’s call Index to keep things plain):
<%@ 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>
As you may have noticed, we named the partial view IndexPartial
and we are ready to make it. You might have the view page already created or you might be ready to type it. Perhaps you would like to scaffold the view. Well, I hear you; scaffolding is great for plain lists, not available for DevExpress GridView
, right? Hear me, go to CodePlex (see reference 4) and download MVC Templates for DevExpress that your well appreciated author posted there for your benefit (Note: Instead of adding the template as a download to this article, we refer you to CodePlex because the template is not the main topic of this article and also CodePlex has great version control features, so you always get your templates fresh.)
Now you have the IndexPartial.ascx page that provides your master grid. Let me assume that you already have the Index
and IndexPartial
actions in your controller. Your code might look similar to mine:
public ActionResult Index()
{
IQueryable main = db.GetMasterData();
return View(main);
}
public ActionResult IndexPartial()
{
IQueryable main = db.GetMasterData();
return View(main);
}
Note: We did notice the code in both actions is exactly the same. We will care about DRY after we are done with this article.
Let’s prepare our detail grid now, starting with the controller action. Let’s assume the master and detail grids will be joined with use of the MasterId
– the primary key of the master grid:
[ChildActionOnly]
public ActionResult DetailIndexPartial(int MasterId)
{
var details = db.GetDetailData(MasterId);
return View();
}
As our sharp reader certainly noticed, we store the detail page in its own partial view. We can use the same scaffolding template we used for the master grid partial view to create the DetailIndexPartial
view too.
We are ready for the magic now!
Do the Magic - Add the Detail Rendering into the IndexPartial.ascx View
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>
We added 3 rows: 17 – 19, all the rest came from the scaffolding. We also added the caption to the 1st column (row 10.) to remind you that removing this column (deleting the row 10) might be a good idea.
Run the project, enjoy the look in your Master-Detail page.
I Like It! May I Have More?
More than one level of table nesting is not usually considered the best user interface design practice. However, frequently we are asked to do just that and sometimes it even makes sense. So, can we repeat what we just did? Yes, we can (no pun intended.) We will add one row to the DetailIndexPartial
action (notice the row 5):
1. [ChildActionOnly]
2. public ActionResult DetailIndexPartial(int MasterId)
3. {
4. var details = db.GetDetailData(MasterId);
5. ViewData["MasterId"] = MasterId;
6. return View();
7. }
The avid reader guessed already that we need to create DetailAnotherIndexPartial
action in our controller, scaffold the partial view with the third grid and insert its rendering into the IndexPartial
view (same as rows 17-19 in the example above, the row 19 will refer to the DetailIndexPartial
sending it the DetailId
). We need to modify two more rows. We change row 7:
settings.Name = "gvDetailTable" + ViewData["MasterId"];
We change row 36 as well:
<%: Html.ActionLink("Create New", "CreateDetail",
new { MasterId = ViewData["MasterId"] })%>
Run the project again, enjoy the look at all 3 levels or nesting.
What Did We Do?
GridView
preview row is used to insert the nested table into the master. We enable the preview in row 17. We template the contents of the preview in row 18 and finally we insert the detail table using the RenderAction
HTML helper method in row 19.
To enable multilevel nesting, we need to add the primary key of the master into the model data of the first detail. We did that at row 5 of the DetailAnotherIndexPartial
action. We are using this value to ensure that each nested table has a unique name. This is done in row 7 of the view.
It is rare to have the “Create New
” feature in all nesting levels or the master-Detail table. We have it because the ListPartialWithDexGridView
template inserted it. Deleting the action link (rows 35-37) is an easy fix. Alternately, to make sure it works, we showed you how to use the ViewData
with master table key in the modification of row 36. Sure, you have to support the feature with the CreateDetail
action and view.
What We Did Not Do?
Master-Detail grids have those nifty little + and – icons that you can click to show or hide the detail. There are many ways to achieve this behavior. We will do it once as the time permits. Would the kind reader mind sending me an email if you beat me in doing the collapsing of the table detail?
References
- ASP.NET MVC Overview – GridView at
http://documentation.devexpress.com/#AspNet/CustomDocument8998
- How to Start Using DevExpress Extensions in an MVC Web Application at http://www.devexpress.com/Support/Center/p/K18376.aspx
- Master-detail view in MVCxGridView – issue report at
http://www.devexpress.com/Support/Center/p/Q260747.aspx
- MVC Templates for DevExpress at
http://devxmvctempates.codeplex.com/
- ASP.NET MVC GridView - Master-detail Grid Layout announcement
Mehul Hary's blog
History
- 30th January, 2011: Initial post
- 31st May, 2011: Article updated, see author's note above