ASP.NET MVC Tip #5 – Create Shared Views
In this tip, I explain how to create shared views for controller actions. A shared view can be used with multiple controllers. In particular, I explain how to create shared views for displaying and editing database data.
Imagine that the HomeController controller exposes an action named Index(). When the Index() action is invoked, the ASP.NET MVC framework first attempts to retrieve the Index view from the following path:
\Views\Home\Index.aspx
If the Index.aspx view is not present in the Home folder, the ASP.NET MVC framework next attempts to retrieve the view from the Shared folder:
\Views\Shared\Index.aspx
If the Index.aspx view can’t be retrieved from either location, then the error message in Figure 1 is displayed.
Figure 1 – Error displayed when attempting to retrieve non-existent view
So, the ASP.NET MVC framework always attempts to retrieve a view from the Shared folder when the view can’t be retrieved from the Views subfolder that corresponds to the controller’s name. This means that you can create default views for action methods that are shared across multiple controllers.
In today’s tip, I show you how to create shared views for performing standard database operations that can be used with the DataController class that we created in yesterday’s Tip #4. If you haven’t read yesterday’s tip, read it now at:
I modified the DataController for this tip so that its controller actions return the LINQ to SQL DataContext to the view. For example, the modified DataController.Index() method looks like this:
Public Function Index() As ActionResult
ViewData("DataContext") = Me.DataContext
ViewData("DataTable") = Me.Table
Return View()
End Function
We need both the LINQ to SQL DataContext and LINQ to SQL Table to render the grid of records.
The Index.aspx view, placed in the Views\Shared folder, is contained in Listing 1.
Listing 1 – Views\Shared\Index.aspx
<%@ Page Title="Index" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="false" CodeBehind="Index.aspx.vb" Inherits="Tip5.SharedViews.Index" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<%=Me.RenderDataTable()%>
<br />
<%= Html.ActionLink("Add Record", "Create") %>
</asp:Content>
The Index.aspx view in Listing 1 simply calls the RenderDataTable() method declared in the view’s code-behind file. The Index.aspx.vb code, the code-behind file for Index.aspx, is contained in Listing 2.
Listing 2 -- \Views\Shared\Index.aspx.vb
Namespace SharedViews
Partial Public Class Index
Inherits DataViewBase
Protected Function RenderDataTable() As String
Dim sb As New StringBuilder()
sb.AppendLine("<table>")
' Create Header Row
Dim columnNames As String() = Me.GetColumnNames()
sb.AppendLine("<tr>")
sb.Append("<th></th>")
For Each columnName As String In columnNames
sb.AppendFormat("<th>{0}</th>", columnName)
Next
sb.AppendLine("</tr>")
' Create Data Rows
Dim row As Object
For Each row In Me.DataTable
Dim identityValue As Integer = Me.GetIdentityValue(row)
sb.AppendLine("<tr>")
sb.Append("<td><small>")
sb.Append(Me.Html.ActionLink("[View]", "Details", New With {.Id = identityValue}))
sb.Append(" ")
sb.Append(Me.Html.ActionLink("[Edit]", "Edit", New With {.Id = identityValue}))
sb.Append(" ")
sb.Append(Me.Html.ActionLink("[Delete]", "Delete", New With {.Id = identityValue}))
sb.Append("</small></td>")
For Each columnName As String In columnNames
Dim value As String = row.GetType() _
.GetProperty(columnName) _
.GetValue(row, Nothing) _
.ToString()
sb.AppendFormat("<td>{0}</td>", HttpUtility.HtmlEncode(value))
Next
sb.AppendLine("</tr>")
Next
sb.AppendLine("</table>")
Return sb.ToString()
End Function
End Class
End Namespace
The RenderDataTable() method generates an HTML table that contains all of the columns and all of the rows represented by the DataTable property. The DataTable is passed by the DataController.Index() controller action to the Index.aspx view.
When you request the Index.aspx view, you get the page displayed in Figure 2.
Figure 2 – The Index.aspx view
Notice that View, Edit, and Delete links appear next to each record. You can click these links to perform the corresponding actions. Notice, furthermore, that there is an Add Record link at the bottom of the page. Clicking this link opens the Create.aspx view in Figure 3.
Figure 3 – The Create.aspx View
The form generated by the Create.aspx view is not pretty, and it does not support validation, but otherwise it works. If you enter a new movie, you can submit the new movie to the database.
The Create form is generated with the code in Listing 3.
Listing 3 – Views\Shared\Create.aspx.vb
Namespace SharedViews
Partial Public Class Create
Inherits DataViewBase
Protected Function RenderCreateForm() As String
Dim sb As New StringBuilder()
Dim columnNames = Me.GetColumnNames()
Dim identityColumnName = Me.GetIdentityColumnName()
sb.AppendFormat("<form method='post' action='{0}'>", Me.Url.Action("New"))
sb.AppendLine("<table>")
Dim columnName As String
For Each columnName In columnNames
If columnName <> identityColumnName Then
sb.AppendLine("<tr>")
sb.AppendFormat("<th><label for='{0}'>{0}:</label></td>", columnName)
sb.AppendFormat("<td><input name='{0}' type='text' /></td>", columnName)
sb.AppendLine("</tr>")
End If
Next
sb.AppendLine("</table>")
sb.AppendLine("<input type='submit' value='Add Record' />")
sb.AppendLine("</form>")
Return sb.ToString()
End Function
End Class
End Namespace
The code in Listing 3 uses the LINQ to SQL DataContext and LINQ to SQL Table to generate the form automatically. Both of these objects are passed by the DataController.Create() controller action.
The purpose of this tip was to give you a sense of what you can do with shared views. In this tip, we examined how you could use shared views to automatically generate display, edit, and insert forms for working with database data. You could follow a similar strategy to create standard views for other types of controller actions (for example, standard shopping cart views).
If you would like to experiment with the code discussed in this tip, click the following link (The download includes both VB.NET and C# versions of the code).