Published: 23 Sep 2009
By: Ben Scheirman

In this article, we will take a look at two upcoming features in ASP.NET MVC 2. This article covers ASP.NET MVC 2 Preview 1.

Contents [hide]

ASP.NET MVC 2

ASP.NET MVC 1.0 has only been out for a few months now and with the rapid release cycle that we've been used to you shouldn't be surprised to know that ASP.NET MVC 2 is already starting to take shape.  Oh no, I can hear it already...  

What?  Are you kidding me?  I just learned ASP.NET MVC 1.0?  How can I keep up if they're releasing so fast? 

Put the torch & pitchfork down and have a seat.  Change is a good thing!  You might choose to ignore ASP.NET MVC 2 until it has a final release, however you'll be missing out on one of the very best ways of providing feedback to shape the product.  In this article we will build a simple application demonstrating some of the features available in MVC 2 Preview 1.

Getting the Bits

First things first, if you want to follow along, go download the code.  You can get it from http://www.microsoft.com/downloads/details.aspx?FamilyID=d34f9eaa-fcbe-4e20-b2fd-a9a03de7d6dd&displaylang=en.  When you run the installer you'll be given an extra project type for the MVC 2 Framework.  And before you ask, yes this is safe to install alongside ASP.NET MVC 1.0.  

When you create a new project you should see this:

Figure 1: Creating a new ASP.NET MVC 2 application

Creating a new ASP.NET MVC 2 application

Our Sample Application

For the purposes of demonstration, let's use a sample application. We will build some screens for a Contact Manager. The simple model looks like Figure 2.

Figure 2: A simple model for a contact manager application

A simple model for a contact manager application

We will use a new feature of ASP.NET MVC to build some display & edit screens. This feature is called Templated Helpers.

Templated Helpers

A new feature that was introduced in ASP.NET MVC 2.0 Preview 1 is called Templated Helpers.  This allows you to create display and edit templates for various types.  The benefit of these helpers over your standard ones are that these have compile time checking, intellisense, and strong support for refactoring.

A single picture explains this well:

Figure 3: The template helpers give you IntelliSense

The template helpers give you IntelliSense

Each of the properties of our model object are available to us here.

Let's say we want to output the contact's name:

That's not very impressive, you say?  Of course, strings are pretty straightforward, and you could just output them directly if you wanted.  What about the DateTime

By default, this would render:

Figure 4: Templated Display Helpers help us render display forms in a strongly-typed fashion

Templated Display Helpers help us render display forms in a strongly-typed fashion

That birth date display isn't very nice looking.  For example, the full time probably isn't needed.  Let's give date times a new template to render from.

To customize the display templates, you need to create a new folder within your controller's view folder (or in Views/Shared) called DisplayTemplates.  Inside of this folder, create a new View User Control (.ascx) file called DateTime.ascx.  Notice how the name of the Type of property and the name of the template are the same.  This is yet another convention that you can follow to simplify your code.

Figure 5: Custom Templates are placed in a DisplayTemplates folder with the filename matching the type

Custom Templates are placed in a DisplayTemplates folder with the filename matching the type

Make your DateTime.ascx file look like this: 

Note

Normally you'd use ViewUserControl<T> to avoid the ugly casting, however T must be a reference type, and DateTime is a value type.

Another option would be to create a custom view model class, and assign a DateTime property to it.

Our DisplayFor(m => m.BirthDate) call will use the new template when it renders:

Figure 6: Customizing the DateTime template

Customizing the DateTime template

Alternatively, you could provide an editable version of this: 

Also throw in a touch of CSS in Site.master to make it look good: 

Now we have a decent looking form, and no strings were used! Figure 7 shows the result.

Figure 7: Using Templated Input Helpers to render input controls

Using Templated Input Helpers to render input controls

Notice how we created the labels and textboxes using the lambda expression on the model.  This is much less error-prone and also supports refactoring.  The real power of these helpers is that they work on complex types too! 

Recall that our Contact class is composed of a name and two other complex types:  Address and PhoneNumber (used two separate times).

Now we can write <%= Html.EditorFor(m => m) %> (effectively asking for an editor for our entire model) to produce this:

Figure 8: Rendering an entire model with Html.EditorFor(…)

Rendering an entire model with Html.EditorFor(…)

Now the layout might need some work, but that's a pretty quick way to get an edit form for your model, wouldn't you say?  Notice how both the simple properties are converted into labels & text fields, and the complex ones do the same, however they are noted with labels as well (see Address, HomePhone, and CellPhone).

