In the small poll I made, one of the users suggested implementing a Dashboard/WebPart framework in MVC as a good subject for a quick tip.

Obviously, implementing this kind of framework is hard work so I chose to use the KISS (Keep It Simple Stupid) principle on it. What I implemented here is a very naive approach to a web part framework but one that I feel accomplishes the desired goal: to be able to have a customizable view where you can change the positioning of specific UI elements.

I will build on this framework a bit further, but for now let’s just set some simple goals:

·        We want a view with 3 fixed “web part zones”

·        We want to let the user specify in which zone a web part should be rendered.

In a real implementation, what we would have was a database of personalization information, where we store each user’s preferences about web part placement. In this case, to keep it short, I chose to just implement a static class that holds the “personalization” information (so no per-user settings). Again, it’s a naive approach but I chose to keep it very simple because how you actually store that information is not really the scope of this article.

So, my model looks like this:

public enum Placement {LEFT, MIDDLE, RIGHT};

 

public class WebPartInfo

{

    public string WebPartName { get; set; }

    public string Template { get; set; }

    public Placement WebPartPlacement { get; set; }

}

 

public static class PersonalizationInfo

{

    public static List<WebPartInfo> webParts;

 

    public static void InitializeListOfWebParts()

    {

        webParts = new List<WebPartInfo>();

       

      //add some webparts here

 

    }

 

    public static List<WebPartInfo> GetListOfWebParts()

    {

        if (webParts == null || webParts.Count == 0)

            InitializeListOfWebParts();

 

       return webParts;

    }

 

    public static void ChangeWebPartPlacement(string webPartName, Placement zone)

    {

        foreach (var item in webParts)

        {

            if (item.WebPartName == webPartName)

            {

                item.WebPartPlacement = zone;

                break;

            }

        }

    }

 

}


Let’s review this code:

·        We’re allowing the existence of 3 web part zones: left, middle and right

·        For each web part we store the WebPartName, a template name and its placement

·        We have a couple of methods, one that gets all the information about the webparts and another one which allows us to change the placement of a specific webpart

This is all very simple, and it’s very easy to turn this into a database model that stores per-user settings on web part placement. But for now, it will do just fine to test our concept.

Obviously, the tricky part is how to use this model to change the layout of our application. Before we run into the actual implementation, let’s do a schema of what we want to do:

mvc web part framework concept

So, we want to have three fixed zones in our view and create a mechanism for displaying one or more (or none) web parts inside each view.

Let’s start by creating our Index View with the basic skeleton for this:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MyDashboard.Models.WebPartInfo>>" %>

 

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">

        Index

</asp:Content>

 

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <div class="Zone">

        <h2>My Left Zone</h2>

    </div>

    <div class="Zone">

        <h2>My Middle Zone</h2>

    </div>

    <div class="Zone">

        <h2>My Right Zone</h2>

    </div>

</asp:Content>

 

This is a strongly-typed view for a list of WebPartInfo objects, but for now, all it does is render three div elements that will act as our web part zones. After changing the default CSS we get this simple output:

Index View skeleton

Now, all we have to do is add some intelligence to our view, so that it’s ready to display web parts inside each of the div elements.

It looks like a difficult task, but fortunately, ASP.NET MVC provides us the concept of Partial Views, which you can think of as a snippet of HTML that can be rendered as part of a larger View (it’s very similar to User Controls in ASP.NET WebForms). So, each web part will be a Partial View that gets pushed inside one of these div elements.

Let’s think of what we want to accomplish: each web part should have some common interface and a simple container for displaying the UI that we want (the functionality of each specific web part). Because we want to respect the principle of DRY (Don’t Repeat Yourself), we don’t want to have to repeat the common interface in each of our web parts. So, let’s take a look at how I implemented the WebPart Partial View:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyDashboard.Models.WebPartInfo>" %>

<div class="WebPart">

   

    <h2><%= Html.Encode(Model.WebPartName) %></h2>

 

    <div class="WebPartContainer">

        <% Html.RenderPartial(Model.Template); %>

    </div>

 

</div>


This Partial View is ready to receive one WebPartInfo object and simply display the WebPartName property. Then it adds a simple container and uses the Html.RenderPartial to render another Partial View. If you recall, our model stored which template is assigned to each web part. So, we’re basically asking our View to render another Partial View which name matches the information contained in our model.

To test this out, let’s change the InitializeListOfWebParts method in our model and add some dummy data:

public static void InitializeListOfWebParts()

