views:

140

answers:

1

I have only tried this in single project areas. So if anyone tries this in a multi-project areas solution please let us know.

Area support was added to MVC2. However the views for your controllers have to be in your main Views folder. The solution I present here will allow you to keep your area specific views in each area. If your project is structured like below, with Blog being an area.

+ Areas          <-- folder
  + Blog         <-- folder
    + Views      <-- folder
      + Shared   <-- folder
      Index.aspx
      Create.aspx
      Edit.aspx
+ Content
+ Controllers
...
ViewEngine.cs

Add this code to the Application_Start method in Global.asax.cs. It will clear your current view engines and use our new ViewEngine instead.

// Area Aware View Engine
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new AreaViewEngine());

Then create a file named ViewEngine.cs and add the code below.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web.Mvc;

namespace MyNamespace
{

    public class AreaViewEngine : WebFormViewEngine
    {
        public AreaViewEngine()
        {
            // {0} = View name
            // {1} = Controller name

            // Master Page locations
            MasterLocationFormats = new[] { "~/Views/{1}/{0}.master"
                                          , "~/Views/Shared/{0}.master"
                                          };

            // View locations
            ViewLocationFormats = new[] { "~/Views/{1}/{0}.aspx"
                                        , "~/Views/{1}/{0}.ascx"
                                        , "~/Views/Shared/{0}.aspx"
                                        , "~/Views/Shared/{0}.ascx"
                                        , "~/Areas/{1}/Views/{0}.aspx"
                                        , "~/Areas/{1}/Views/{0}.ascx"
                                        , "~/Areas/{1}/Views/Shared/{0}.aspx"
                                        , "~/Areas/{1}/Views/Shared/{0}.ascx"
                                        };

            // Partial view locations
            PartialViewLocationFormats = ViewLocationFormats;

        }

        protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
        {
            return new WebFormView(partialPath, null);
        }

        protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
        {
            return new WebFormView(viewPath, masterPath);
        }

    }   // End Class AreaViewEngine

}       // End Namespace    

This will find and use the views you have created in your areas.

This is one possible solution that allows me to keep views in the specified area. Does anyone else have a different, better, enhanced solution?

Thanks

+2  A: 

I'm sorry to be the one to tell you this, but you must be missing something. I currently have your scenario working out of the box with ASP.NET MVC 2 RC.

I assume you have all the register routes and have the correct web.config files inside your area's view folder?

Maybe have a look at this walk through, especially the part about creating the areas.

HTHs,
Charles

EDIT: Ok, so you're not happy about putting in the extra new { area = "blog' }, null - fair enough, I'll admit its niggly... but what else are you going to do?

What happens when you have two controllers with the same name? One in your root project and one in an area or two controllers with the same name in two different areas? How is it going to find the correct view?

Also, I do see a problem with your ViewLocationFormats. All of the area view locations have no reference to their area... e.g. ~/Areas/{1}/Views/{0}.ascx - how does it know what area?

If you are suggesting that all the different area's views and all thrown into the Areas folder under their controller name and then found under Views and Views/Shared - I would highly recommend against that... It'll become a mess very quickly.

So where does that leave you? It really leaves you needing to specify the area when creating the route. It really boils down to the fact that although it's niggly having to specify the area, there really is no other way.

Charlino
Charles you are correct. This solution will work. However, if you implement this solution referenced in your link, you will have to add area to every ActionLink.The solution I have shown does not required adding area to any of your links. So it becomes a matter of preference. Personally, I didn't want to add ',new { area = ""}, null' to those links to views not in an area.Thanks for your answer. I did enjoy going through the exercise.
37Stars