To customize these we can provide custom partials in the EditorTemplates subfolder (either under /views/contacts or /views/shared).  Let's try this out on our PhoneNumber class.

First, we create the EditorTemplates folder in the /Views/Shared directory.  Then we create a new partial view called PhoneNumber.ascx.

Figure 9: Adding a Custom Editor Template for our PhoneNumber class

Adding a Custom Editor Template for our PhoneNumber class

The file ends up looking like Listing 1.

Listing 1: PhoneNumber.ascx

This template makes our phone number displays much more friendly, as you can see in Figure 10.

Figure 10: Our PhoneNumber template in action

Our PhoneNumber template in action

It's easy to see how much time this can save you, especially if you have many edit screens.  You could easily add validation to this or whatever you require.

Pop Quiz

If you wanted to customize the default template for individual fields, how could you do it?   

Give up?  

You can create a partial called Object.ascx and place it one of the template folders to customize basic layout. 

A known limitation in Preview 1 is that if you try to render a model that contains an interface it will throw an exception.  In Preview 2 this has been fixed and will use the standard object template to output the properties. 

The astute reader will notice that we gave those phone number textboxes an id value (namely AreaCode, Prefix, and Suffix).  Having two of these templates on the same page will cause id collisions, right?  If you view the generated HTML source you'll notice that the ids were actually prefixed by the name of the referencing object's property name.  Thus we have HomePhone_AreaCode, HomePhone_Prefix, and so on.

Figure 11: The generated ids are prefixed with the parent object's property name

The generated ids are prefixed with the parent object's property name

You can use this to your advantage.  If you wanted one of your controls have the same name as its parent property name, then you can leave the control id blank, and it will fill in the name for you. 

Multiple templates for each type 

What if you wanted a special template for some of your fields, but you didn't want to modify all of them render using the template named after their type?  

You have 2 options here.  You can give the property an attribute of [UIHint("SomeCustomTemplate")] or you can call it out explicitly when you render the template, as in Html.DisplayFor(m=>m.SomeProperty, "SomeTemplate").  The latter only works if you're calling Html.EditorFor() directly on that property.  Otherwise you'll have to use the attribute based version.  We're not directly rendering the templates for the model properties, so we'll have to use the attribute.

Say we added a new property on our Contact model object:

We want to display this value on the edit form, but we don't want it to ignore the time like we had for the BirthDate.  In fact, we'd like to output a template that displays the time relative to the current time.  This gives you a friendly notice that the record was added 5 minutes ago, 22 minutes ago, 5 hours ago, etc.

In order to get this property to render a template other than DateTime.ascx, we must add the [UIHint] attribute to our property. 

The template is added to both EditorTemplates and DisplayTemplates, since the property might be viewable in both display and edit views.  We can write a simple helper to do the relative date logic for us, leaving our template as simple as Listing 2.

Listing 2: RelativeDateTime.ascx

Again we can't use ViewUserControl<T> because DateTime is a value type.  The helper we used is defined in Listing 3.

Listing 3: DateHelper.cs

Rendering the form now shows us the new template in use:

Figure 12: Our RelativeDateTime template now renders for this field

Our RelativeDateTime template now renders for this field

Customizing the Labels 

The labels that are generated for us match the property names, but they aren't very human friendly.  We can use the [Display] attribute to control this, for example:

Validation 

You can decorate your view model objects with additional attributes from the System.ComponentModel.DataAnnotations namespace.  For example, to make a field required:

There are also other attributes you can use, such as:

  • Range
  • RegularExpression
  • StringLength

In Preview 1, this will drive server-side ModelState validation.  When Preview 2 drops, this will drive client-side validation with jQuery automatically off of the attributes on your model.  Cool!

Figure 13: Adding [Required] to your properties drives server-side ModelState validation (in Preview 1)

Adding [Required] to your properties drives server-side ModelState validation (in Preview 1)

Skipping Properties

Our contact class has an Id property, but it's not something you would generally allow people to set.  To tell the helper not to generate a field for a property, you can add another attribute:

Now Id won't be part of our form.  Be careful though, in order to continue to use this in an edit form, you'll need to output the Id manually as a hidden field so that it will be picked up in the form post.

A note about putting view attributes on your model

