Display hierarchical data in ASP.net MVC framework
I am playing around recently with MVC Framework and as a apart of my experiments I wanted to display hierarchical data. I want to share my finding with you, I hope you will find this interesting and if you are interested you can download source here.
Simple unordered list
My final goal is to display a nested unordered list, as shown below each item can have sub item up to nth level. In past you might have created this kind of list using XSLT or ASP.net Repeater Control.
The Model with self referencing hierarchy
I have very simple Tab class which can have a collection of sub-tab. this class can be used to create multiple level of tab hierarchy.
public int ID { get; set; }
public string Name { get; set; }
public string Alias { get; set; }
public List<Tab> Tabs { get; set; }
}
The Controller
I have a single controller called TabController it has Index action, it does two things 1) Gets list of tabs from by calling service and 2) Passes that list to view.
[ControllerAction]
public void Index() {
ViewData[ "tabs" ] = TabService.GetTabs() ;
RenderView( "Index" ) ;
}
}
View extension for partials
If you have worked with ROR and MonoRail then you must be familiar with partials. Partial allows you to extract a common piece of a template into a separate file and then you can use it form other templates. in my case I have to create partial because I want to use it from another template and recursively from itself. Current implementation of the ViewPage in the MVC Framework does not allow me to call another view. So I will need an extrension method which will allow me to call another view. Credit for view extension code goes to Nikhil Kothari
public static void RenderPartial( this ViewPage page,
string viewName, object viewData)
{
RenderPartialCore(page.Html.ViewContext, viewName, viewData) ;
}
private static void RenderPartialCore(ViewContext viewContext
, string viewName, object viewData)
{
Controller controller = (Controller)viewContext.Controller ;
IViewFactory viewFactory = controller.ViewFactory ;
IView partialView = viewFactory.CreateView(viewContext,
viewName, null , viewData) ;
partialView.RenderView(viewContext) ;
}
}
Views
I have two views in /Views/Tab folder first one is Index.aspx and second is TabPartial.aspx
Index view is the main view, it does not have any rendering logic it justs calls partial view and passes data.
MasterPageFile ="~/Views/Shared/Site.Master"
Inherits ="System.Web.Mvc.ViewPage" % >
< %@ Import Namespace ="System.Collections.Generic" % >
< %@ Import Namespace ="ExperimentsWithMVC.Models" % >
< %@ Import Namespace ="System.Web.Mvc" % >
< asp:Content ID ="Content2"
ContentPlaceHolderID ="MainContentPlaceHolder"
runat ="server">
< h2 > Tabs </ h2 >
< % this.RenderPartial( "TabPartial",
new Dictionary<string, object > ()
{{ "tabs", ViewData["tabs"] } }); %>
</ asp:Content >
TabPartial.aspx is the partial view, it is responsible for rendering the unordered list, it will recursively call itself to render the sub-tabs. Remember that we are not using any masterpage for the partial template it is meant to be used only from another view.
< %@ Import Namespace ="System.Collections.Generic" % >
< %@ Import Namespace ="ExperimentsWithMVC.Models" % >
< ul >
< % foreach (Tab tab in ViewData[ "tabs"] as List<Tab > ) {%>
< li >
< %= tab.Name % >
< % if (tab.Tabs ! = null && tab.Tabs.Count > 0) { %>
< % this.RenderPartial( "TabPartial",
new Dictionary<string, object > ()
{ { "tabs", tab.Tabs } }); %>
< %} % >
</ li >
< % }% >
</ ul >
Parting thoughts
In the current implementation(Techincal Preview) of ASP.NET MVC Framework, ViewPage inherits from System.Web.UI.Page class which make it very expensive for above scenerio because of unncecessary page life cycle events. I am sure that next beta version will have lighter implementation of ViewPage.