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:

ViewsHomeIndex.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:

ViewsSharedIndex.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

image

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:

/blog/archive/2008/06/18/asp-net-mvc-tip-4-create-a-custom-data-controller-base-class.aspx

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 ViewsShared folder, is contained in Listing 1.

Listing 1 – ViewsSharedIndex.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 — ViewsSharedIndex.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("&nbsp;")
                sb.Append(Me.Html.ActionLink("[Edit]", "Edit", New With {.Id = identityValue}))
                sb.Append("&nbsp;")
                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

image

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

image

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 – ViewsSharedCreate.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).

Download the Code

Discussion

  1. Dan Miser says:

    Great couple of articles there. I see a lot of overlap from the whole Dynamic Data area, but given that this is geared towards usability with the MVC bits today, it still adds some good value.

    Perhaps it would be good to open source these classes/pages so we could get the community to extend them. For example, dealing with a non-trivial object model, it would be nice to get an automatic drop-down list for foreign-key fields, not display the FK ids, and provide links to child collections. Plus, there are those that would inevitably pitch in to evolve the GUI.

    Either way, thanks for the blogs. They are very content rich!

  2. Hi..
    thanks for the tips..
    very useful for me..

  3. i have to go back to school to understand this….i hope i still have a time for that. TQ

  4. In our blog are many good tips. Thanks!

  5. MOV to DVD says:

    I get the error:
    System.NotSupportedException: Method ‘Boolean Like(System.String, System.String)’ cannot be used on the client; it is only for translation to SQL.

  6. i prefer to use what is expert said….so thanks a lot stephen.

  7. dating tips says:

    The views are specially beneficial when dealing with the databases. MySQL was so hard to use before they introduced views in version 5.

  8. g5 It looks like DataContextExtensions.cs line 45 of the Save method should pass the primaryKeyName through to Update.

  9. Thank you for sharing this articles1

  10. klip izle says:

    The views are specially beneficial when dealing with the databases. MySQL was so hard to use before they introduced views in version 4

  11. HD Video Converter says:

    As the users of HD Camcorders like Sony, Canon, Panasonic, this HD Video Converter is necessary to help us convert hd Video easily and quickly. The Converter for HD provides several practical editing functions to help you achieve ideal output effect. Trim function is to cut videos into clips which you can just convert and transfer to your player. Crop function helps you remove black bars around the movie. You could use Effect function to adjust video brightness, contrast, saturation and more parameters. More powerful and considerate functions are waiting for you to explore.
    M2TS Converter and MTS Converter l MOD Converter ..