For simple applications, what you have seen here will probably work just fine.  However in larger, more complex applications you'll want to separate the view concerns from your model.  This is generally referred to as View Model, or Presentation Model, where you have an object specifically geared for displaying on a form or collecting data from the user.  Your domain objects can be translated into these view model objects (and vice versa).  Doing so will keep you from having to sprinkle view related attributes all over your domain model.

These templates won't be able to satisfy your every need when it comes to rendering edit forms, but it does get you on the ground and running quickly.  When the default behavior doesn't work, you can always customize the templates for a given type.

Areas

One feature that was often requested in ASP.NET MVC 1.0 was support for Areas.  Having multiple areas allows you to completely segregate your site into multiple sections, each having their own controllers, views, routes, and models.  There were a handful of solutions for adding this behavior to 1.0, but each had their quirks.

As of Preview 1 each area is a separate project. In Preview 2, you'll be able to separate areas by using folders within the same project.

Multi-Project Areas 

To implement multiple areas, you'll first need a root project to be the entry point.  We'll also create two additional projects to serve as the sub-areas of our application.  I placed these projects in a solution folder called Areas.

In the sub area projects, you'll need to remove all of the controllers, all of the views (except the Views/web.config file, that is important).  You can also safely remove the Content and Scripts folders.  Figure 14 shows what it should look like.

Figure 14: Sub-area projects are normal ASP.NET MVC 2 projects, however some of their folders need to be removed

Sub-area projects are normal ASP.NET MVC 2 projects, however some of their folders need to be removed

Notice that I've created a class called Routes in each of the sub-area projects.  This is simply a static class that we can use to register routes for that area.  We'll look at those in a minute. First, we have to take a manual step to get this all working together.

To get this multi-project setup to work in Preview 1, we'll need to do some manual surgery on the project files.  Right click on the Billing sub-area project and select Unload Project (Figure 15).  Then right click on the same node and select Edit Billing.csproj (Figure 16).  Inside of the project file you'll see a section that is commented out that looks like Listing 4.

Figure 15: Unloading the project so that we can edit the file manually

Unloading the project so that we can edit the file manually

Figure 16: Select this to edit the raw XML of the project file

Select this to edit the raw XML of the project file

Listing 4: Modifying the project files to enable areas support

Notice that I've uncommented the bolded lines, just as the comments indicate (as this is an area child project).  We'll do the same thing for the Shipping project.  For the root project, (InternalOps) we will follow the same process, but we'll uncomment the other section instead, indicating that it is the root or "area parent project". 

Go ahead and Reload the projects (right-click, Reload Project).  You'll get a warning that the file might be "dangerous" but don't worry, just click OK. This dialog will hopefully go away in future preview releases.

Wait a Minute!

Do we really have to go through all of this junk any time we want a solution with multiple area projects?  For Preview 1, yes.  Future releases will provide much easier Visual Studio tooling support that should make this process painless.

Now, back to the projects.  Did you notice that each area child project had a Route.cs file?  This needs to be created manually.  It's just a static class that we can use to register our routes for each area.  This helps keep the area registration contained to the project for which it is intended.  You could choose to ignore this step if you want to provide all of your routes in the root project, however this is much cleaner.

Here is our billing Routes.cs is shown in Listing 5.

Listing 5: Billing/Routes.cs

There is a new method called MapAreaRoute that we use to register routes with an area.  The method signature looks like this:

We have to tell the route what area this is for, and provide it a unique name (note that names have to be unique across the board, hence why I prefixed this default route name with the area name).  We also have to indicate in which namespace our controllers reside.

The Shipping Routes.cs is similar, but here we have a custom route (Listing 6).

Listing 6: Shipping/Routes.cs

Most of the area routes will start with a static identifier to select the area.  It doesn't have to be the area name, but in most cases it will be.  You will still have to look at your routes holistically, in order to determine which route matches first for a given request.  Remember that order matters!

In our root project's Global.asax (Listing 7) we'll call on both of these classes to register their routes.

Listing 7: Global.asax

We're still manually calling our area routes here, but we have control in what order they get registered.  We also have to give the root area a (non-empty) area name, so I called it main here.  The reason is because we'll be generating links between the areas, and we will have to specify what area the link is intended for.

Now let's add some controllers & views, shall we?  I'd like to have the main page link to some recent orders (which will come from the Billing area).  Each order will have a tracking number that will link to the Shipping area to track.

Here's the link on our master page to the Recent Orders action:

Note 

