ASP.NET MVC Framework (Part 3): Passing ViewData from Controllers to Views
page 3 of 5
by Scott Guthrie
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 34827/ 109

A Simple Product Listing Scenario

To help illustrate some of the techniques we can use to pass ViewData from a Controller to a View, let's build a simple product listing page:

Figure 2

We will use a CategoryID integer to filter the products that we want to display for the page.  Notice above how we are embedding the CategoryID as part of the URL (for example: /Products/Category/2 or /Products/Category/4).

Our product listing page is then rendering two separate dynamic content elements. The first is the textual name of the category we are displaying (for example: "Condiments").  The second is an HTML <ul><li/></ul> list of product names.  I've circled both of these in red in the above screen-shot.

Below we'll look at two different approaches we can use to implement a "ProductsController" class that processes the incoming request, retrieves the data necessary to handle it, and then passes this data to a "List" view to render it.  The first approach we'll examine passes the data using a late-bound dictionary object.  The second approach we'll examine passes it using a strongly-typed class.

Approach 1: Passing ViewData using the Controller.ViewData Dictionary

The Controller base class has a "ViewData" dictionary property that can be used to populate data that you want to pass to a View.  You add objects into the ViewData dictionary using a key/value pattern.

Below is a ProductsController class with a "Category" action method that implements our product listing scenario above.  Notice how it is using the category's ID parameter to lookup the textual name of the category, as well as retrieve a list of the Products within that category.  It is storing both of these in the Controller.ViewData collection using a "CategoryName" and "Products" key:

Figure 3

Our Category action above is then calling RenderView("List") to indicate which view template it wants to render.  When you call RenderView like this it will pass the ViewData dictionary to the View in order for it to render.

Implementing Our View

We will implement our List view using a List.aspx file that lives under the \Views\Products directory of our project.  This List.aspx page will inherit the layout of the Site.Master MasterPage under the \Views\Shared folder (right click within VS 2008 and select Add New Item->MVC View Content Page to wire up a master page when you create a new view page):

Figure 4

When we create our List.aspx page using the MVC View Content Page template it derives not from the usual System.Web.UI.Page class, but rather from the System.Web.Mvc.ViewPage base class (which is a subclass of the existing Page class):

Figure 5

The ViewPage base class provides us with a ViewData dictionary property that we can use within the view page to access the data objects that were added by the Controller.  We can then take these data objects and use them to render HTML output using either server controls, or by using <%= %> rendering code.

Implementing Our View Using Server Controls

Below is an example of how we could use the existing <asp:literal> and <asp:repeater> server controls to implement our HTML UI:

Figure 6

We can bind the ViewData to these controls using the below code-behind class (note how we are using the ViewPage's ViewData dictionary to-do this):

Figure 7

Note: Because we have no <form runat="server"> on the page, no view-state is ever emitted.  The above controls also don't automatically render any ID value - which means that you have total control over the HTML emitted.

Implementing our View using <%= %> Code

If you prefer to use inline rendering code to generate the output, you can accomplish the same result as above using the List.aspx below:

Figure 8

Note: Because ViewData is typed as a dictionary containing "objects", we need to cast ViewData["Products"] to a List<Product> or an IEnumerable<Product> in order to use the foreach statement on it.  I am importing both the System.Collections.Generic and MyStore.Models namespaces on the page to avoid having to fully qualify the List<T> and Product types.

Note: The use of the "var" keyword above is an example of using the new C# and VB "type inference" feature in VS 2008 (read here for my previous post on this).  Because we have cast ViewData["Products"] as a List<Product> we get full intellisense on the product variable within the List.aspx file:

Figure 9

Approach 2: Passing ViewData using Strongly Typed Classes

In addition to supporting a late-bound dictionary approach, the ASP.NET MVC Framework also enables you to pass strongly typed ViewData objects from your Controller to your View.  There are a couple of benefits of using this strongly typed approach:

You avoid using strings to lookup objects, and get compile-time checking of both your Controller and View code

You avoid the need to explicitly cast values from the ViewData object dictionary when using strongly-typed languages like C#

You get automatic code intellisense against your ViewData object within both the markup and code-behind of your view page

You can use code refactoring tools to help automate changes across your app and unit-test code base

Below is a strongly typed "ProductsListViewData" class that encapsulates the data needed for the List.aspx view to render our product listing.  It has CategoryName and Products properties (implemented using the new C# automatic properties support):

Figure 10

We can then update our ProductsController implementation to use this object to pass a strongly typed ViewData object to our view:

Figure 11

Notice above how we are passing our strongly typed ProductsListViewData object to the View by adding it as an extra parameter to the RenderView() method.

Using the View's ViewData Dictionary with a Strongly Typed ViewData Object

The previous List.aspx view implementations we wrote will continue to work with our updated ProductsController - no code changes required.  This is because when a strongly typed ViewData object is passed to a View that derives from ViewPage, the ViewData dictionary will automatically use reflection against the properties of the strongly typed object to lookup values.  So code in our view like below:

Figure 12

will automatically use reflection to retrieve the value from the CategoryName property of the strongly typed ProductsListViewData object we passed when calling the RenderView method.

Using the ViewPage<T> Base Class to Strongly Type ViewData

In addition to supporting a dictionary based ViewPage base class, the ASP.NET MVC Framework also ships with a generics based ViewPage<T> implementation.  If your View derives from ViewPage<T> - where T indicates the type of the ViewData class the Controller passes to the view - then the ViewData property will be strongly typed using this class type.

For example, we could update our List.aspx.cs code-behind class to derive not from ViewPage, but from ViewPage<ProductsListViewData>:

Figure 13

When we do this, the ViewData property on the page will change from being a dictionary to being of type ProductsListViewData.  This means that instead of using string-based dictionary lookups to retrieve data, we can now use strongly typed properties:

Figure 14

We can then use either a sever-control approach, or a <%= %> rendering approach to render HTML based on this ViewData.

Implementing Our ViewPage<T> Implementation Using Server Controls

Below is an example of how we could use the <asp:literal> and <asp:repeater> server controls to implement our HTML UI.  This is the exact same markup that we used when our List.aspx page derived from ViewPage:

Figure 15

Below is what the code-behind now looks like.  Note how because we are deriving from ViewPage<ProductsListViewData> we can access the properties directly - and we don't need to cast anything (we'll also get refactoring tool support anytime we decide to rename one of the properties):

Figure 16

Implementing our ViewPage<T> implementation using <%= %> Code

If you prefer to use inline rendering code to generate the output, you can accomplish the same result as above using the List.aspx below:

Figure 17

Using the ViewPage<T> approach we now no longer need to use string lookups of the ViewData.  Even more importantly, notice above how we no longer need to cast any of the properties - since they are strongly typed.  This means we can write foreach (var product in ViewData.Products) and not have to cast Products.  We also get full intellisense on the product variable within the loop:

Figure 18


View Entire Article

User Comments

No comments posted yet.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2014 ASPAlliance.com  |  Page Processed at 10/22/2014 5:41:47 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search