{

    webParts = new List<WebPartInfo>();

 

    WebPartInfo wpi = new WebPartInfo();

    wpi.WebPartName = "Books List";

    wpi.Template = "booksWebPart";

    wpi.WebPartPlacement = Placement.LEFT;

    webParts.Add(wpi);

 

    WebPartInfo wpi2 = new WebPartInfo();

    wpi2.WebPartName = "Authors List";

    wpi2.Template = "AuthorsWebPart";

    wpi2.WebPartPlacement = Placement.MIDDLE;

    webParts.Add(wpi2);

 

    WebPartInfo wpi3 = new WebPartInfo();

    wpi3.WebPartName = "Chapters List";

    wpi3.Template = "ChaptersWebPart";

    wpi3.WebPartPlacement = Placement.RIGHT;

    webParts.Add(wpi3);

}


For example, if we pass the first WebPartInfo object to our WebPart Partial View, we will see the name being displayed (“Books List”) and the ASP.NET MVC framework will try to find a partial view named booksWebPart and render it.

I added three new Partial Views to my Shared folder (one for each web part that I’m storing) with code very similar to this one:

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

<p>THIS IS THE BOOKS WEB PART</p>


So, we’re almost ready: we have a WebPart Partial View that knows how to interact with the WebPartInfo object and display the template (partial view) we want.

All we have to do is now change the Index view so that it calls this WebPart Partial View for each of the webparts we want to show.

So, after making the changes, the Index View looks like this:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

 <div class="Zone">

     <h2>My Left Zone</h2>

     <% foreach (var item in Model)

        {

            if (item.WebPartPlacement == MyDashboard.Models.Placement.LEFT)

            { %>

            <% Html.RenderPartial("WebPart", item); %>

            <%} %>           

       <%} %>

    </div>

    <div class="Zone">

        <h2>My Middle Zone</h2>

     <% foreach (var item in Model)

        {

            if (item.WebPartPlacement == MyDashboard.Models.Placement.MIDDLE)

            { %>

            <% Html.RenderPartial("WebPart",item); %>

            <%} %>           

       <%} %>

    </div>

    <div class="Zone">

        <h2>My Right Zone</h2>

        <% foreach (var item in Model)

        {

            if (item.WebPartPlacement == MyDashboard.Models.Placement.RIGHT)

            { %>

            <% Html.RenderPartial("WebPart",item); %>

            <%} %>           

       <%} %>

    </div>

</asp:Content>


So, inside each div element we look inside the list of web parts and chose the ones we want to show in that zone. For each of those web parts we call the RenderPartial method, using the WebPart Partial View and passing it the current WebPartInfo object.

We also have to change the Index method so we actually send the list of webparts to the view:

public ActionResult Index()

{

    var webPartList = PersonalizationInfo.GetListOfWebParts();

    return View(webPartList);       

}


If we now start the application we will see something like this:

Index view with rendered webparts

Through CSS, I added a few borders so you can easily see how the final layout is being rendered:

·        We have each of our div elements (the zones) with a Blue border

·        Inside we have the WebPart Partial View with a Green border

·        With a Red border we have each of the web part templates that were retrieved from the model.

If we play a bit with the InitializeListOfWebParts method, either by adding webparts or changing the placement of any webpart, we would see the layout changing accordingly.

However, we want to allow the user to change this themselves. Let’s change the WebPart Partial View to allow this:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyDashboard.Models.WebPartInfo>" %>

<div class="WebPart">

<h2><%= Html.Encode(Model.WebPartName) %></h2>

 

<% using (Html.BeginForm("ChangeWebPartPlacement", "Home", new { id = Model.WebPartName }))

   { %>

    Choose Placement:

    <%= Html.DropDownList("PlacementChooser",

                          new SelectList(new string[] { "LEFT", "MIDDLE", "RIGHT" },        

                          Model.WebPartPlacement.ToString()))%>

        <input type="submit" value="Save" />

<%} %>

 

<% Html.RenderPartial(Model.Template); %>

</div>


I’m adding a form to this page using a simple dropdown list and a submit button. Notice the parameters in the Html.BeginForm method: we’re specifically asking for a post to be made for the ChangeWebPartPlacement action method (which doesn’t exist yet), in the Home controller, passing the current WebPartName as the id route parameter (I could create a custom route for this, but I opted to stick with the default).

Let’s add the action method to the controller:

[HttpPost]

public ActionResult ChangeWebPartPlacement(string id)

{

    Placement zone = (Placement) Enum.Parse(typeof(Placement),Request.Form["PlacementChooser"]);

    PersonalizationInfo.ChangeWebPartPlacement(id, zone);

    return RedirectToAction("Index");

}


If we now start the application, this will be the result:

Web parts with placement selector

Notice that I’m changing the placement of the BooksList webpart to be rendered in the Middle zone. If I click the Save button, the page will render again showing the desired layout:

index view with personalized layout

Our proposed objectives have been met so, in order not to drag this quick tip further, I will call it a day for now. In the next Quick Tip, I’ll expand this model so that our web parts are not simply displaying static content. That means we have to find a way to pass information to these partial views and have them render accordingly.

Until then, have fun!