views:

507

answers:

3

Important Update
Since the release of MVC 2.0 Preview 1 this feature has been implemented as the part of the actual framework itself in the form of Areas. More details available on Phil Haack's blog here

I have a controller called ListManagerController. This controller contain an ActionResult method called Index(). When I right cick on Index in Visual Studio and select Add View the new view is created in /Views/ListManager/Index.

However I want the Index view and all subsequent views to be created in /Views/Manage/ListManager/. How would I accomplish this?

Edit: It was pointed out that this question is a duplicate of the question posted here. It seems my searching skills failed me initially.

+2  A: 

The location of views is tied to the ViewFactory you are using. AFAIK the web forms view engines does not support areas [Manage in your example].

Spark supports this and is very clean, you can also mix and match web forms and spark views so you don't have to recreate all your views.

UPDATE: Looks like Phil Haack has a blog post on how to achieve this. His code is for the RC, but I think that should compile fine against ASP.NET MVC RTM.

Andrew Burns
The spark solution looks interesting but from what I understand it puts most of the work in the view layer which I want to avoid. However it is worth investigating in more details and I will look at some samples today.
Diago
I had a look at Phil's blog post and it might be exactly what I need. I will also be looking at his prototype today and see if it does work.
Diago
I selected this as the correct answer as the solution is provided by someone from the Mvc team.
Diago
Thank you for the accept Diago; however what do you mean by putting most of the work in the view?
Andrew Burns
@Andrew - To be honest right now I can't remember why I made that statement. I have made a note to read the documentation on Spark again and will then update this answer accordingly.
Diago
A: 

This question is VERY much a repeat of this question

so I'll quote my answer to that one here.

I came up with a different solution that didn't require me to roll my own view engine.

Basically, I wanted to keep MVC as "Convention" driven as possible, but I still wanted to organize all of my "Admin" views under the ~/Views/Admin folder.

Example:

  • ~/Views/Admin/User/
  • ~/Views/Admin/News/
  • ~/Views/Admin/Blog/

My solution was to create a new base class for my specific admin controllers and "force" the path to the view for that controller.

I have a blog post and sample code here: Organize your views in ASP.Net MVC

datacop
It is interesting that when I did a search on stackoverflow and Google the question you linked to did not come up, granted I did do this at 2:00 am in the morning and lack of sleep could have been the culprit.
Diago
I agree with keeping the convention as much as possible and will look into your solution today to determine if it meets the my specific requirements, however it seems like I need to think outside the box for this one.
Diago
Your solution also works however I feel the solution provided by Phil is more accurate. Unfortunatly I can only choose one answers as the solution provided here is also an option
Diago
A: 

I know you already accepted an answer but here's what I came up with while experimenting with the same idea, with the help of Phil Haack's post.

First you need to have your own ViewEngine to look for folders under View folder. Something like this : (You'll notice that it looks a lot like Phil Haack's areas code)

public class TestViewEngine : WebFormViewEngine
{
    public TestViewEngine()
        : base()
    {
        MasterLocationFormats = new[] {
            "~/Views/{1}/{0}.master",
            "~/Views/Shared/{0}.master"
        };

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

        PartialViewLocationFormats = ViewLocationFormats;
    }
    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        ViewEngineResult rootResult = null;

        //if the route data has a root value defined when mapping routes in global.asax
        if (controllerContext.RouteData.Values.ContainsKey("root")) {
            //then try to find the view in the folder name defined in that route
            string rootViewName = FormatViewName(controllerContext, viewName);
            rootResult = base.FindView(controllerContext, rootViewName, masterName, useCache);
            if (rootResult != null && rootResult.View != null) {
                return rootResult;
            }
            //same if it's a shared view
            string sharedRootViewName = FormatSharedViewName(controllerContext, viewName);
            rootResult = base.FindView(controllerContext, sharedRootViewName, masterName, useCache);
            if (rootResult != null && rootResult.View != null) {
                return rootResult;
            }
        }
        //if not let the base handle it
        return base.FindView(controllerContext, viewName, masterName, useCache);
    }

    private static string FormatViewName(ControllerContext controllerContext, string viewName) {
        string controllerName = controllerContext.RouteData.GetRequiredString("controller");

        string root = controllerContext.RouteData.Values["root"].ToString();
        return "Views/" + root + "/" + controllerName + "/" + viewName;
    }

    private static string FormatSharedViewName(ControllerContext controllerContext, string viewName) {
        string root = controllerContext.RouteData.Values["root"].ToString();
        return "Views/" + root + "/Shared/" + viewName;
    }
}

Then in your Global.asax replace the default ViewEngine with your custom one, on Application_Start :

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new TestViewEngine());

Now when you are defining routes in Global.asax, you need to set a root value indicating the folder to look for under the View folders like so :

routes.MapRoute(
    "ListManager",
    "ListManager/{action}/{id}",
    new { controller = "ListManager", action = "Index", id = "", root = "Manage" }
 );
çağdaş
Thanks. As mentioned I went with re-factoring the way the site works but all the solutions listed are brilliant.
Diago