You have to specify what area you're pointing to in a master page like this.  The reason is because you might be sitting in a different area when the link is generated.  If you omitted the Area="main" on the "Home" link, then it would try to access HomeController from the current area, which might be Billing or Shipping.  To prevent this, we must specify the area explicitly.

Figure 17: The link generates the appropriate URL for the Billing area

The link generates the appropriate URL for the Billing area

See how the URL is correctly generated (using our route definition from Billing/Routes.cs) to the Billing area.  Let's write a quick controller/action to show us this data:

Listing 8: Billing/Controllers/OrderController.cs

Here we're just faking the orders, but they could come from anywhere.  Here is our view:

Listing 9: Billing/Views/Order/Index.aspx

Notice the link we generate for the tracking number.  We specify the area name as "Shipping".  This will use our custom route that we defined in the Shipping project. Our final page looks like Figure 18.

Figure 18: The orders index view links to the shipping area.

The orders index view links to the shipping area.

Our Track Package link is successfully using the custom route we created in Shipping/Routes.cs. Next, let's create a quick & dirty controller/action & view for this link.

Listing 10: Shipping/Controllers/ParcelController.cs

Again, we're faking the data here. Our view....

Listing 11: Shipping/Views/Parcel/Track.aspx

And the final result:

Figure 19: The tracking action is invoked from the Tracking area

The tracking action is invoked from the Tracking area

And that wraps up how to implement areas using multiple projects in MVC 2.  There are some rough edges, but having built-in support for areas is great, and enables larger applications to segregate out various pieces of semi-related functionality into their own place.

Many folks questioned the need for multiple projects, as it does add quite a bit of complexity to the solution.  For example, any view changes for sub-areas are compiled and included into the root project's template.  As a result, if you change a sub-area view, you have to recompile in order to see it take effect on your site.  Luckily, Preview 2 will give us support for single-project areas.

What's next?

In this article, you learned about two big features of the upcoming ASP.NET MVC 2 framework. You learned about Templated Helpers to aid in building quick scaffolding for your view model objects. You also learned how to leverage the new Areas support with multiple projects to build larger applications with more separation of responsibility. This article covered Preview 1, however Preview 2 is due to be out very soon and will come with it many enhancements and improvements on what you see here. The best way to keep up and help shape the product is to participate in these early previews releases.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Ben Scheirman

Ben Scheirman is a software developer specializing in .NET. He has worked extensively on the web on various platforms and languages. Ben is a Microsoft MVP, Microsoft ASP Insider, and Certified ScrumMaster. When not programming, Ben enjoys speaking, blogging, spending time with his wife and five won...

This author has published 4 articles on DotNetSlackers. View other articles or the complete profile here.

Other articles in this category


Code First Approach using Entity Framework 4.1, Inversion of Control, Unity Framework, Repository and Unit of Work Patterns, and MVC3 Razor View
A detailed introduction about the code first approach using Entity Framework 4.1, Inversion of Contr...
jQuery Mobile ListView
In this article, we're going to look at what JQuery Mobile uses to represent lists, and how capable ...
Exception Handling and .Net (A practical approach)
Error Handling has always been crucial for an application in a number of ways. It may affect the exe...
JQuery Mobile Widgets Overview
An overview of widgets in jQuery Mobile.
Book Review: SignalR: Real-time Application Development
A book review of SignalR by Simone.

You might also be interested in the following related blog posts


Understanding the MvcRouteHandler and MvcHandler in ASP.NET MVC read more
Code First with Entity Framework 5 using MVC4 and MVC Scaffold read more
Using the Telerik Extensions in ASP.NET MVC4 Today - A First Look read more
MSDN Guidance on ASP.NET MVC vs WebForms and its Impact on my EF + ASP.NET Work read more
Html Encoding Nuggets With ASP.NET MVC 2 read more
VBA code I wrote this week in Excel and how it helped me read more
Get Ready for Teleriks Custom-built Extensions for ASP.NET MVC read more
Screencast Whats new in the Entity Data Model Designer in VS2010 read more
10 resources to learn Moq read more
RadControls for WPF/Silverlight Q3 Beta 2 release is live! read more
Top
 
 
 

Discussion


Subject Author Date
placeholder Wonderful article! Andrew Siemer 9/29/2009 4:25 PM
great article. Souheil Charada 7/30/2010 8:53 AM
placeholder RE: great article. Sonu Kapoor 7/30/2010 9:23 AM
Model for sample Kyle Russell 8/25/2010 8:54 AM

Please login to rate or to leave a comment.